From e611f5957bea8779e749613351abfbe6ace6104c Mon Sep 17 00:00:00 2001 From: Daniel Lauding Date: Tue, 24 Feb 2026 09:35:08 +0100 Subject: [PATCH 01/66] Add poll CRUD, voting, comments, image upload, remix endpoints --- backend/middleware/upload.js | 25 + backend/models/Comment.js | 39 ++ backend/models/Poll.js | 98 ++++ backend/models/Report.js | 44 ++ backend/models/User.js | 46 ++ backend/package.json | 7 +- backend/server.js | 506 +++++++++++++++++- frontend/.storybook/main.ts | 17 + frontend/.storybook/preview.ts | 22 + frontend/.storybook/vitest.setup.ts | 7 + frontend/components.json | 23 + frontend/dist/assets/index-NXDCLOLN.css | 1 + frontend/dist/assets/index-fkK1QVdD.js | 185 +++++++ frontend/dist/index.html | 14 + frontend/dist/vite.svg | 1 + frontend/package.json | 41 +- frontend/src/App.jsx | 25 +- frontend/src/api/auth.ts | 28 + frontend/src/api/polls.ts | 57 ++ frontend/src/components/AuthModal.stories.tsx | 27 + frontend/src/components/AuthModal.tsx | 99 ++++ frontend/src/components/Header.tsx | 57 ++ frontend/src/components/ui/avatar.tsx | 109 ++++ frontend/src/components/ui/badge.tsx | 48 ++ frontend/src/components/ui/button.stories.tsx | 44 ++ frontend/src/components/ui/button.tsx | 64 +++ frontend/src/components/ui/card.stories.tsx | 45 ++ frontend/src/components/ui/card.tsx | 92 ++++ frontend/src/components/ui/dialog.tsx | 158 ++++++ frontend/src/components/ui/dropdown-menu.tsx | 255 +++++++++ frontend/src/components/ui/input.stories.tsx | 34 ++ frontend/src/components/ui/input.tsx | 21 + frontend/src/components/ui/label.tsx | 22 + frontend/src/components/ui/progress.tsx | 31 ++ frontend/src/components/ui/skeleton.tsx | 13 + frontend/src/components/ui/sonner.tsx | 38 ++ frontend/src/components/ui/tabs.tsx | 89 +++ frontend/src/components/ui/textarea.tsx | 18 + frontend/src/context/AuthContext.tsx | 64 +++ frontend/src/index.css | 124 +++++ frontend/src/lib/utils.ts | 6 + frontend/src/pages/CreatePoll.tsx | 8 + frontend/src/pages/Dashboard.tsx | 139 +++++ frontend/src/pages/Home.tsx | 25 + frontend/src/pages/Results.tsx | 8 + frontend/src/pages/VotePoll.tsx | 8 + frontend/tsconfig.app.json | 7 + frontend/tsconfig.json | 25 + frontend/vite.config.js | 45 +- frontend/vitest.shims.d.ts | 1 + 50 files changed, 2888 insertions(+), 22 deletions(-) create mode 100644 backend/middleware/upload.js create mode 100644 backend/models/Comment.js create mode 100644 backend/models/Poll.js create mode 100644 backend/models/Report.js create mode 100644 backend/models/User.js create mode 100644 frontend/.storybook/main.ts create mode 100644 frontend/.storybook/preview.ts create mode 100644 frontend/.storybook/vitest.setup.ts create mode 100644 frontend/components.json create mode 100644 frontend/dist/assets/index-NXDCLOLN.css create mode 100644 frontend/dist/assets/index-fkK1QVdD.js create mode 100644 frontend/dist/index.html create mode 100644 frontend/dist/vite.svg create mode 100644 frontend/src/api/auth.ts create mode 100644 frontend/src/api/polls.ts create mode 100644 frontend/src/components/AuthModal.stories.tsx create mode 100644 frontend/src/components/AuthModal.tsx create mode 100644 frontend/src/components/Header.tsx create mode 100644 frontend/src/components/ui/avatar.tsx create mode 100644 frontend/src/components/ui/badge.tsx create mode 100644 frontend/src/components/ui/button.stories.tsx create mode 100644 frontend/src/components/ui/button.tsx create mode 100644 frontend/src/components/ui/card.stories.tsx create mode 100644 frontend/src/components/ui/card.tsx create mode 100644 frontend/src/components/ui/dialog.tsx create mode 100644 frontend/src/components/ui/dropdown-menu.tsx create mode 100644 frontend/src/components/ui/input.stories.tsx create mode 100644 frontend/src/components/ui/input.tsx create mode 100644 frontend/src/components/ui/label.tsx create mode 100644 frontend/src/components/ui/progress.tsx create mode 100644 frontend/src/components/ui/skeleton.tsx create mode 100644 frontend/src/components/ui/sonner.tsx create mode 100644 frontend/src/components/ui/tabs.tsx create mode 100644 frontend/src/components/ui/textarea.tsx create mode 100644 frontend/src/context/AuthContext.tsx create mode 100644 frontend/src/lib/utils.ts create mode 100644 frontend/src/pages/CreatePoll.tsx create mode 100644 frontend/src/pages/Dashboard.tsx create mode 100644 frontend/src/pages/Home.tsx create mode 100644 frontend/src/pages/Results.tsx create mode 100644 frontend/src/pages/VotePoll.tsx create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vitest.shims.d.ts diff --git a/backend/middleware/upload.js b/backend/middleware/upload.js new file mode 100644 index 000000000..1acb38743 --- /dev/null +++ b/backend/middleware/upload.js @@ -0,0 +1,25 @@ +import { v2 as cloudinary } from "cloudinary"; +import { CloudinaryStorage } from "multer-storage-cloudinary"; +import multer from "multer"; + +cloudinary.config({ + cloud_name: process.env.CLOUDINARY_CLOUD_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, +}); + +const storage = new CloudinaryStorage({ + cloudinary, + params: { + folder: "designvote", + allowed_formats: ["jpg", "jpeg", "png", "gif", "webp", "gif"], + transformation: [{ width: 1200, crop: "limit" }], + }, +}); + +const upload = multer({ + storage, + limits: { fileSize: 5 * 1024 * 1024 }, +}); + +export default upload; \ No newline at end of file diff --git a/backend/models/Comment.js b/backend/models/Comment.js new file mode 100644 index 000000000..5ffbeb399 --- /dev/null +++ b/backend/models/Comment.js @@ -0,0 +1,39 @@ +import mongoose from "mongoose"; + +const commentSchema = new mongoose.Schema({ + text: { + type: String, + required: true, + minlength: 1, + maxlength: 500 + }, + user: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true + }, + username: { + type: String, + required: true + }, + poll: { + type: mongoose.Schema.Types.ObjectId, + ref: "Poll", + required: true + }, + optionIndex: { + type: Number, + default: null + }, + imageUrl: { + type: String, + default: "" + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +const Comment = mongoose.model("Comment", commentSchema); +export default Comment; diff --git a/backend/models/Poll.js b/backend/models/Poll.js new file mode 100644 index 000000000..2b11a57da --- /dev/null +++ b/backend/models/Poll.js @@ -0,0 +1,98 @@ +import mongoose from "mongoose"; +import crypto from "crypto"; + +const optionSchema = new mongoose.Schema({ + label: { + type: String, + required: true, + maxlength: 100 + }, + imageUrl: { + type: String, + default: "" + }, + externalUrl: { + type: String, + default: "" + }, + embedUrl: { + type: String, + default: "" + }, + embedType: { + type: String, + enum: ["none", "figma", "lovable", "codepen", "generic"], + default: "none" + }, + votes: [{ + type: mongoose.Schema.Types.ObjectId, + ref: "User" + }] +}); + +const pollSchema = new mongoose.Schema({ + title: { + type: String, + required: true, + minlength: 3, + maxlength: 100 + }, + description: { + type: String, + maxlength: 500, + default: "" + }, + creator: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true + }, + creatorName: { + type: String, + default: "Anonymous" + }, + options: { + type: [optionSchema], + validate: { + validator: function(arr) { + return arr.length >= 2 && arr.length <= 4; + }, + message: "A poll must have between 2 and 4 options" + } + }, + status: { + type: String, + enum: ["draft", "published"], + default: "published" + }, + visibility: { + type: String, + enum: ["public", "unlisted", "private"], + default: "public" + }, + shareId: { + type: String, + unique: true, + default: () => crypto.randomUUID().slice(0, 8) + }, + allowRemix: { + type: Boolean, + default: true + }, + remixedFrom: { + type: mongoose.Schema.Types.ObjectId, + ref: "Poll", + default: null + }, + deadline: { + type: Date, + default: null + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +const Poll = mongoose.model("Poll", pollSchema); +export default Poll; diff --git a/backend/models/Report.js b/backend/models/Report.js new file mode 100644 index 000000000..5e129b6f7 --- /dev/null +++ b/backend/models/Report.js @@ -0,0 +1,44 @@ +import mongoose from "mongoose"; + +const reportSchema = new mongoose.Schema({ + reason: { + type: String, + required: true, + enum: ["spam", "inappropriate", "copyright", "other"] + }, + message: { + type: String, + maxlength: 500, + default: "" + }, + targetType: { + type: String, + required: true, + enum: ["poll", "comment"] + }, + targetId: { + type: mongoose.Schema.Types.ObjectId, + required: true + }, + reporter: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true + }, + reporterName: { + type: String, + required: true + }, + status: { + type: String, + enum: ["pending", "reviewed", "dismissed"], + default: "pending" + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +const Report = mongoose.model("Report", reportSchema); +export default Report; diff --git a/backend/models/User.js b/backend/models/User.js new file mode 100644 index 000000000..a459cb757 --- /dev/null +++ b/backend/models/User.js @@ -0,0 +1,46 @@ +import mongoose from "mongoose"; +import bcrypt from "bcrypt"; +import crypto from "crypto"; + +const userSchema = new mongoose.Schema({ + username: { + type: String, + required: true, + unique: true, + minlength: 3, + maxlength: 20 + }, + email: { + type: String, + required: true, + unique: true, + lowercase: true + }, + password: { + type: String, + required: true, + minlength: 8 + }, + role: { + type: String, + enum: ["user", "admin"], + default: "user" + }, + accessToken: { + type: String, + default: () => crypto.randomUUID() + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +userSchema.pre("save", async function() { + if (this.isModified("password")) { + this.password = await bcrypt.hash(this.password, 10); + } +}); + +const User = mongoose.model("User", userSchema); +export default User; \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 08f29f244..dc391c8ed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,9 +12,14 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^6.0.0", + "cloudinary": "^1.41.3", "cors": "^2.8.5", + "dotenv": "^17.3.1", "express": "^4.17.3", "mongoose": "^8.4.0", + "multer": "^2.0.2", + "multer-storage-cloudinary": "^4.0.0", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/backend/server.js b/backend/server.js index 070c87518..4dd1cd578 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,10 +1,14 @@ -import express from "express"; +import "dotenv/config"; import cors from "cors"; +import express from "express"; import mongoose from "mongoose"; - -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; -mongoose.connect(mongoUrl); -mongoose.Promise = Promise; +import bcrypt from "bcrypt"; +import crypto from "crypto"; +import User from "./models/User.js"; +import Poll from "./models/Poll.js"; +import Comment from "./models/Comment.js"; +import Report from "./models/Report.js"; +import upload from "./middleware/upload.js"; const port = process.env.PORT || 8080; const app = express(); @@ -12,11 +16,495 @@ const app = express(); app.use(cors()); app.use(express.json()); +// Auth middleware +const authenticateUser = async (req, res, next) => { + const token = req.header("Authorization"); + + if (!token) { + return res.status(401).json({ + success: false, + error: "Access denied. No token provided" + }); + } + + try { + const user = await User.findOne({ accessToken: token }); + if (!user) { + return res.status(401).json({ + success: false, + error: "Invalid token" + }); + } + + req.user = user; + next(); + } catch (error) { + res.status(500).json({ + success: false, + error: "Authentication error" + }); + } +}; + app.get("/", (req, res) => { - res.send("Hello Technigo!"); + res.json({ + message: "Welcome to DesignVote API", + endpoints: [ + { method: "POST", path: "/users", description: "Register" }, + { method: "POST", path: "/sessions", description: "Login" }, + { method: "GET", path: "/users/me", description: "Get profile (auth)" }, + { method: "GET", path: "/polls", description: "Get all polls" }, + { method: "GET", path: "/polls/:shareId", description: "Get one poll" }, + { method: "POST", path: "/polls", description: "Create poll (auth)" }, + { method: "POST", path: "/polls/:id/vote", description: "Vote (auth)" }, + { method: "PATCH", path: "/polls/:id", description: "Update poll (auth, owner)" }, + { method: "DELETE", path: "/polls/:id", description: "Delete poll (auth, owner)" }, + { method: "POST", path: "/polls/:id/remix", description: "Remix a poll (auth)" }, + { method: "POST", path: "/upload", description: "Upload image (auth)" }, + { method: "GET", path: "/polls/:id/comments", description: "Get comments" }, + { method: "POST", path: "/polls/:id/comments", description: "Add comment (auth)" }, + { method: "DELETE", path: "/comments/:id", description: "Delete comment (auth, owner)" }, + { method: "POST", path: "/reports", description: "Report poll/comment (auth)" }, + { method: "GET", path: "/admin/reports", description: "View reports (admin)" }, + { method: "PATCH", path: "/admin/reports/:id", description: "Update report (admin)" }, + ] + }); +}); + +// Register +app.post("/users", async (req, res) => { + try { + const { username, email, password } = req.body; + + const existingUser = await User.findOne({ email }); + if (existingUser) { + return res.status(400).json({ + success: false, + error: "Email already exists" + }); + } + + const user = new User({ username, email, password }); + const savedUser = await user.save(); + + res.status(201).json({ + success: true, + userId: savedUser._id, + username: savedUser.username, + accessToken: savedUser.accessToken + }); + } catch (error) { + res.status(400).json({ + success: false, + error: "Could not create user", + message: error.message + }); + } }); -// Start the server -app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); +// Login +app.post("/sessions", async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ email }); + if (!user) { + return res.status(401).json({ + success: false, + error: "Invalid email or password" + }); + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res.status(401).json({ + success: false, + error: "Invalid email or password" + }); + } + + user.accessToken = crypto.randomUUID(); + await user.save(); + + res.json({ + success: true, + userId: user._id, + username: user.username, + accessToken: user.accessToken + }); + } catch (error) { + res.status(500).json({ + success: false, + error: "Login failed", + message: error.message + }); + } +}); + +// Get profile +app.get("/users/me", authenticateUser, async (req, res) => { + res.json({ + userId: req.user._id, + username: req.user.username, + email: req.user.email, + createdAt: req.user.createdAt + }); +}); + +// Delete own account +app.delete("/users/me", authenticateUser, async (req, res) => { + try { + await User.findByIdAndDelete(req.user._id); + res.json({ success: true, message: "Account deleted" }); + } catch (error) { + res.status(500).json({ + success: false, + error: "Could not delete account", + message: error.message + }); + } +}); + +// Get all polls +app.get("/polls", async (req, res) => { + try { + const { page = 1, limit = 20 } = req.query; + + let filter = { status: "published" }; + + const token = req.header("Authorization"); + if (token) { + const user = await User.findOne({ accessToken: token }); + if (user) { + filter = { + $or: [ + { status: "published" }, + { creator: user._id } + ] + }; + } + } + + const skip = (Number(page) - 1) * Number(limit); + + const polls = await Poll.find(filter) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(Number(limit)); + + const total = await Poll.countDocuments(filter); + + res.json({ + total, + page: Number(page), + totalPages: Math.ceil(total / Number(limit)), + results: polls + }); + } catch (error) { + res.status(500).json({ success: false, error: "Could not fetch polls", message: error.message }); + } +}); + +// Get specific poll +app.get("/polls/:shareId", async (req, res) => { + try { + const poll = await Poll.findOne({ shareId: req.params.shareId }); + + if (!poll) { + return res.status(404).json({ success: false, error: "Poll not found" }); + } + + const totalVotes = poll.options.reduce((sum, opt) => sum + opt.votes.length, 0); + + const results = poll.options.map((opt) => ({ + label: opt.label, + imageUrl: opt.imageUrl, + externalUrl: opt.externalUrl, + voteCount: opt.votes.length, + percentage: totalVotes > 0 ? Math.round((opt.votes.length / totalVotes) * 100) : 0 + })); + + res.json({ ...poll.toObject(), totalVotes, results }); + } catch (error) { + res.status(500).json({ success: false, error: "Could not fetch poll", message: error.message }); + } +}); + +// Create poll +app.post("/polls", authenticateUser, async (req, res) => { + try { + const { title, description, options, status } = req.body; + + const poll = new Poll({ + title, + description, + options, + status: status || "published", + creator: req.user._id, + creatorName: req.user.username + }); + + const savedPoll = await poll.save(); + + res.status(201).json({ + success: true, + poll: savedPoll, + shareUrl: `/poll/${savedPoll.shareId}` + }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not create poll", message: error.message }); + } +}); + +// Vote poll +app.post("/polls/:id/vote", authenticateUser, async (req, res) => { + try { + const { optionIndex } = req.body; + const poll = await Poll.findById(req.params.id); + + if (!poll) { + return res.status(404).json({ success: false, error: "Poll not found" }); + } + + if (optionIndex < 0 || optionIndex >= poll.options.length) { + return res.status(400).json({ success: false, error: "Invalid option index" }); + } + + const alreadyVoted = poll.options.some( + opt => opt.votes.some(v => v.toString() === req.user._id.toString()) + ); + + if (alreadyVoted) { + return res.status(400).json({ success: false, error: "You have already voted on this poll" }); + } + + poll.options[optionIndex].votes.push(req.user._id); + await poll.save(); + + res.json({ success: true, message: "Vote recorded!" }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not vote", message: error.message }); + } }); + +// Update poll (owner of poll) +app.patch("/polls/:id", authenticateUser, async (req, res) => { + try { + const poll = await Poll.findById(req.params.id); + + if (!poll) { + return res.status(404).json({ success: false, error: "Poll not found" }); + } + + if (poll.creator.toString() !== req.user._id.toString()) { + return res.status(403).json({ success: false, error: "Not authorized" }); + } + + const { title, description, status } = req.body; + if (title) poll.title = title; + if (description !== undefined) poll.description = description; + if (status) poll.status = status; + + const updated = await poll.save(); + res.json(updated); + } catch (error) { + res.status(400).json({ success: false, error: "Could not update poll", message: error.message }); + } +}); + +// Remove poll (owner of poll) +app.delete("/polls/:id", authenticateUser, async (req, res) => { + try { + const poll = await Poll.findById(req.params.id); + + if (!poll) { + return res.status(404).json({ success: false, error: "Poll not found" }); + } + + if (poll.creator.toString() !== req.user._id.toString()) { + return res.status(403).json({ success: false, error: "Not authorized" }); + } + + await Comment.deleteMany({ poll: poll._id }); + await Poll.findByIdAndDelete(req.params.id); + + res.json({ success: true, message: "Poll deleted" }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not delete poll", message: error.message }); + } +}); + +// Remix +app.post("/polls/:id/remix", authenticateUser, async (req, res) => { + try { + const original = await Poll.findById(req.params.id); + + if (!original) { + return res.status(404).json({ success: false, error: "Poll not found" }); + } + + if (!original.allowRemix) { + return res.status(403).json({ success: false, error: "This poll does not allow remixes" }); + } + + const { title, description, options } = req.body; + + const remix = new Poll({ + title: title || `Remix: ${original.title}`, + description: description || original.description, + options: options || original.options.map(opt => ({ + label: opt.label, + imageUrl: opt.imageUrl, + externalUrl: opt.externalUrl, + votes: [] + })), + creator: req.user._id, + creatorName: req.user.username, + remixedFrom: original._id + }); + + const saved = await remix.save(); + res.status(201).json({ success: true, poll: saved }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not remix poll", message: error.message }); + } +}); + +// Upload +app.post("/upload", authenticateUser, upload.single("image"), (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ success: false, error: "No image provided" }); + } + + res.json({ + success: true, + imageUrl: req.file.path, + publicId: req.file.filename + }); + } catch (error) { + res.status(500).json({ success: false, error: "Upload failed", message: error.message }); + } +}); + +// Comments +app.get("/polls/:id/comments", async (req, res) => { + try { + const comments = await Comment.find({ poll: req.params.id }).sort({ createdAt: -1 }); + res.json(comments); + } catch (error) { + res.status(500).json({ success: false, error: "Could not fetch comments", message: error.message }); + } +}); + +app.post("/polls/:id/comments", authenticateUser, async (req, res) => { + try { + const { text, optionIndex, imageUrl } = req.body; + + const comment = new Comment({ + text, + user: req.user._id, + username: req.user.username, + poll: req.params.id, + optionIndex: optionIndex ?? null, + imageUrl: imageUrl || "" + }); + + const saved = await comment.save(); + res.status(201).json(saved); + } catch (error) { + res.status(400).json({ success: false, error: "Could not create comment", message: error.message }); + } +}); + +app.delete("/comments/:id", authenticateUser, async (req, res) => { + try { + const comment = await Comment.findById(req.params.id); + + if (!comment) { + return res.status(404).json({ success: false, error: "Comment not found" }); + } + + if (comment.user.toString() !== req.user._id.toString()) { + return res.status(403).json({ success: false, error: "Not authorized" }); + } + + await Comment.findByIdAndDelete(req.params.id); + res.json({ success: true, message: "Comment deleted" }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not delete comment", message: error.message }); + } +}); + +// Report a poll or comment +app.post("/reports", authenticateUser, async (req, res) => { + try { + const { reason, message, targetType, targetId } = req.body; + + const report = new Report({ + reason, + message: message || "", + targetType, + targetId, + reporter: req.user._id, + reporterName: req.user.username + }); + + const saved = await report.save(); + res.status(201).json({ success: true, message: "Report submitted", report: saved }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not submit report", message: error.message }); + } +}); + +// Admin: Get all pending reports +app.get("/admin/reports", authenticateUser, async (req, res) => { + try { + if (req.user.role !== "admin") { + return res.status(403).json({ success: false, error: "Admin access required" }); + } + + const { status = "pending" } = req.query; + const reports = await Report.find({ status }).sort({ createdAt: -1 }); + res.json({ total: reports.length, reports }); + } catch (error) { + res.status(500).json({ success: false, error: "Could not fetch reports", message: error.message }); + } +}); + +// Admin: Update report status +app.patch("/admin/reports/:id", authenticateUser, async (req, res) => { + try { + if (req.user.role !== "admin") { + return res.status(403).json({ success: false, error: "Admin access required" }); + } + + const { status } = req.body; + const report = await Report.findByIdAndUpdate( + req.params.id, + { status }, + { new: true } + ); + + if (!report) { + return res.status(404).json({ success: false, error: "Report not found" }); + } + + res.json({ success: true, report }); + } catch (error) { + res.status(400).json({ success: false, error: "Could not update report", message: error.message }); + } +}); + +// Connect to MongoDB, then start server +mongoose + .connect(process.env.MONGO_URL) + .then(() => { + console.log("Connected to MongoDB"); + app.listen(port, () => { + console.log(`Server running on http://localhost:${port}`); + }); + }) + .catch((error) => { + console.error("Could not connect to MongoDB:", error.message); + }); diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts new file mode 100644 index 000000000..78cf7b392 --- /dev/null +++ b/frontend/.storybook/main.ts @@ -0,0 +1,17 @@ +import type { StorybookConfig } from '@storybook/react-vite'; + +const config: StorybookConfig = { + "stories": [ + "../src/**/*.mdx", + "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)" + ], + "addons": [ + "@chromatic-com/storybook", + "@storybook/addon-vitest", + "@storybook/addon-a11y", + "@storybook/addon-docs", + "@storybook/addon-onboarding" + ], + "framework": "@storybook/react-vite" +}; +export default config; \ No newline at end of file diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts new file mode 100644 index 000000000..340961be8 --- /dev/null +++ b/frontend/.storybook/preview.ts @@ -0,0 +1,22 @@ +import type { Preview } from '@storybook/react-vite' +import '../src/index.css' + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + + a11y: { + // 'todo' - show a11y violations in the test UI only + // 'error' - fail CI on a11y violations + // 'off' - skip a11y checks entirely + test: 'todo' + } + }, +}; + +export default preview; \ No newline at end of file diff --git a/frontend/.storybook/vitest.setup.ts b/frontend/.storybook/vitest.setup.ts new file mode 100644 index 000000000..44922d55e --- /dev/null +++ b/frontend/.storybook/vitest.setup.ts @@ -0,0 +1,7 @@ +import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; +import { setProjectAnnotations } from '@storybook/react-vite'; +import * as projectAnnotations from './preview'; + +// This is an important step to apply the right configuration when testing your stories. +// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations +setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]); \ No newline at end of file diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 000000000..c3085d6c2 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/frontend/dist/assets/index-NXDCLOLN.css b/frontend/dist/assets/index-NXDCLOLN.css new file mode 100644 index 000000000..f0bf59743 --- /dev/null +++ b/frontend/dist/assets/index-NXDCLOLN.css @@ -0,0 +1 @@ +/*! tailwindcss v4.2.0 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-duration:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-widest:.1em;--radius-xs:.125rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring) 50%,transparent)}}body{background-color:var(--background);color:var(--foreground)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.pointer-events-none{pointer-events:none}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-4{top:calc(var(--spacing) * 4)}.top-\[50\%\]{top:50%}.right-0{right:calc(var(--spacing) * 0)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-2{left:calc(var(--spacing) * 2)}.left-\[50\%\]{left:50%}.z-10{z-index:10}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing) * 1)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.inline-flex{display:inline-flex}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.size-full{width:100%;height:100%}.h-2{height:calc(var(--spacing) * 2)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-48{height:calc(var(--spacing) * 48)}.h-\[calc\(100\%-1px\)\]{height:calc(100% - 1px)}.h-full{height:100%}.h-px{height:1px}.max-h-\(--radix-dropdown-menu-content-available-height\){max-height:var(--radix-dropdown-menu-content-available-height)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-12{width:calc(var(--spacing) * 12)}.w-16{width:calc(var(--spacing) * 16)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[8rem\]{min-width:8rem}.flex-1{flex:1}.shrink-0{flex-shrink:0}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.overflow-hidden{overflow:hidden}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--border)}.border-input{border-color:var(--input)}.border-transparent{border-color:#0000}.bg-accent{background-color:var(--accent)}.bg-background{background-color:var(--background)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-muted{background-color:var(--muted)}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/20{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/20{background-color:color-mix(in oklab,var(--primary) 20%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-transparent{background-color:#0000}.fill-current{fill:currentColor}.p-1{padding:calc(var(--spacing) * 1)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-\[3px\]{padding:3px}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-16{padding-block:calc(var(--spacing) * 16)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.whitespace-nowrap{white-space:nowrap}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-foreground,.text-foreground\/60{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\/60{color:color-mix(in oklab,var(--foreground) 60%,transparent)}}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{color:var(--color-white)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-background{--tw-ring-color:var(--background)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-has-data-\[size\=lg\]\/avatar-group\:size-10:is(:where(.group\/avatar-group):has([data-size=lg]) *){width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.group-has-data-\[size\=sm\]\/avatar-group\:size-6:is(:where(.group\/avatar-group):has([data-size=sm]) *){width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.group-data-\[orientation\=horizontal\]\/tabs\:h-9:is(:where(.group\/tabs)[data-orientation=horizontal] *){height:calc(var(--spacing) * 9)}.group-data-\[orientation\=vertical\]\/tabs\:h-fit:is(:where(.group\/tabs)[data-orientation=vertical] *){height:fit-content}.group-data-\[orientation\=vertical\]\/tabs\:w-full:is(:where(.group\/tabs)[data-orientation=vertical] *){width:100%}.group-data-\[orientation\=vertical\]\/tabs\:flex-col:is(:where(.group\/tabs)[data-orientation=vertical] *){flex-direction:column}.group-data-\[orientation\=vertical\]\/tabs\:justify-start:is(:where(.group\/tabs)[data-orientation=vertical] *){justify-content:flex-start}.group-data-\[size\=default\]\/avatar\:size-2\.5:is(:where(.group\/avatar)[data-size=default] *){width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.group-data-\[size\=lg\]\/avatar\:size-3:is(:where(.group\/avatar)[data-size=lg] *){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=sm\]\/avatar\:size-2:is(:where(.group\/avatar)[data-size=sm] *){width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:text-xs:is(:where(.group\/avatar)[data-size=sm] *){font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.group-data-\[variant\=line\]\/tabs-list\:bg-transparent:is(:where(.group\/tabs-list)[data-variant=line] *){background-color:#0000}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-7::file-selector-button{height:calc(var(--spacing) * 7)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bg-foreground:after{content:var(--tw-content);background-color:var(--foreground)}.after\:opacity-0:after{content:var(--tw-content);opacity:0}.after\:transition-opacity:after{content:var(--tw-content);transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.group-data-\[orientation\=horizontal\]\/tabs\:after\:inset-x-0:is(:where(.group\/tabs)[data-orientation=horizontal] *):after{content:var(--tw-content);inset-inline:calc(var(--spacing) * 0)}.group-data-\[orientation\=horizontal\]\/tabs\:after\:bottom-\[-5px\]:is(:where(.group\/tabs)[data-orientation=horizontal] *):after{content:var(--tw-content);bottom:-5px}.group-data-\[orientation\=horizontal\]\/tabs\:after\:h-0\.5:is(:where(.group\/tabs)[data-orientation=horizontal] *):after{content:var(--tw-content);height:calc(var(--spacing) * .5)}.group-data-\[orientation\=vertical\]\/tabs\:after\:inset-y-0:is(:where(.group\/tabs)[data-orientation=vertical] *):after{content:var(--tw-content);inset-block:calc(var(--spacing) * 0)}.group-data-\[orientation\=vertical\]\/tabs\:after\:-right-1:is(:where(.group\/tabs)[data-orientation=vertical] *):after{content:var(--tw-content);right:calc(var(--spacing) * -1)}.group-data-\[orientation\=vertical\]\/tabs\:after\:w-0\.5:is(:where(.group\/tabs)[data-orientation=vertical] *):after{content:var(--tw-content);width:calc(var(--spacing) * .5)}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive) 90%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary) 90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--secondary) 80%,transparent)}}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-ring:focus-visible{outline-color:var(--ring)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-\[\>svg\]\:px-1\.5:has(>svg){padding-inline:calc(var(--spacing) * 1.5)}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing) * 2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing) * 3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing) * 4)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing) * 8)}.data-\[orientation\=horizontal\]\:flex-col[data-orientation=horizontal]{flex-direction:column}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=lg\]\:size-10[data-size=lg]{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.data-\[size\=sm\]\:size-6[data-size=sm]{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}:is(.\*\:data-\[slot\=avatar\]\:ring-2>*)[data-slot=avatar]{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:is(.\*\:data-\[slot\=avatar\]\:ring-background>*)[data-slot=avatar]{--tw-ring-color:var(--background)}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.data-\[state\=active\]\:text-foreground[data-state=active]{color:var(--foreground)}.group-data-\[variant\=default\]\/tabs-list\:data-\[state\=active\]\:shadow-sm:is(:where(.group\/tabs-list)[data-variant=default] *)[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.group-data-\[variant\=line\]\/tabs-list\:data-\[state\=active\]\:bg-transparent:is(:where(.group\/tabs-list)[data-variant=line] *)[data-state=active]{background-color:#0000}.group-data-\[variant\=line\]\/tabs-list\:data-\[state\=active\]\:shadow-none:is(:where(.group\/tabs-list)[data-variant=line] *)[data-state=active]{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.group-data-\[variant\=line\]\/tabs-list\:data-\[state\=active\]\:after\:opacity-100:is(:where(.group\/tabs-list)[data-variant=line] *)[data-state=active]:after{content:var(--tw-content);opacity:1}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive) 10%,transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.data-\[variant\=line\]\:rounded-none[data-variant=line]{border-radius:0}@media(min-width:40rem){.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:text-left{text-align:left}}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media(min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive) 60%,transparent)}}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input) 30%,transparent)}}.dark\:text-muted-foreground:is(.dark *){color:var(--muted-foreground)}@media(hover:hover){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--accent) 50%,transparent)}}.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input) 50%,transparent)}}.dark\:hover\:text-foreground:is(.dark *):hover{color:var(--foreground)}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:data-\[state\=active\]\:border-input:is(.dark *)[data-state=active]{border-color:var(--input)}.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:color-mix(in oklab,var(--input) 30%,transparent)}}.dark\:data-\[state\=active\]\:text-foreground:is(.dark *)[data-state=active]{color:var(--foreground)}.dark\:group-data-\[variant\=line\]\/tabs-list\:data-\[state\=active\]\:border-transparent:is(.dark *):is(:where(.group\/tabs-list)[data-variant=line] *)[data-state=active]{border-color:#0000}.dark\:group-data-\[variant\=line\]\/tabs-list\:data-\[state\=active\]\:bg-transparent:is(.dark *):is(:where(.group\/tabs-list)[data-variant=line] *)[data-state=active]{background-color:#0000}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive) 20%,transparent)}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing) * 6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing) * 6)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.group-has-data-\[size\=lg\]\/avatar-group\:\[\&\>svg\]\:size-5:is(:where(.group\/avatar-group):has([data-size=lg]) *)>svg{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.group-has-data-\[size\=sm\]\/avatar-group\:\[\&\>svg\]\:size-3:is(:where(.group\/avatar-group):has([data-size=sm]) *)>svg{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.group-data-\[size\=default\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=default] *)>svg,.group-data-\[size\=lg\]\/avatar\:\[\&\>svg\]\:size-2:is(:where(.group\/avatar)[data-size=lg] *)>svg{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.group-data-\[size\=sm\]\/avatar\:\[\&\>svg\]\:hidden:is(:where(.group\/avatar)[data-size=sm] *)>svg{display:none}@media(hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive) 90%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary) 90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary) 90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}a.\[a\&\]\:hover\:underline:hover{text-decoration-line:underline}}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}} diff --git a/frontend/dist/assets/index-fkK1QVdD.js b/frontend/dist/assets/index-fkK1QVdD.js new file mode 100644 index 000000000..73054beb4 --- /dev/null +++ b/frontend/dist/assets/index-fkK1QVdD.js @@ -0,0 +1,185 @@ +function yh(r,l){for(var i=0;iu[c]})}}}return Object.freeze(Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}))}(function(){const l=document.createElement("link").relList;if(l&&l.supports&&l.supports("modulepreload"))return;for(const c of document.querySelectorAll('link[rel="modulepreload"]'))u(c);new MutationObserver(c=>{for(const d of c)if(d.type==="childList")for(const p of d.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&u(p)}).observe(document,{childList:!0,subtree:!0});function i(c){const d={};return c.integrity&&(d.integrity=c.integrity),c.referrerPolicy&&(d.referrerPolicy=c.referrerPolicy),c.crossOrigin==="use-credentials"?d.credentials="include":c.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function u(c){if(c.ep)return;c.ep=!0;const d=i(c);fetch(c.href,d)}})();function as(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var ba={exports:{}},ro={},Ia={exports:{}},de={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var md;function wh(){if(md)return de;md=1;var r=Symbol.for("react.element"),l=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),u=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),p=Symbol.for("react.context"),m=Symbol.for("react.forward_ref"),v=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),k=Symbol.for("react.lazy"),S=Symbol.iterator;function _(E){return E===null||typeof E!="object"?null:(E=S&&E[S]||E["@@iterator"],typeof E=="function"?E:null)}var j={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},$=Object.assign,N={};function T(E,z,se){this.props=E,this.context=z,this.refs=N,this.updater=se||j}T.prototype.isReactComponent={},T.prototype.setState=function(E,z){if(typeof E!="object"&&typeof E!="function"&&E!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,E,z,"setState")},T.prototype.forceUpdate=function(E){this.updater.enqueueForceUpdate(this,E,"forceUpdate")};function b(){}b.prototype=T.prototype;function U(E,z,se){this.props=E,this.context=z,this.refs=N,this.updater=se||j}var A=U.prototype=new b;A.constructor=U,$(A,T.prototype),A.isPureReactComponent=!0;var Y=Array.isArray,Z=Object.prototype.hasOwnProperty,ie={current:null},q={key:!0,ref:!0,__self:!0,__source:!0};function W(E,z,se){var ue,re={},oe=null,ve=null;if(z!=null)for(ue in z.ref!==void 0&&(ve=z.ref),z.key!==void 0&&(oe=""+z.key),z)Z.call(z,ue)&&!q.hasOwnProperty(ue)&&(re[ue]=z[ue]);var me=arguments.length-2;if(me===1)re.children=se;else if(1>>1,z=B[E];if(0>>1;Ec(re,O))oec(ve,re)?(B[E]=ve,B[oe]=O,E=oe):(B[E]=re,B[ue]=O,E=ue);else if(oec(ve,O))B[E]=ve,B[oe]=O,E=oe;else break e}}return ee}function c(B,ee){var O=B.sortIndex-ee.sortIndex;return O!==0?O:B.id-ee.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;r.unstable_now=function(){return d.now()}}else{var p=Date,m=p.now();r.unstable_now=function(){return p.now()-m}}var v=[],y=[],k=1,S=null,_=3,j=!1,$=!1,N=!1,T=typeof setTimeout=="function"?setTimeout:null,b=typeof clearTimeout=="function"?clearTimeout:null,U=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function A(B){for(var ee=i(y);ee!==null;){if(ee.callback===null)u(y);else if(ee.startTime<=B)u(y),ee.sortIndex=ee.expirationTime,l(v,ee);else break;ee=i(y)}}function Y(B){if(N=!1,A(B),!$)if(i(v)!==null)$=!0,we(Z);else{var ee=i(y);ee!==null&&ge(Y,ee.startTime-B)}}function Z(B,ee){$=!1,N&&(N=!1,b(W),W=-1),j=!0;var O=_;try{for(A(ee),S=i(v);S!==null&&(!(S.expirationTime>ee)||B&&!Ge());){var E=S.callback;if(typeof E=="function"){S.callback=null,_=S.priorityLevel;var z=E(S.expirationTime<=ee);ee=r.unstable_now(),typeof z=="function"?S.callback=z:S===i(v)&&u(v),A(ee)}else u(v);S=i(v)}if(S!==null)var se=!0;else{var ue=i(y);ue!==null&&ge(Y,ue.startTime-ee),se=!1}return se}finally{S=null,_=O,j=!1}}var ie=!1,q=null,W=-1,fe=5,Ee=-1;function Ge(){return!(r.unstable_now()-EeB||125E?(B.sortIndex=O,l(y,B),i(v)===null&&B===i(y)&&(N?(b(W),W=-1):N=!0,ge(Y,O-E))):(B.sortIndex=z,l(v,B),$||j||($=!0,we(Z))),B},r.unstable_shouldYield=Ge,r.unstable_wrapCallback=function(B){var ee=_;return function(){var O=_;_=ee;try{return B.apply(this,arguments)}finally{_=O}}}})($a)),$a}var wd;function Eh(){return wd||(wd=1,Fa.exports=Sh()),Fa.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var xd;function Ch(){if(xd)return ot;xd=1;var r=ss(),l=Eh();function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),v=Object.prototype.hasOwnProperty,y=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,k={},S={};function _(e){return v.call(S,e)?!0:v.call(k,e)?!1:y.test(e)?S[e]=!0:(k[e]=!0,!1)}function j(e,t,n,o){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return o?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function $(e,t,n,o){if(t===null||typeof t>"u"||j(e,t,n,o))return!0;if(o)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function N(e,t,n,o,a,s,f){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=o,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=f}var T={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){T[e]=new N(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];T[t]=new N(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){T[e]=new N(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){T[e]=new N(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){T[e]=new N(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){T[e]=new N(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){T[e]=new N(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){T[e]=new N(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){T[e]=new N(e,5,!1,e.toLowerCase(),null,!1,!1)});var b=/[\-:]([a-z])/g;function U(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(b,U);T[t]=new N(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(b,U);T[t]=new N(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(b,U);T[t]=new N(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){T[e]=new N(e,1,!1,e.toLowerCase(),null,!1,!1)}),T.xlinkHref=new N("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){T[e]=new N(e,1,!1,e.toLowerCase(),null,!0,!0)});function A(e,t,n,o){var a=T.hasOwnProperty(t)?T[t]:null;(a!==null?a.type!==0:o||!(2g||a[f]!==s[g]){var w=` +`+a[f].replace(" at new "," at ");return e.displayName&&w.includes("")&&(w=w.replace("",e.displayName)),w}while(1<=f&&0<=g);break}}}finally{se=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?z(e):""}function re(e){switch(e.tag){case 5:return z(e.type);case 16:return z("Lazy");case 13:return z("Suspense");case 19:return z("SuspenseList");case 0:case 2:case 15:return e=ue(e.type,!1),e;case 11:return e=ue(e.type.render,!1),e;case 1:return e=ue(e.type,!0),e;default:return""}}function oe(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case q:return"Fragment";case ie:return"Portal";case fe:return"Profiler";case W:return"StrictMode";case We:return"Suspense";case Ye:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ge:return(e.displayName||"Context")+".Consumer";case Ee:return(e._context.displayName||"Context")+".Provider";case Me:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Fe:return t=e.displayName||null,t!==null?t:oe(e.type)||"Memo";case we:t=e._payload,e=e._init;try{return oe(e(t))}catch{}}return null}function ve(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return oe(t);case 8:return t===W?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function me(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ae(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Be(e){var t=ae(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),o=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var a=n.get,s=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(f){o=""+f,s.call(this,f)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return o},setValue:function(f){o=""+f},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Mt(e){e._valueTracker||(e._valueTracker=Be(e))}function kn(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),o="";return e&&(o=ae(e)?e.checked?"true":"false":e.value),e=o,e!==n?(t.setValue(e),!0):!1}function Gt(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Yt(e,t){var n=t.checked;return O({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function ks(e,t){var n=t.defaultValue==null?"":t.defaultValue,o=t.checked!=null?t.checked:t.defaultChecked;n=me(t.value!=null?t.value:n),e._wrapperState={initialChecked:o,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Ss(e,t){t=t.checked,t!=null&&A(e,"checked",t,!1)}function Vl(e,t){Ss(e,t);var n=me(t.value),o=t.type;if(n!=null)o==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(o==="submit"||o==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Hl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Hl(e,t.type,me(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Es(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var o=t.type;if(!(o!=="submit"&&o!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Hl(e,t,n){(t!=="number"||Gt(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var yr=Array.isArray;function $n(e,t,n,o){if(e=e.options,t){t={};for(var a=0;a"+t.valueOf().toString()+"",t=mo.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function wr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var xr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Sp=["Webkit","ms","Moz","O"];Object.keys(xr).forEach(function(e){Sp.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),xr[t]=xr[e]})});function Ls(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||xr.hasOwnProperty(e)&&xr[e]?(""+t).trim():t+"px"}function Ts(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var o=n.indexOf("--")===0,a=Ls(n,t[n],o);n==="float"&&(n="cssFloat"),o?e.setProperty(n,a):e[n]=a}}var Ep=O({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Gl(e,t){if(t){if(Ep[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(i(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(i(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(i(61))}if(t.style!=null&&typeof t.style!="object")throw Error(i(62))}}function Yl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Xl=null;function Jl(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Zl=null,Un=null,Wn=null;function Os(e){if(e=Wr(e)){if(typeof Zl!="function")throw Error(i(280));var t=e.stateNode;t&&(t=Io(t),Zl(e.stateNode,e.type,t))}}function js(e){Un?Wn?Wn.push(e):Wn=[e]:Un=e}function zs(){if(Un){var e=Un,t=Wn;if(Wn=Un=null,Os(e),t)for(e=0;e>>=0,e===0?32:31-(Dp(e)/Mp|0)|0}var wo=64,xo=4194304;function Cr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ko(e,t){var n=e.pendingLanes;if(n===0)return 0;var o=0,a=e.suspendedLanes,s=e.pingedLanes,f=n&268435455;if(f!==0){var g=f&~a;g!==0?o=Cr(g):(s&=f,s!==0&&(o=Cr(s)))}else f=n&~a,f!==0?o=Cr(f):s!==0&&(o=Cr(s));if(o===0)return 0;if(t!==0&&t!==o&&(t&a)===0&&(a=o&-o,s=t&-t,a>=s||a===16&&(s&4194240)!==0))return t;if((o&4)!==0&&(o|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=o;0n;n++)t.push(e);return t}function Rr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-gt(t),e[t]=n}function Fp(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var o=e.eventTimes;for(e=e.expirationTimes;0=zr),au=" ",su=!1;function uu(e,t){switch(e){case"keyup":return pm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function cu(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Hn=!1;function hm(e,t){switch(e){case"compositionend":return cu(t);case"keypress":return t.which!==32?null:(su=!0,au);case"textInput":return e=t.data,e===au&&su?null:e;default:return null}}function vm(e,t){if(Hn)return e==="compositionend"||!vi&&uu(e,t)?(e=tu(),No=ci=en=null,Hn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=o}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=gu(n)}}function wu(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?wu(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function xu(){for(var e=window,t=Gt();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Gt(e.document)}return t}function wi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Rm(e){var t=xu(),n=e.focusedElem,o=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&wu(n.ownerDocument.documentElement,n)){if(o!==null&&wi(n)){if(t=o.start,e=o.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var a=n.textContent.length,s=Math.min(o.start,a);o=o.end===void 0?s:Math.min(o.end,a),!e.extend&&s>o&&(a=o,o=s,s=a),a=yu(n,s);var f=yu(n,o);a&&f&&(e.rangeCount!==1||e.anchorNode!==a.node||e.anchorOffset!==a.offset||e.focusNode!==f.node||e.focusOffset!==f.offset)&&(t=t.createRange(),t.setStart(a.node,a.offset),e.removeAllRanges(),s>o?(e.addRange(t),e.extend(f.node,f.offset)):(t.setEnd(f.node,f.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Qn=null,xi=null,Ir=null,ki=!1;function ku(e,t,n){var o=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ki||Qn==null||Qn!==Gt(o)||(o=Qn,"selectionStart"in o&&wi(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),Ir&&br(Ir,o)||(Ir=o,o=Do(xi,"onSelect"),0Jn||(e.current=zi[Jn],zi[Jn]=null,Jn--)}function xe(e,t){Jn++,zi[Jn]=e.current,e.current=t}var on={},Ve=rn(on),qe=rn(!1),Cn=on;function Zn(e,t){var n=e.type.contextTypes;if(!n)return on;var o=e.stateNode;if(o&&o.__reactInternalMemoizedUnmaskedChildContext===t)return o.__reactInternalMemoizedMaskedChildContext;var a={},s;for(s in n)a[s]=t[s];return o&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function et(e){return e=e.childContextTypes,e!=null}function Ao(){Se(qe),Se(Ve)}function bu(e,t,n){if(Ve.current!==on)throw Error(i(168));xe(Ve,t),xe(qe,n)}function Iu(e,t,n){var o=e.stateNode;if(t=t.childContextTypes,typeof o.getChildContext!="function")return n;o=o.getChildContext();for(var a in o)if(!(a in t))throw Error(i(108,ve(e)||"Unknown",a));return O({},n,o)}function Fo(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||on,Cn=Ve.current,xe(Ve,e),xe(qe,qe.current),!0}function Au(e,t,n){var o=e.stateNode;if(!o)throw Error(i(169));n?(e=Iu(e,t,Cn),o.__reactInternalMemoizedMergedChildContext=e,Se(qe),Se(Ve),xe(Ve,e)):Se(qe),xe(qe,n)}var It=null,$o=!1,Di=!1;function Fu(e){It===null?It=[e]:It.push(e)}function Im(e){$o=!0,Fu(e)}function ln(){if(!Di&&It!==null){Di=!0;var e=0,t=ye;try{var n=It;for(ye=1;e>=f,a-=f,At=1<<32-gt(t)+a|n<le?(Ae=ne,ne=null):Ae=ne.sibling;var he=D(R,ne,P[le],F);if(he===null){ne===null&&(ne=Ae);break}e&&ne&&he.alternate===null&&t(R,ne),x=s(he,x,le),te===null?J=he:te.sibling=he,te=he,ne=Ae}if(le===P.length)return n(R,ne),Ce&&Nn(R,le),J;if(ne===null){for(;lele?(Ae=ne,ne=null):Ae=ne.sibling;var hn=D(R,ne,he.value,F);if(hn===null){ne===null&&(ne=Ae);break}e&&ne&&hn.alternate===null&&t(R,ne),x=s(hn,x,le),te===null?J=hn:te.sibling=hn,te=hn,ne=Ae}if(he.done)return n(R,ne),Ce&&Nn(R,le),J;if(ne===null){for(;!he.done;le++,he=P.next())he=I(R,he.value,F),he!==null&&(x=s(he,x,le),te===null?J=he:te.sibling=he,te=he);return Ce&&Nn(R,le),J}for(ne=o(R,ne);!he.done;le++,he=P.next())he=V(ne,R,le,he.value,F),he!==null&&(e&&he.alternate!==null&&ne.delete(he.key===null?le:he.key),x=s(he,x,le),te===null?J=he:te.sibling=he,te=he);return e&&ne.forEach(function(gh){return t(R,gh)}),Ce&&Nn(R,le),J}function Te(R,x,P,F){if(typeof P=="object"&&P!==null&&P.type===q&&P.key===null&&(P=P.props.children),typeof P=="object"&&P!==null){switch(P.$$typeof){case Z:e:{for(var J=P.key,te=x;te!==null;){if(te.key===J){if(J=P.type,J===q){if(te.tag===7){n(R,te.sibling),x=a(te,P.props.children),x.return=R,R=x;break e}}else if(te.elementType===J||typeof J=="object"&&J!==null&&J.$$typeof===we&&Hu(J)===te.type){n(R,te.sibling),x=a(te,P.props),x.ref=Br(R,te,P),x.return=R,R=x;break e}n(R,te);break}else t(R,te);te=te.sibling}P.type===q?(x=Dn(P.props.children,R.mode,F,P.key),x.return=R,R=x):(F=ml(P.type,P.key,P.props,null,R.mode,F),F.ref=Br(R,x,P),F.return=R,R=F)}return f(R);case ie:e:{for(te=P.key;x!==null;){if(x.key===te)if(x.tag===4&&x.stateNode.containerInfo===P.containerInfo&&x.stateNode.implementation===P.implementation){n(R,x.sibling),x=a(x,P.children||[]),x.return=R,R=x;break e}else{n(R,x);break}else t(R,x);x=x.sibling}x=Oa(P,R.mode,F),x.return=R,R=x}return f(R);case we:return te=P._init,Te(R,x,te(P._payload),F)}if(yr(P))return Q(R,x,P,F);if(ee(P))return X(R,x,P,F);Vo(R,P)}return typeof P=="string"&&P!==""||typeof P=="number"?(P=""+P,x!==null&&x.tag===6?(n(R,x.sibling),x=a(x,P),x.return=R,R=x):(n(R,x),x=Ta(P,R.mode,F),x.return=R,R=x),f(R)):n(R,x)}return Te}var nr=Qu(!0),Ku=Qu(!1),Ho=rn(null),Qo=null,rr=null,$i=null;function Ui(){$i=rr=Qo=null}function Wi(e){var t=Ho.current;Se(Ho),e._currentValue=t}function Bi(e,t,n){for(;e!==null;){var o=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,o!==null&&(o.childLanes|=t)):o!==null&&(o.childLanes&t)!==t&&(o.childLanes|=t),e===n)break;e=e.return}}function or(e,t){Qo=e,$i=rr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(tt=!0),e.firstContext=null)}function dt(e){var t=e._currentValue;if($i!==e)if(e={context:e,memoizedValue:t,next:null},rr===null){if(Qo===null)throw Error(i(308));rr=e,Qo.dependencies={lanes:0,firstContext:e}}else rr=rr.next=e;return t}var Pn=null;function Vi(e){Pn===null?Pn=[e]:Pn.push(e)}function Gu(e,t,n,o){var a=t.interleaved;return a===null?(n.next=n,Vi(t)):(n.next=a.next,a.next=n),t.interleaved=n,$t(e,o)}function $t(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var an=!1;function Hi(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Yu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ut(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function sn(e,t,n){var o=e.updateQueue;if(o===null)return null;if(o=o.shared,(pe&2)!==0){var a=o.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),o.pending=t,$t(e,n)}return a=o.interleaved,a===null?(t.next=t,Vi(o)):(t.next=a.next,a.next=t),o.interleaved=t,$t(e,n)}function Ko(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,li(e,n)}}function Xu(e,t){var n=e.updateQueue,o=e.alternate;if(o!==null&&(o=o.updateQueue,n===o)){var a=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var f={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};s===null?a=s=f:s=s.next=f,n=n.next}while(n!==null);s===null?a=s=t:s=s.next=t}else a=s=t;n={baseState:o.baseState,firstBaseUpdate:a,lastBaseUpdate:s,shared:o.shared,effects:o.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Go(e,t,n,o){var a=e.updateQueue;an=!1;var s=a.firstBaseUpdate,f=a.lastBaseUpdate,g=a.shared.pending;if(g!==null){a.shared.pending=null;var w=g,L=w.next;w.next=null,f===null?s=L:f.next=L,f=w;var M=e.alternate;M!==null&&(M=M.updateQueue,g=M.lastBaseUpdate,g!==f&&(g===null?M.firstBaseUpdate=L:g.next=L,M.lastBaseUpdate=w))}if(s!==null){var I=a.baseState;f=0,M=L=w=null,g=s;do{var D=g.lane,V=g.eventTime;if((o&D)===D){M!==null&&(M=M.next={eventTime:V,lane:0,tag:g.tag,payload:g.payload,callback:g.callback,next:null});e:{var Q=e,X=g;switch(D=t,V=n,X.tag){case 1:if(Q=X.payload,typeof Q=="function"){I=Q.call(V,I,D);break e}I=Q;break e;case 3:Q.flags=Q.flags&-65537|128;case 0:if(Q=X.payload,D=typeof Q=="function"?Q.call(V,I,D):Q,D==null)break e;I=O({},I,D);break e;case 2:an=!0}}g.callback!==null&&g.lane!==0&&(e.flags|=64,D=a.effects,D===null?a.effects=[g]:D.push(g))}else V={eventTime:V,lane:D,tag:g.tag,payload:g.payload,callback:g.callback,next:null},M===null?(L=M=V,w=I):M=M.next=V,f|=D;if(g=g.next,g===null){if(g=a.shared.pending,g===null)break;D=g,g=D.next,D.next=null,a.lastBaseUpdate=D,a.shared.pending=null}}while(!0);if(M===null&&(w=I),a.baseState=w,a.firstBaseUpdate=L,a.lastBaseUpdate=M,t=a.shared.interleaved,t!==null){a=t;do f|=a.lane,a=a.next;while(a!==t)}else s===null&&(a.shared.lanes=0);Tn|=f,e.lanes=f,e.memoizedState=I}}function Ju(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var o=Xi.transition;Xi.transition={};try{e(!1),t()}finally{ye=n,Xi.transition=o}}function vc(){return ft().memoizedState}function Um(e,t,n){var o=fn(e);if(n={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null},gc(e))yc(t,n);else if(n=Gu(e,t,n,o),n!==null){var a=Je();Et(n,e,o,a),wc(n,t,o)}}function Wm(e,t,n){var o=fn(e),a={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null};if(gc(e))yc(t,a);else{var s=e.alternate;if(e.lanes===0&&(s===null||s.lanes===0)&&(s=t.lastRenderedReducer,s!==null))try{var f=t.lastRenderedState,g=s(f,n);if(a.hasEagerState=!0,a.eagerState=g,yt(g,f)){var w=t.interleaved;w===null?(a.next=a,Vi(t)):(a.next=w.next,w.next=a),t.interleaved=a;return}}catch{}finally{}n=Gu(e,t,a,o),n!==null&&(a=Je(),Et(n,e,o,a),wc(n,t,o))}}function gc(e){var t=e.alternate;return e===Ne||t!==null&&t===Ne}function yc(e,t){Kr=Jo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function wc(e,t,n){if((n&4194240)!==0){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,li(e,n)}}var el={readContext:dt,useCallback:He,useContext:He,useEffect:He,useImperativeHandle:He,useInsertionEffect:He,useLayoutEffect:He,useMemo:He,useReducer:He,useRef:He,useState:He,useDebugValue:He,useDeferredValue:He,useTransition:He,useMutableSource:He,useSyncExternalStore:He,useId:He,unstable_isNewReconciler:!1},Bm={readContext:dt,useCallback:function(e,t){return _t().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:sc,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Zo(4194308,4,dc.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Zo(4194308,4,e,t)},useInsertionEffect:function(e,t){return Zo(4,2,e,t)},useMemo:function(e,t){var n=_t();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var o=_t();return t=n!==void 0?n(t):t,o.memoizedState=o.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},o.queue=e,e=e.dispatch=Um.bind(null,Ne,e),[o.memoizedState,e]},useRef:function(e){var t=_t();return e={current:e},t.memoizedState=e},useState:ic,useDebugValue:ra,useDeferredValue:function(e){return _t().memoizedState=e},useTransition:function(){var e=ic(!1),t=e[0];return e=$m.bind(null,e[1]),_t().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var o=Ne,a=_t();if(Ce){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),Ie===null)throw Error(i(349));(Ln&30)!==0||tc(o,t,n)}a.memoizedState=n;var s={value:n,getSnapshot:t};return a.queue=s,sc(rc.bind(null,o,s,e),[e]),o.flags|=2048,Xr(9,nc.bind(null,o,s,n,t),void 0,null),n},useId:function(){var e=_t(),t=Ie.identifierPrefix;if(Ce){var n=Ft,o=At;n=(o&~(1<<32-gt(o)-1)).toString(32)+n,t=":"+t+"R"+n,n=Gr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof o.is=="string"?e=f.createElement(n,{is:o.is}):(e=f.createElement(n),n==="select"&&(f=e,o.multiple?f.multiple=!0:o.size&&(f.size=o.size))):e=f.createElementNS(e,n),e[Nt]=t,e[Ur]=o,Fc(e,t,!1,!1),t.stateNode=e;e:{switch(f=Yl(n,o),n){case"dialog":ke("cancel",e),ke("close",e),a=o;break;case"iframe":case"object":case"embed":ke("load",e),a=o;break;case"video":case"audio":for(a=0;aur&&(t.flags|=128,o=!0,Jr(s,!1),t.lanes=4194304)}else{if(!o)if(e=Yo(f),e!==null){if(t.flags|=128,o=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Jr(s,!0),s.tail===null&&s.tailMode==="hidden"&&!f.alternate&&!Ce)return Qe(t),null}else 2*Le()-s.renderingStartTime>ur&&n!==1073741824&&(t.flags|=128,o=!0,Jr(s,!1),t.lanes=4194304);s.isBackwards?(f.sibling=t.child,t.child=f):(n=s.last,n!==null?n.sibling=f:t.child=f,s.last=f)}return s.tail!==null?(t=s.tail,s.rendering=t,s.tail=t.sibling,s.renderingStartTime=Le(),t.sibling=null,n=Re.current,xe(Re,o?n&1|2:n&1),t):(Qe(t),null);case 22:case 23:return Pa(),o=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==o&&(t.flags|=8192),o&&(t.mode&1)!==0?(st&1073741824)!==0&&(Qe(t),t.subtreeFlags&6&&(t.flags|=8192)):Qe(t),null;case 24:return null;case 25:return null}throw Error(i(156,t.tag))}function Jm(e,t){switch(bi(t),t.tag){case 1:return et(t.type)&&Ao(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return lr(),Se(qe),Se(Ve),Yi(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Ki(t),null;case 13:if(Se(Re),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));tr()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Se(Re),null;case 4:return lr(),null;case 10:return Wi(t.type._context),null;case 22:case 23:return Pa(),null;case 24:return null;default:return null}}var ol=!1,Ke=!1,Zm=typeof WeakSet=="function"?WeakSet:Set,H=null;function ar(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(o){_e(e,t,o)}else n.current=null}function ha(e,t,n){try{n()}catch(o){_e(e,t,o)}}var Wc=!1;function qm(e,t){if(Pi=Co,e=xu(),wi(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var o=n.getSelection&&n.getSelection();if(o&&o.rangeCount!==0){n=o.anchorNode;var a=o.anchorOffset,s=o.focusNode;o=o.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var f=0,g=-1,w=-1,L=0,M=0,I=e,D=null;t:for(;;){for(var V;I!==n||a!==0&&I.nodeType!==3||(g=f+a),I!==s||o!==0&&I.nodeType!==3||(w=f+o),I.nodeType===3&&(f+=I.nodeValue.length),(V=I.firstChild)!==null;)D=I,I=V;for(;;){if(I===e)break t;if(D===n&&++L===a&&(g=f),D===s&&++M===o&&(w=f),(V=I.nextSibling)!==null)break;I=D,D=I.parentNode}I=V}n=g===-1||w===-1?null:{start:g,end:w}}else n=null}n=n||{start:0,end:0}}else n=null;for(_i={focusedElem:e,selectionRange:n},Co=!1,H=t;H!==null;)if(t=H,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,H=e;else for(;H!==null;){t=H;try{var Q=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(Q!==null){var X=Q.memoizedProps,Te=Q.memoizedState,R=t.stateNode,x=R.getSnapshotBeforeUpdate(t.elementType===t.type?X:xt(t.type,X),Te);R.__reactInternalSnapshotBeforeUpdate=x}break;case 3:var P=t.stateNode.containerInfo;P.nodeType===1?P.textContent="":P.nodeType===9&&P.documentElement&&P.removeChild(P.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(i(163))}}catch(F){_e(t,t.return,F)}if(e=t.sibling,e!==null){e.return=t.return,H=e;break}H=t.return}return Q=Wc,Wc=!1,Q}function Zr(e,t,n){var o=t.updateQueue;if(o=o!==null?o.lastEffect:null,o!==null){var a=o=o.next;do{if((a.tag&e)===e){var s=a.destroy;a.destroy=void 0,s!==void 0&&ha(t,n,s)}a=a.next}while(a!==o)}}function ll(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var o=n.create;n.destroy=o()}n=n.next}while(n!==t)}}function va(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Bc(e){var t=e.alternate;t!==null&&(e.alternate=null,Bc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Nt],delete t[Ur],delete t[ji],delete t[Mm],delete t[bm])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Vc(e){return e.tag===5||e.tag===3||e.tag===4}function Hc(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Vc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ga(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=bo));else if(o!==4&&(e=e.child,e!==null))for(ga(e,t,n),e=e.sibling;e!==null;)ga(e,t,n),e=e.sibling}function ya(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(o!==4&&(e=e.child,e!==null))for(ya(e,t,n),e=e.sibling;e!==null;)ya(e,t,n),e=e.sibling}var $e=null,kt=!1;function un(e,t,n){for(n=n.child;n!==null;)Qc(e,t,n),n=n.sibling}function Qc(e,t,n){if(Rt&&typeof Rt.onCommitFiberUnmount=="function")try{Rt.onCommitFiberUnmount(yo,n)}catch{}switch(n.tag){case 5:Ke||ar(n,t);case 6:var o=$e,a=kt;$e=null,un(e,t,n),$e=o,kt=a,$e!==null&&(kt?(e=$e,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):$e.removeChild(n.stateNode));break;case 18:$e!==null&&(kt?(e=$e,n=n.stateNode,e.nodeType===8?Oi(e.parentNode,n):e.nodeType===1&&Oi(e,n),Tr(e)):Oi($e,n.stateNode));break;case 4:o=$e,a=kt,$e=n.stateNode.containerInfo,kt=!0,un(e,t,n),$e=o,kt=a;break;case 0:case 11:case 14:case 15:if(!Ke&&(o=n.updateQueue,o!==null&&(o=o.lastEffect,o!==null))){a=o=o.next;do{var s=a,f=s.destroy;s=s.tag,f!==void 0&&((s&2)!==0||(s&4)!==0)&&ha(n,t,f),a=a.next}while(a!==o)}un(e,t,n);break;case 1:if(!Ke&&(ar(n,t),o=n.stateNode,typeof o.componentWillUnmount=="function"))try{o.props=n.memoizedProps,o.state=n.memoizedState,o.componentWillUnmount()}catch(g){_e(n,t,g)}un(e,t,n);break;case 21:un(e,t,n);break;case 22:n.mode&1?(Ke=(o=Ke)||n.memoizedState!==null,un(e,t,n),Ke=o):un(e,t,n);break;default:un(e,t,n)}}function Kc(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Zm),t.forEach(function(o){var a=sh.bind(null,e,o);n.has(o)||(n.add(o),o.then(a,a))})}}function St(e,t){var n=t.deletions;if(n!==null)for(var o=0;oa&&(a=f),o&=~s}if(o=a,o=Le()-o,o=(120>o?120:480>o?480:1080>o?1080:1920>o?1920:3e3>o?3e3:4320>o?4320:1960*th(o/1960))-o,10e?16:e,dn===null)var o=!1;else{if(e=dn,dn=null,cl=0,(pe&6)!==0)throw Error(i(331));var a=pe;for(pe|=4,H=e.current;H!==null;){var s=H,f=s.child;if((H.flags&16)!==0){var g=s.deletions;if(g!==null){for(var w=0;wLe()-ka?jn(e,0):xa|=n),rt(e,t)}function id(e,t){t===0&&((e.mode&1)===0?t=1:(t=xo,xo<<=1,(xo&130023424)===0&&(xo=4194304)));var n=Je();e=$t(e,t),e!==null&&(Rr(e,t,n),rt(e,n))}function ah(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),id(e,n)}function sh(e,t){var n=0;switch(e.tag){case 13:var o=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:o=e.stateNode;break;default:throw Error(i(314))}o!==null&&o.delete(t),id(e,n)}var ad;ad=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||qe.current)tt=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return tt=!1,Ym(e,t,n);tt=(e.flags&131072)!==0}else tt=!1,Ce&&(t.flags&1048576)!==0&&$u(t,Wo,t.index);switch(t.lanes=0,t.tag){case 2:var o=t.type;rl(e,t),e=t.pendingProps;var a=Zn(t,Ve.current);or(t,n),a=Zi(null,t,o,e,a,n);var s=qi();return t.flags|=1,typeof a=="object"&&a!==null&&typeof a.render=="function"&&a.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,et(o)?(s=!0,Fo(t)):s=!1,t.memoizedState=a.state!==null&&a.state!==void 0?a.state:null,Hi(t),a.updater=tl,t.stateNode=a,a._reactInternals=t,la(t,o,e,n),t=ua(null,t,o,!0,s,n)):(t.tag=0,Ce&&s&&Mi(t),Xe(null,t,a,n),t=t.child),t;case 16:o=t.elementType;e:{switch(rl(e,t),e=t.pendingProps,a=o._init,o=a(o._payload),t.type=o,a=t.tag=ch(o),e=xt(o,e),a){case 0:t=sa(null,t,o,e,n);break e;case 1:t=zc(null,t,o,e,n);break e;case 11:t=_c(null,t,o,e,n);break e;case 14:t=Lc(null,t,o,xt(o.type,e),n);break e}throw Error(i(306,o,""))}return t;case 0:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:xt(o,a),sa(e,t,o,a,n);case 1:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:xt(o,a),zc(e,t,o,a,n);case 3:e:{if(Dc(t),e===null)throw Error(i(387));o=t.pendingProps,s=t.memoizedState,a=s.element,Yu(e,t),Go(t,o,null,n);var f=t.memoizedState;if(o=f.element,s.isDehydrated)if(s={element:o,isDehydrated:!1,cache:f.cache,pendingSuspenseBoundaries:f.pendingSuspenseBoundaries,transitions:f.transitions},t.updateQueue.baseState=s,t.memoizedState=s,t.flags&256){a=ir(Error(i(423)),t),t=Mc(e,t,o,n,a);break e}else if(o!==a){a=ir(Error(i(424)),t),t=Mc(e,t,o,n,a);break e}else for(at=nn(t.stateNode.containerInfo.firstChild),it=t,Ce=!0,wt=null,n=Ku(t,null,o,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(tr(),o===a){t=Wt(e,t,n);break e}Xe(e,t,o,n)}t=t.child}return t;case 5:return Zu(t),e===null&&Ai(t),o=t.type,a=t.pendingProps,s=e!==null?e.memoizedProps:null,f=a.children,Li(o,a)?f=null:s!==null&&Li(o,s)&&(t.flags|=32),jc(e,t),Xe(e,t,f,n),t.child;case 6:return e===null&&Ai(t),null;case 13:return bc(e,t,n);case 4:return Qi(t,t.stateNode.containerInfo),o=t.pendingProps,e===null?t.child=nr(t,null,o,n):Xe(e,t,o,n),t.child;case 11:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:xt(o,a),_c(e,t,o,a,n);case 7:return Xe(e,t,t.pendingProps,n),t.child;case 8:return Xe(e,t,t.pendingProps.children,n),t.child;case 12:return Xe(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(o=t.type._context,a=t.pendingProps,s=t.memoizedProps,f=a.value,xe(Ho,o._currentValue),o._currentValue=f,s!==null)if(yt(s.value,f)){if(s.children===a.children&&!qe.current){t=Wt(e,t,n);break e}}else for(s=t.child,s!==null&&(s.return=t);s!==null;){var g=s.dependencies;if(g!==null){f=s.child;for(var w=g.firstContext;w!==null;){if(w.context===o){if(s.tag===1){w=Ut(-1,n&-n),w.tag=2;var L=s.updateQueue;if(L!==null){L=L.shared;var M=L.pending;M===null?w.next=w:(w.next=M.next,M.next=w),L.pending=w}}s.lanes|=n,w=s.alternate,w!==null&&(w.lanes|=n),Bi(s.return,n,t),g.lanes|=n;break}w=w.next}}else if(s.tag===10)f=s.type===t.type?null:s.child;else if(s.tag===18){if(f=s.return,f===null)throw Error(i(341));f.lanes|=n,g=f.alternate,g!==null&&(g.lanes|=n),Bi(f,n,t),f=s.sibling}else f=s.child;if(f!==null)f.return=s;else for(f=s;f!==null;){if(f===t){f=null;break}if(s=f.sibling,s!==null){s.return=f.return,f=s;break}f=f.return}s=f}Xe(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,o=t.pendingProps.children,or(t,n),a=dt(a),o=o(a),t.flags|=1,Xe(e,t,o,n),t.child;case 14:return o=t.type,a=xt(o,t.pendingProps),a=xt(o.type,a),Lc(e,t,o,a,n);case 15:return Tc(e,t,t.type,t.pendingProps,n);case 17:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:xt(o,a),rl(e,t),t.tag=1,et(o)?(e=!0,Fo(t)):e=!1,or(t,n),kc(t,o,a),la(t,o,a,n),ua(null,t,o,!0,e,n);case 19:return Ac(e,t,n);case 22:return Oc(e,t,n)}throw Error(i(156,t.tag))};function sd(e,t){return Us(e,t)}function uh(e,t,n,o){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=o,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function mt(e,t,n,o){return new uh(e,t,n,o)}function La(e){return e=e.prototype,!(!e||!e.isReactComponent)}function ch(e){if(typeof e=="function")return La(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Me)return 11;if(e===Fe)return 14}return 2}function mn(e,t){var n=e.alternate;return n===null?(n=mt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function ml(e,t,n,o,a,s){var f=2;if(o=e,typeof e=="function")La(e)&&(f=1);else if(typeof e=="string")f=5;else e:switch(e){case q:return Dn(n.children,a,s,t);case W:f=8,a|=8;break;case fe:return e=mt(12,n,t,a|2),e.elementType=fe,e.lanes=s,e;case We:return e=mt(13,n,t,a),e.elementType=We,e.lanes=s,e;case Ye:return e=mt(19,n,t,a),e.elementType=Ye,e.lanes=s,e;case ge:return hl(n,a,s,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ee:f=10;break e;case Ge:f=9;break e;case Me:f=11;break e;case Fe:f=14;break e;case we:f=16,o=null;break e}throw Error(i(130,e==null?e:typeof e,""))}return t=mt(f,n,t,a),t.elementType=e,t.type=o,t.lanes=s,t}function Dn(e,t,n,o){return e=mt(7,e,o,t),e.lanes=n,e}function hl(e,t,n,o){return e=mt(22,e,o,t),e.elementType=ge,e.lanes=n,e.stateNode={isHidden:!1},e}function Ta(e,t,n){return e=mt(6,e,null,t),e.lanes=n,e}function Oa(e,t,n){return t=mt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function dh(e,t,n,o,a){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=oi(0),this.expirationTimes=oi(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=oi(0),this.identifierPrefix=o,this.onRecoverableError=a,this.mutableSourceEagerHydrationData=null}function ja(e,t,n,o,a,s,f,g,w){return e=new dh(e,t,n,g,w),t===1?(t=1,s===!0&&(t|=8)):t=0,s=mt(3,null,null,t),e.current=s,s.stateNode=e,s.memoizedState={element:o,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Hi(s),e}function fh(e,t,n){var o=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(l){console.error(l)}}return r(),Aa.exports=Ch(),Aa.exports}var Sd;function Rh(){if(Sd)return Sl;Sd=1;var r=tf();return Sl.createRoot=r.createRoot,Sl.hydrateRoot=r.hydrateRoot,Sl}var Nh=Rh();const Ph=as(Nh);/** + * react-router v7.13.0 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var Ed="popstate";function _h(r={}){function l(u,c){let{pathname:d,search:p,hash:m}=u.location;return ns("",{pathname:d,search:p,hash:m},c.state&&c.state.usr||null,c.state&&c.state.key||"default")}function i(u,c){return typeof c=="string"?c:ao(c)}return Th(l,i,null,r)}function Pe(r,l){if(r===!1||r===null||typeof r>"u")throw new Error(l)}function jt(r,l){if(!r){typeof console<"u"&&console.warn(l);try{throw new Error(l)}catch{}}}function Lh(){return Math.random().toString(36).substring(2,10)}function Cd(r,l){return{usr:r.state,key:r.key,idx:l}}function ns(r,l,i=null,u){return{pathname:typeof r=="string"?r:r.pathname,search:"",hash:"",...typeof l=="string"?vr(l):l,state:i,key:l&&l.key||u||Lh()}}function ao({pathname:r="/",search:l="",hash:i=""}){return l&&l!=="?"&&(r+=l.charAt(0)==="?"?l:"?"+l),i&&i!=="#"&&(r+=i.charAt(0)==="#"?i:"#"+i),r}function vr(r){let l={};if(r){let i=r.indexOf("#");i>=0&&(l.hash=r.substring(i),r=r.substring(0,i));let u=r.indexOf("?");u>=0&&(l.search=r.substring(u),r=r.substring(0,u)),r&&(l.pathname=r)}return l}function Th(r,l,i,u={}){let{window:c=document.defaultView,v5Compat:d=!1}=u,p=c.history,m="POP",v=null,y=k();y==null&&(y=0,p.replaceState({...p.state,idx:y},""));function k(){return(p.state||{idx:null}).idx}function S(){m="POP";let T=k(),b=T==null?null:T-y;y=T,v&&v({action:m,location:N.location,delta:b})}function _(T,b){m="PUSH";let U=ns(N.location,T,b);y=k()+1;let A=Cd(U,y),Y=N.createHref(U);try{p.pushState(A,"",Y)}catch(Z){if(Z instanceof DOMException&&Z.name==="DataCloneError")throw Z;c.location.assign(Y)}d&&v&&v({action:m,location:N.location,delta:1})}function j(T,b){m="REPLACE";let U=ns(N.location,T,b);y=k();let A=Cd(U,y),Y=N.createHref(U);p.replaceState(A,"",Y),d&&v&&v({action:m,location:N.location,delta:0})}function $(T){return Oh(T)}let N={get action(){return m},get location(){return r(c,p)},listen(T){if(v)throw new Error("A history only accepts one active listener");return c.addEventListener(Ed,S),v=T,()=>{c.removeEventListener(Ed,S),v=null}},createHref(T){return l(c,T)},createURL:$,encodeLocation(T){let b=$(T);return{pathname:b.pathname,search:b.search,hash:b.hash}},push:_,replace:j,go(T){return p.go(T)}};return N}function Oh(r,l=!1){let i="http://localhost";typeof window<"u"&&(i=window.location.origin!=="null"?window.location.origin:window.location.href),Pe(i,"No window.location.(origin|href) available to create URL");let u=typeof r=="string"?r:ao(r);return u=u.replace(/ $/,"%20"),!l&&u.startsWith("//")&&(u=i+u),new URL(u,i)}function nf(r,l,i="/"){return jh(r,l,i,!1)}function jh(r,l,i,u){let c=typeof l=="string"?vr(l):l,d=Qt(c.pathname||"/",i);if(d==null)return null;let p=rf(r);zh(p);let m=null;for(let v=0;m==null&&v{let k={relativePath:y===void 0?p.path||"":y,caseSensitive:p.caseSensitive===!0,childrenIndex:m,route:p};if(k.relativePath.startsWith("/")){if(!k.relativePath.startsWith(u)&&v)return;Pe(k.relativePath.startsWith(u),`Absolute route path "${k.relativePath}" nested under path "${u}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),k.relativePath=k.relativePath.slice(u.length)}let S=Ht([u,k.relativePath]),_=i.concat(k);p.children&&p.children.length>0&&(Pe(p.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${S}".`),rf(p.children,l,_,S,v)),!(p.path==null&&!p.index)&&l.push({path:S,score:$h(S,p.index),routesMeta:_})};return r.forEach((p,m)=>{var v;if(p.path===""||!((v=p.path)!=null&&v.includes("?")))d(p,m);else for(let y of of(p.path))d(p,m,!0,y)}),l}function of(r){let l=r.split("/");if(l.length===0)return[];let[i,...u]=l,c=i.endsWith("?"),d=i.replace(/\?$/,"");if(u.length===0)return c?[d,""]:[d];let p=of(u.join("/")),m=[];return m.push(...p.map(v=>v===""?d:[d,v].join("/"))),c&&m.push(...p),m.map(v=>r.startsWith("/")&&v===""?"/":v)}function zh(r){r.sort((l,i)=>l.score!==i.score?i.score-l.score:Uh(l.routesMeta.map(u=>u.childrenIndex),i.routesMeta.map(u=>u.childrenIndex)))}var Dh=/^:[\w-]+$/,Mh=3,bh=2,Ih=1,Ah=10,Fh=-2,Rd=r=>r==="*";function $h(r,l){let i=r.split("/"),u=i.length;return i.some(Rd)&&(u+=Fh),l&&(u+=bh),i.filter(c=>!Rd(c)).reduce((c,d)=>c+(Dh.test(d)?Mh:d===""?Ih:Ah),u)}function Uh(r,l){return r.length===l.length&&r.slice(0,-1).every((u,c)=>u===l[c])?r[r.length-1]-l[l.length-1]:0}function Wh(r,l,i=!1){let{routesMeta:u}=r,c={},d="/",p=[];for(let m=0;m{if(k==="*"){let $=m[_]||"";p=d.slice(0,d.length-$.length).replace(/(.)\/+$/,"$1")}const j=m[_];return S&&!j?y[k]=void 0:y[k]=(j||"").replace(/%2F/g,"/"),y},{}),pathname:d,pathnameBase:p,pattern:r}}function Bh(r,l=!1,i=!0){jt(r==="*"||!r.endsWith("*")||r.endsWith("/*"),`Route path "${r}" will be treated as if it were "${r.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${r.replace(/\*$/,"/*")}".`);let u=[],c="^"+r.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(p,m,v)=>(u.push({paramName:m,isOptional:v!=null}),v?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return r.endsWith("*")?(u.push({paramName:"*"}),c+=r==="*"||r==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):i?c+="\\/*$":r!==""&&r!=="/"&&(c+="(?:(?=\\/|$))"),[new RegExp(c,l?void 0:"i"),u]}function Vh(r){try{return r.split("/").map(l=>decodeURIComponent(l).replace(/\//g,"%2F")).join("/")}catch(l){return jt(!1,`The URL path "${r}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${l}).`),r}}function Qt(r,l){if(l==="/")return r;if(!r.toLowerCase().startsWith(l.toLowerCase()))return null;let i=l.endsWith("/")?l.length-1:l.length,u=r.charAt(i);return u&&u!=="/"?null:r.slice(i)||"/"}var Hh=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function Qh(r,l="/"){let{pathname:i,search:u="",hash:c=""}=typeof r=="string"?vr(r):r,d;return i?(i=i.replace(/\/\/+/g,"/"),i.startsWith("/")?d=Nd(i.substring(1),"/"):d=Nd(i,l)):d=l,{pathname:d,search:Yh(u),hash:Xh(c)}}function Nd(r,l){let i=l.replace(/\/+$/,"").split("/");return r.split("/").forEach(c=>{c===".."?i.length>1&&i.pop():c!=="."&&i.push(c)}),i.length>1?i.join("/"):"/"}function Ua(r,l,i,u){return`Cannot include a '${r}' character in a manually specified \`to.${l}\` field [${JSON.stringify(u)}]. Please separate it out to the \`to.${i}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Kh(r){return r.filter((l,i)=>i===0||l.route.path&&l.route.path.length>0)}function lf(r){let l=Kh(r);return l.map((i,u)=>u===l.length-1?i.pathname:i.pathnameBase)}function af(r,l,i,u=!1){let c;typeof r=="string"?c=vr(r):(c={...r},Pe(!c.pathname||!c.pathname.includes("?"),Ua("?","pathname","search",c)),Pe(!c.pathname||!c.pathname.includes("#"),Ua("#","pathname","hash",c)),Pe(!c.search||!c.search.includes("#"),Ua("#","search","hash",c)));let d=r===""||c.pathname==="",p=d?"/":c.pathname,m;if(p==null)m=i;else{let S=l.length-1;if(!u&&p.startsWith("..")){let _=p.split("/");for(;_[0]==="..";)_.shift(),S-=1;c.pathname=_.join("/")}m=S>=0?l[S]:"/"}let v=Qh(c,m),y=p&&p!=="/"&&p.endsWith("/"),k=(d||p===".")&&i.endsWith("/");return!v.pathname.endsWith("/")&&(y||k)&&(v.pathname+="/"),v}var Ht=r=>r.join("/").replace(/\/\/+/g,"/"),Gh=r=>r.replace(/\/+$/,"").replace(/^\/*/,"/"),Yh=r=>!r||r==="?"?"":r.startsWith("?")?r:"?"+r,Xh=r=>!r||r==="#"?"":r.startsWith("#")?r:"#"+r,Jh=class{constructor(r,l,i,u=!1){this.status=r,this.statusText=l||"",this.internal=u,i instanceof Error?(this.data=i.toString(),this.error=i):this.data=i}};function Zh(r){return r!=null&&typeof r.status=="number"&&typeof r.statusText=="string"&&typeof r.internal=="boolean"&&"data"in r}function qh(r){return r.map(l=>l.route.path).filter(Boolean).join("/").replace(/\/\/*/g,"/")||"/"}var sf=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function uf(r,l){let i=r;if(typeof i!="string"||!Hh.test(i))return{absoluteURL:void 0,isExternal:!1,to:i};let u=i,c=!1;if(sf)try{let d=new URL(window.location.href),p=i.startsWith("//")?new URL(d.protocol+i):new URL(i),m=Qt(p.pathname,l);p.origin===d.origin&&m!=null?i=m+p.search+p.hash:c=!0}catch{jt(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:u,isExternal:c,to:i}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var cf=["POST","PUT","PATCH","DELETE"];new Set(cf);var ev=["GET",...cf];new Set(ev);var gr=h.createContext(null);gr.displayName="DataRouter";var Fl=h.createContext(null);Fl.displayName="DataRouterState";var tv=h.createContext(!1),df=h.createContext({isTransitioning:!1});df.displayName="ViewTransition";var nv=h.createContext(new Map);nv.displayName="Fetchers";var rv=h.createContext(null);rv.displayName="Await";var vt=h.createContext(null);vt.displayName="Navigation";var co=h.createContext(null);co.displayName="Location";var Kt=h.createContext({outlet:null,matches:[],isDataRoute:!1});Kt.displayName="Route";var us=h.createContext(null);us.displayName="RouteError";var ff="REACT_ROUTER_ERROR",ov="REDIRECT",lv="ROUTE_ERROR_RESPONSE";function iv(r){if(r.startsWith(`${ff}:${ov}:{`))try{let l=JSON.parse(r.slice(28));if(typeof l=="object"&&l&&typeof l.status=="number"&&typeof l.statusText=="string"&&typeof l.location=="string"&&typeof l.reloadDocument=="boolean"&&typeof l.replace=="boolean")return l}catch{}}function av(r){if(r.startsWith(`${ff}:${lv}:{`))try{let l=JSON.parse(r.slice(40));if(typeof l=="object"&&l&&typeof l.status=="number"&&typeof l.statusText=="string")return new Jh(l.status,l.statusText,l.data)}catch{}}function sv(r,{relative:l}={}){Pe(fo(),"useHref() may be used only in the context of a component.");let{basename:i,navigator:u}=h.useContext(vt),{hash:c,pathname:d,search:p}=po(r,{relative:l}),m=d;return i!=="/"&&(m=d==="/"?i:Ht([i,d])),u.createHref({pathname:m,search:p,hash:c})}function fo(){return h.useContext(co)!=null}function In(){return Pe(fo(),"useLocation() may be used only in the context of a component."),h.useContext(co).location}var pf="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function mf(r){h.useContext(vt).static||h.useLayoutEffect(r)}function uv(){let{isDataRoute:r}=h.useContext(Kt);return r?Sv():cv()}function cv(){Pe(fo(),"useNavigate() may be used only in the context of a component.");let r=h.useContext(gr),{basename:l,navigator:i}=h.useContext(vt),{matches:u}=h.useContext(Kt),{pathname:c}=In(),d=JSON.stringify(lf(u)),p=h.useRef(!1);return mf(()=>{p.current=!0}),h.useCallback((v,y={})=>{if(jt(p.current,pf),!p.current)return;if(typeof v=="number"){i.go(v);return}let k=af(v,JSON.parse(d),c,y.relative==="path");r==null&&l!=="/"&&(k.pathname=k.pathname==="/"?l:Ht([l,k.pathname])),(y.replace?i.replace:i.push)(k,y.state,y)},[l,i,d,c,r])}h.createContext(null);function po(r,{relative:l}={}){let{matches:i}=h.useContext(Kt),{pathname:u}=In(),c=JSON.stringify(lf(i));return h.useMemo(()=>af(r,JSON.parse(c),u,l==="path"),[r,c,u,l])}function dv(r,l){return hf(r,l)}function hf(r,l,i,u,c){var U;Pe(fo(),"useRoutes() may be used only in the context of a component.");let{navigator:d}=h.useContext(vt),{matches:p}=h.useContext(Kt),m=p[p.length-1],v=m?m.params:{},y=m?m.pathname:"/",k=m?m.pathnameBase:"/",S=m&&m.route;{let A=S&&S.path||"";gf(y,!S||A.endsWith("*")||A.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${y}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let _=In(),j;if(l){let A=typeof l=="string"?vr(l):l;Pe(k==="/"||((U=A.pathname)==null?void 0:U.startsWith(k)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${k}" but pathname "${A.pathname}" was given in the \`location\` prop.`),j=A}else j=_;let $=j.pathname||"/",N=$;if(k!=="/"){let A=k.replace(/^\//,"").split("/");N="/"+$.replace(/^\//,"").split("/").slice(A.length).join("/")}let T=nf(r,{pathname:N});jt(S||T!=null,`No routes matched location "${j.pathname}${j.search}${j.hash}" `),jt(T==null||T[T.length-1].route.element!==void 0||T[T.length-1].route.Component!==void 0||T[T.length-1].route.lazy!==void 0,`Matched leaf route at location "${j.pathname}${j.search}${j.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let b=vv(T&&T.map(A=>Object.assign({},A,{params:Object.assign({},v,A.params),pathname:Ht([k,d.encodeLocation?d.encodeLocation(A.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:A.pathname]),pathnameBase:A.pathnameBase==="/"?k:Ht([k,d.encodeLocation?d.encodeLocation(A.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:A.pathnameBase])})),p,i,u,c);return l&&b?h.createElement(co.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...j},navigationType:"POP"}},b):b}function fv(){let r=kv(),l=Zh(r)?`${r.status} ${r.statusText}`:r instanceof Error?r.message:JSON.stringify(r),i=r instanceof Error?r.stack:null,u="rgba(200,200,200, 0.5)",c={padding:"0.5rem",backgroundColor:u},d={padding:"2px 4px",backgroundColor:u},p=null;return console.error("Error handled by React Router default ErrorBoundary:",r),p=h.createElement(h.Fragment,null,h.createElement("p",null,"💿 Hey developer 👋"),h.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",h.createElement("code",{style:d},"ErrorBoundary")," or"," ",h.createElement("code",{style:d},"errorElement")," prop on your route.")),h.createElement(h.Fragment,null,h.createElement("h2",null,"Unexpected Application Error!"),h.createElement("h3",{style:{fontStyle:"italic"}},l),i?h.createElement("pre",{style:c},i):null,p)}var pv=h.createElement(fv,null),vf=class extends h.Component{constructor(r){super(r),this.state={location:r.location,revalidation:r.revalidation,error:r.error}}static getDerivedStateFromError(r){return{error:r}}static getDerivedStateFromProps(r,l){return l.location!==r.location||l.revalidation!=="idle"&&r.revalidation==="idle"?{error:r.error,location:r.location,revalidation:r.revalidation}:{error:r.error!==void 0?r.error:l.error,location:l.location,revalidation:r.revalidation||l.revalidation}}componentDidCatch(r,l){this.props.onError?this.props.onError(r,l):console.error("React Router caught the following error during render",r)}render(){let r=this.state.error;if(this.context&&typeof r=="object"&&r&&"digest"in r&&typeof r.digest=="string"){const i=av(r.digest);i&&(r=i)}let l=r!==void 0?h.createElement(Kt.Provider,{value:this.props.routeContext},h.createElement(us.Provider,{value:r,children:this.props.component})):this.props.children;return this.context?h.createElement(mv,{error:r},l):l}};vf.contextType=tv;var Wa=new WeakMap;function mv({children:r,error:l}){let{basename:i}=h.useContext(vt);if(typeof l=="object"&&l&&"digest"in l&&typeof l.digest=="string"){let u=iv(l.digest);if(u){let c=Wa.get(l);if(c)throw c;let d=uf(u.location,i);if(sf&&!Wa.get(l))if(d.isExternal||u.reloadDocument)window.location.href=d.absoluteURL||d.to;else{const p=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(d.to,{replace:u.replace}));throw Wa.set(l,p),p}return h.createElement("meta",{httpEquiv:"refresh",content:`0;url=${d.absoluteURL||d.to}`})}}return r}function hv({routeContext:r,match:l,children:i}){let u=h.useContext(gr);return u&&u.static&&u.staticContext&&(l.route.errorElement||l.route.ErrorBoundary)&&(u.staticContext._deepestRenderedBoundaryId=l.route.id),h.createElement(Kt.Provider,{value:r},i)}function vv(r,l=[],i=null,u=null,c=null){if(r==null){if(!i)return null;if(i.errors)r=i.matches;else if(l.length===0&&!i.initialized&&i.matches.length>0)r=i.matches;else return null}let d=r,p=i==null?void 0:i.errors;if(p!=null){let k=d.findIndex(S=>S.route.id&&(p==null?void 0:p[S.route.id])!==void 0);Pe(k>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(p).join(",")}`),d=d.slice(0,Math.min(d.length,k+1))}let m=!1,v=-1;if(i)for(let k=0;k=0?d=d.slice(0,v+1):d=[d[0]];break}}}let y=i&&u?(k,S)=>{var _,j;u(k,{location:i.location,params:((j=(_=i.matches)==null?void 0:_[0])==null?void 0:j.params)??{},unstable_pattern:qh(i.matches),errorInfo:S})}:void 0;return d.reduceRight((k,S,_)=>{let j,$=!1,N=null,T=null;i&&(j=p&&S.route.id?p[S.route.id]:void 0,N=S.route.errorElement||pv,m&&(v<0&&_===0?(gf("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),$=!0,T=null):v===_&&($=!0,T=S.route.hydrateFallbackElement||null)));let b=l.concat(d.slice(0,_+1)),U=()=>{let A;return j?A=N:$?A=T:S.route.Component?A=h.createElement(S.route.Component,null):S.route.element?A=S.route.element:A=k,h.createElement(hv,{match:S,routeContext:{outlet:k,matches:b,isDataRoute:i!=null},children:A})};return i&&(S.route.ErrorBoundary||S.route.errorElement||_===0)?h.createElement(vf,{location:i.location,revalidation:i.revalidation,component:N,error:j,children:U(),routeContext:{outlet:null,matches:b,isDataRoute:!0},onError:y}):U()},null)}function cs(r){return`${r} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function gv(r){let l=h.useContext(gr);return Pe(l,cs(r)),l}function yv(r){let l=h.useContext(Fl);return Pe(l,cs(r)),l}function wv(r){let l=h.useContext(Kt);return Pe(l,cs(r)),l}function ds(r){let l=wv(r),i=l.matches[l.matches.length-1];return Pe(i.route.id,`${r} can only be used on routes that contain a unique "id"`),i.route.id}function xv(){return ds("useRouteId")}function kv(){var u;let r=h.useContext(us),l=yv("useRouteError"),i=ds("useRouteError");return r!==void 0?r:(u=l.errors)==null?void 0:u[i]}function Sv(){let{router:r}=gv("useNavigate"),l=ds("useNavigate"),i=h.useRef(!1);return mf(()=>{i.current=!0}),h.useCallback(async(c,d={})=>{jt(i.current,pf),i.current&&(typeof c=="number"?await r.navigate(c):await r.navigate(c,{fromRouteId:l,...d}))},[r,l])}var Pd={};function gf(r,l,i){!l&&!Pd[r]&&(Pd[r]=!0,jt(!1,i))}h.memo(Ev);function Ev({routes:r,future:l,state:i,onError:u}){return hf(r,void 0,i,u,l)}function mr(r){Pe(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Cv({basename:r="/",children:l=null,location:i,navigationType:u="POP",navigator:c,static:d=!1,unstable_useTransitions:p}){Pe(!fo(),"You cannot render a inside another . You should never have more than one in your app.");let m=r.replace(/^\/*/,"/"),v=h.useMemo(()=>({basename:m,navigator:c,static:d,unstable_useTransitions:p,future:{}}),[m,c,d,p]);typeof i=="string"&&(i=vr(i));let{pathname:y="/",search:k="",hash:S="",state:_=null,key:j="default"}=i,$=h.useMemo(()=>{let N=Qt(y,m);return N==null?null:{location:{pathname:N,search:k,hash:S,state:_,key:j},navigationType:u}},[m,y,k,S,_,j,u]);return jt($!=null,` is not able to match the URL "${y}${k}${S}" because it does not start with the basename, so the won't render anything.`),$==null?null:h.createElement(vt.Provider,{value:v},h.createElement(co.Provider,{children:l,value:$}))}function Rv({children:r,location:l}){return dv(rs(r),l)}function rs(r,l=[]){let i=[];return h.Children.forEach(r,(u,c)=>{if(!h.isValidElement(u))return;let d=[...l,c];if(u.type===h.Fragment){i.push.apply(i,rs(u.props.children,d));return}Pe(u.type===mr,`[${typeof u.type=="string"?u.type:u.type.name}] is not a component. All component children of must be a or `),Pe(!u.props.index||!u.props.children,"An index route cannot have child routes.");let p={id:u.props.id||d.join("-"),caseSensitive:u.props.caseSensitive,element:u.props.element,Component:u.props.Component,index:u.props.index,path:u.props.path,middleware:u.props.middleware,loader:u.props.loader,action:u.props.action,hydrateFallbackElement:u.props.hydrateFallbackElement,HydrateFallback:u.props.HydrateFallback,errorElement:u.props.errorElement,ErrorBoundary:u.props.ErrorBoundary,hasErrorBoundary:u.props.hasErrorBoundary===!0||u.props.ErrorBoundary!=null||u.props.errorElement!=null,shouldRevalidate:u.props.shouldRevalidate,handle:u.props.handle,lazy:u.props.lazy};u.props.children&&(p.children=rs(u.props.children,d)),i.push(p)}),i}var jl="get",zl="application/x-www-form-urlencoded";function $l(r){return typeof HTMLElement<"u"&&r instanceof HTMLElement}function Nv(r){return $l(r)&&r.tagName.toLowerCase()==="button"}function Pv(r){return $l(r)&&r.tagName.toLowerCase()==="form"}function _v(r){return $l(r)&&r.tagName.toLowerCase()==="input"}function Lv(r){return!!(r.metaKey||r.altKey||r.ctrlKey||r.shiftKey)}function Tv(r,l){return r.button===0&&(!l||l==="_self")&&!Lv(r)}var El=null;function Ov(){if(El===null)try{new FormData(document.createElement("form"),0),El=!1}catch{El=!0}return El}var jv=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Ba(r){return r!=null&&!jv.has(r)?(jt(!1,`"${r}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${zl}"`),null):r}function zv(r,l){let i,u,c,d,p;if(Pv(r)){let m=r.getAttribute("action");u=m?Qt(m,l):null,i=r.getAttribute("method")||jl,c=Ba(r.getAttribute("enctype"))||zl,d=new FormData(r)}else if(Nv(r)||_v(r)&&(r.type==="submit"||r.type==="image")){let m=r.form;if(m==null)throw new Error('Cannot submit a + + + + + ); +}; + +export default AuthModal; diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx new file mode 100644 index 000000000..212241260 --- /dev/null +++ b/frontend/src/components/Header.tsx @@ -0,0 +1,57 @@ +import { Link } from "react-router-dom"; +import { Button } from "./ui/button"; +import { Vote, LayoutDashboard, PlusCircle, LogIn, LogOut } from "lucide-react"; +import { useAuth } from "../context/AuthContext"; +import { useState } from "react"; +import AuthModal from "./AuthModal"; + +const Header = () => { + const { user, logout } = useAuth(); + const [showAuth, setShowAuth] = useState(false); + + return ( +
+
+ + + DesignVote + + + +
+ + setShowAuth(false)} /> +
+ ); +}; + +export default Header; diff --git a/frontend/src/components/ui/avatar.tsx b/frontend/src/components/ui/avatar.tsx new file mode 100644 index 000000000..1ac15704f --- /dev/null +++ b/frontend/src/components/ui/avatar.tsx @@ -0,0 +1,109 @@ +"use client" + +import * as React from "react" +import { Avatar as AvatarPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" | "lg" +}) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...props} + /> + ) +} + +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AvatarGroupCount({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className + )} + {...props} + /> + ) +} + +export { + Avatar, + AvatarImage, + AvatarFallback, + AvatarBadge, + AvatarGroup, + AvatarGroupCount, +} diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx new file mode 100644 index 000000000..beb56ed1c --- /dev/null +++ b/frontend/src/components/ui/badge.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/frontend/src/components/ui/button.stories.tsx b/frontend/src/components/ui/button.stories.tsx new file mode 100644 index 000000000..49c7b1747 --- /dev/null +++ b/frontend/src/components/ui/button.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Button } from "./button"; +import { Vote, PlusCircle, Trash2 } from "lucide-react"; + +const meta: Meta = { + title: "UI/Button", + component: Button, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { children: "Klicka här" }, +}; + +export const Outline: Story = { + args: { variant: "outline", children: "Outline" }, +}; + +export const Destructive: Story = { + args: { variant: "destructive", children: "Ta bort" }, +}; + +export const WithIcon: Story = { + render: () => ( +
+ + + +
+ ), +}; + +export const Sizes: Story = { + render: () => ( +
+ + + +
+ ), +}; diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx new file mode 100644 index 000000000..b5ea4abd1 --- /dev/null +++ b/frontend/src/components/ui/button.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot.Root : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/frontend/src/components/ui/card.stories.tsx b/frontend/src/components/ui/card.stories.tsx new file mode 100644 index 000000000..bd6d41b50 --- /dev/null +++ b/frontend/src/components/ui/card.stories.tsx @@ -0,0 +1,45 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./card"; +import { Badge } from "./badge"; +import { Button } from "./button"; + +const meta: Meta = { + title: "UI/Card", + component: Card, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const PollCard: Story = { + render: () => ( + + +
+ Vilken logo är bäst? + published +
+ Vi ska välja ny logo +
+ +

3 alternativ | 10 röster

+
+ + + + +
+ ), +}; + +export const EmptyState: Story = { + render: () => ( + + +

Inga polls ännu

+ +
+
+ ), +}; diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx new file mode 100644 index 000000000..681ad980f --- /dev/null +++ b/frontend/src/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx new file mode 100644 index 000000000..80d7ad604 --- /dev/null +++ b/frontend/src/components/ui/dialog.tsx @@ -0,0 +1,158 @@ +"use client" + +import * as React from "react" +import { XIcon } from "lucide-react" +import { Dialog as DialogPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean +}) { + return ( +
+ {children} + {showCloseButton && ( + + + + )} +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/frontend/src/components/ui/dropdown-menu.tsx b/frontend/src/components/ui/dropdown-menu.tsx new file mode 100644 index 000000000..ac026d1eb --- /dev/null +++ b/frontend/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,255 @@ +import * as React from "react" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" +import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/frontend/src/components/ui/input.stories.tsx b/frontend/src/components/ui/input.stories.tsx new file mode 100644 index 000000000..b6277163d --- /dev/null +++ b/frontend/src/components/ui/input.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Input } from "./input"; +import { Label } from "./label"; + +const meta: Meta = { + title: "UI/Input", + component: Input, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { placeholder: "Skriv något..." }, +}; + +export const WithLabel: Story = { + render: () => ( +
+ + +
+ ), +}; + +export const Password: Story = { + render: () => ( +
+ + +
+ ), +}; diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx new file mode 100644 index 000000000..89169058d --- /dev/null +++ b/frontend/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/frontend/src/components/ui/label.tsx b/frontend/src/components/ui/label.tsx new file mode 100644 index 000000000..f752f82ba --- /dev/null +++ b/frontend/src/components/ui/label.tsx @@ -0,0 +1,22 @@ +import * as React from "react" +import { Label as LabelPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Label } diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx new file mode 100644 index 000000000..bca13feb4 --- /dev/null +++ b/frontend/src/components/ui/progress.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import { Progress as ProgressPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function Progress({ + className, + value, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { Progress } diff --git a/frontend/src/components/ui/skeleton.tsx b/frontend/src/components/ui/skeleton.tsx new file mode 100644 index 000000000..32ea0ef7f --- /dev/null +++ b/frontend/src/components/ui/skeleton.tsx @@ -0,0 +1,13 @@ +import { cn } from "@/lib/utils" + +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Skeleton } diff --git a/frontend/src/components/ui/sonner.tsx b/frontend/src/components/ui/sonner.tsx new file mode 100644 index 000000000..9f46e06d5 --- /dev/null +++ b/frontend/src/components/ui/sonner.tsx @@ -0,0 +1,38 @@ +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon, +} from "lucide-react" +import { useTheme } from "next-themes" +import { Toaster as Sonner, type ToasterProps } from "sonner" + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + , + info: , + warning: , + error: , + loading: , + }} + style={ + { + "--normal-bg": "var(--popover)", + "--normal-text": "var(--popover-foreground)", + "--normal-border": "var(--border)", + "--border-radius": "var(--radius)", + } as React.CSSProperties + } + {...props} + /> + ) +} + +export { Toaster } diff --git a/frontend/src/components/ui/tabs.tsx b/frontend/src/components/ui/tabs.tsx new file mode 100644 index 000000000..7bf18aa7e --- /dev/null +++ b/frontend/src/components/ui/tabs.tsx @@ -0,0 +1,89 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Tabs as TabsPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function Tabs({ + className, + orientation = "horizontal", + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const tabsListVariants = cva( + "rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function TabsList({ + className, + variant = "default", + ...props +}: React.ComponentProps & + VariantProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } diff --git a/frontend/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx new file mode 100644 index 000000000..7f21b5e78 --- /dev/null +++ b/frontend/src/components/ui/textarea.tsx @@ -0,0 +1,18 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { + return ( +