diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 9def360..d29df9d 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -13,12 +13,15 @@
"@hey-api/openapi-ts": "^0.99.0",
"@supabase/supabase-js": "^2.110.8",
"@tailwindcss/vite": "^4.2.2",
+ "@tanstack/react-table": "^8.21.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^4.4.0",
"lucide-react": "^1.8.0",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-circle-flags": "^0.0.29",
+ "react-day-picker": "^10.0.1",
"react-dom": "^19.2.4",
"react-router-dom": "7.18.2",
"shadcn": "^4.2.0",
@@ -522,6 +525,12 @@
}
}
},
+ "node_modules/@date-fns/tz": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz",
+ "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==",
+ "license": "MIT"
+ },
"node_modules/@dotenvx/dotenvx": {
"version": "1.75.1",
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.75.1.tgz",
@@ -3601,6 +3610,39 @@
"vite": "^5.2.0 || ^6 || ^7 || ^8"
}
},
+ "node_modules/@tanstack/react-table": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
+ "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/table-core": "8.21.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/@tanstack/table-core": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
+ "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@ts-morph/common": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz",
@@ -3612,6 +3654,42 @@
"path-browserify": "^1.0.1"
}
},
+ "node_modules/@ts-morph/common/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@ts-morph/common/node_modules/brace-expansion": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@ts-morph/common/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
@@ -3859,6 +3937,42 @@
"typescript": ">=4.8.4 <6.1.0"
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
@@ -4645,6 +4759,16 @@
"devOptional": true,
"license": "MIT"
},
+ "node_modules/date-fns": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz",
+ "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/debounce-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz",
@@ -5363,9 +5487,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
- "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
+ "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"funding": [
{
"type": "github",
@@ -7510,6 +7634,32 @@
"react": "^16.0.0 || 17.x || 18.x || 19.x"
}
},
+ "node_modules/react-day-picker": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-10.0.1.tgz",
+ "integrity": "sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@date-fns/tz": "^1.4.1",
+ "date-fns": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/gpbl"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-dom": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 5e8f0a5..bcf7111 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -19,12 +19,15 @@
"@hey-api/openapi-ts": "^0.99.0",
"@supabase/supabase-js": "^2.110.8",
"@tailwindcss/vite": "^4.2.2",
+ "@tanstack/react-table": "^8.21.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^4.4.0",
"lucide-react": "^1.8.0",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-circle-flags": "^0.0.29",
+ "react-day-picker": "^10.0.1",
"react-dom": "^19.2.4",
"react-router-dom": "7.18.2",
"shadcn": "^4.2.0",
diff --git a/frontend/public/Images/auth/SignupSideImage.png b/frontend/public/Images/auth/SignupSideImage.png
new file mode 100644
index 0000000..fcf1543
Binary files /dev/null and b/frontend/public/Images/auth/SignupSideImage.png differ
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 37c3cff..40e66a3 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -4,9 +4,31 @@ import { useAuth } from '@/context/AuthContext';
import SessionPage from './pages/Session';
import LoginPage from './pages/Login_page';
import Home from './pages/Home';
-import CreateCommittee from './pages/CreateCommittee';
-import UserDash from './pages/UserDashboard';
import Pricing from './pages/Info Pages/Pricing';
+import OurTeam from './pages/Info Pages/OurTeam';
+import ContactUs from './pages/Info Pages/Contact-us';
+import SignupPage from './pages/Signup';
+import UserDash from './pages/UserDashboard';
+import DashHome from './components/userDashboard/dash-home.tsx';
+import CreateCommittee from './pages/CreateCommittee';
+import BasicInfo from './components/userDashboard/conferenceMenus/about/conference-basic-info.tsx';
+
+
+import ConferenceOverview from './components/userDashboard/conferenceMenus/conference-overview';
+import Enrollment from './components/userDashboard/conferenceMenus/participants/enrollment';
+import ParticipantListAllocation from './components/userDashboard/conferenceMenus/participants/list-allocation';
+import PresenceCertificates from './components/userDashboard/conferenceMenus/participants/presence-certificates';
+import ConferenceSchedule from './components/userDashboard/conferenceMenus/about/conference-schedule';
+import TeamStructure from './components/userDashboard/conferenceMenus/team/team-structure';
+import TeamAllocate from './components/userDashboard/conferenceMenus/team/team-alocate';
+import CommitteeManagement from './components/userDashboard/conferenceMenus/committee-managemant';
+import ConferenceDocs from './components/userDashboard/conferenceMenus/about/conference-docs';
+import ManagePermsRoles from './components/userDashboard/conferenceMenus/team/manage-perms-roles.tsx';
+import CommitteeSessions from './components/userDashboard/committeeMenus/committee-sessions.tsx';
+import CommitteeDocs from './components/userDashboard/committeeMenus/committee-docs.tsx';
+import CommitteeInfo from './components/userDashboard/committeeMenus/committee-info.tsx';
+
+
function RequireAuth({ children }: { children: ReactNode }) {
const { loading, token } = useAuth();
@@ -28,10 +50,29 @@ function App() {
element={ }
/>
} />
- } />
+ } />
+ } />
} />
+ } />
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
} />
- } />
+ } />
diff --git a/frontend/src/components/auth/login-form.tsx b/frontend/src/components/auth/login-form.tsx
index 98e7e99..88b4e12 100644
--- a/frontend/src/components/auth/login-form.tsx
+++ b/frontend/src/components/auth/login-form.tsx
@@ -10,6 +10,7 @@ import {
FieldSeparator,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
+import { Link } from "react-router-dom"
import { supabase } from "@/lib/supabaseClient"
export function LoginForm({
@@ -114,9 +115,9 @@ export function LoginForm({
Don't have an account?{" "}
-
+
Sign up
-
+
diff --git a/frontend/src/components/auth/signup-form.tsx b/frontend/src/components/auth/signup-form.tsx
new file mode 100644
index 0000000..d7078a0
--- /dev/null
+++ b/frontend/src/components/auth/signup-form.tsx
@@ -0,0 +1,93 @@
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/components/ui/field"
+import { Input } from "@/components/ui/input"
+import { Link } from "react-router-dom"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/frontend/src/components/homepage/footer.tsx b/frontend/src/components/homepage/footer.tsx
index e9fb3db..2a0de37 100644
--- a/frontend/src/components/homepage/footer.tsx
+++ b/frontend/src/components/homepage/footer.tsx
@@ -1,28 +1,46 @@
-
-
import { Link } from "react-router-dom"
+import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
const footerLinkGroups = [
{
title: "Sobre Nós",
- items: ["Equipe", "Nossa História", "Imprensa"],
+ items: [
+ { label: "Equipe", to: "/our-team" },
+ { label: "Nossa História", to: "/our-team" },
+ { label: "Imprensa", to: "/contact-us" },
+ ],
},
{
title: "O Produto",
- items: ["Documentação", "Preços", "Changelog", "Repositório GitHub"],
+ items: [
+ { label: "Documentação", to: "/dashboard" },
+ { label: "Preços", to: "/pricing" },
+ { label: "Changelog", to: "/dashboard" },
+ { label: "Repositório GitHub", href: "https://github.com" },
+ ],
},
{
title: "Contato",
- items: ["Duvidas", "Suporte", "Seja nosso parceiro"],
+ items: [
+ { label: "Duvidas", to: "/contact-us" },
+ { label: "Suporte", to: "/contact-us" },
+ { label: "Seja nosso parceiro", to: "/contact-us" },
+ ],
},
{
title: "Redes Sociais",
- items: ["Instagram", "Twitter", "LinkedIn", "TikTok", "YouTube"],
+ items: [
+ { label: "Instagram", href: "https://instagram.com" },
+ { label: "Twitter", href: "https://x.com" },
+ { label: "LinkedIn", href: "https://linkedin.com" },
+ { label: "TikTok", href: "https://tiktok.com" },
+ { label: "YouTube", href: "https://youtube.com" },
+ ],
},
]
-const placeholderLinkClassName = "cursor-pointer transition hover:underline"
+const LinkClassName = "cursor-pointer transition hover:underline"
export default function Footer() {
@@ -38,14 +56,18 @@ export default function Footer() {
{group.title}
{group.items.map((item) => (
-
- event.preventDefault()}
- className={placeholderLinkClassName}
- >
- {item}
-
+
+
+ {item.href ? (
+
+ {item.label}
+
+ ) : (
+
+ {item.label}
+
+ )}
+
))}
@@ -55,9 +77,21 @@ export default function Footer() {
- event.preventDefault()} className={placeholderLinkClassName}>Política de Privacidade
- event.preventDefault()} className={placeholderLinkClassName}>Termos de Serviço
- event.preventDefault()} className={placeholderLinkClassName}>Cookies
+
+
+ Política de Privacidade
+
+
+
+
+ Termos de Serviço
+
+
+
+
+ Cookies
+
+
diff --git a/frontend/src/components/homepage/navbar.tsx b/frontend/src/components/homepage/navbar.tsx
index f2b3831..3e06498 100644
--- a/frontend/src/components/homepage/navbar.tsx
+++ b/frontend/src/components/homepage/navbar.tsx
@@ -78,6 +78,9 @@ export default function Navbar() {
Conheça os termos de uso do WebMun e nossa política de privacidade.
+
+ Tem dúvidas ou sugestões? Entre em contato conosco!
+
@@ -85,7 +88,7 @@ export default function Navbar() {
Sobre
-
+
Conheça a equipe por trás do WebMun!
@@ -105,12 +108,14 @@ export default function Navbar() {
- (window.location.href = "/create-committee")}>
- TEST CREATE COMMITTEE
+
+ TEST CREATE COMMITTEE
+
+
+ Criar Conta
- Criar Conta
- (window.location.href = "/login")}>
- Login
+
+ Login
diff --git a/frontend/src/components/ui/calendar.tsx b/frontend/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..ead1c58
--- /dev/null
+++ b/frontend/src/components/ui/calendar.tsx
@@ -0,0 +1,220 @@
+import * as React from "react"
+import {
+ DayPicker,
+ getDefaultClassNames,
+ type DayButton,
+ type Locale,
+} from "react-day-picker"
+
+import { cn } from "@/lib/utils"
+import { Button, buttonVariants } from "@/components/ui/button"
+import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react"
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ locale,
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"]
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ locale={locale}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString(locale?.code, { month: "short" }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "relative flex flex-col gap-4 md:flex-row",
+ defaultClassNames.months
+ ),
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
+ nav: cn(
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ "relative rounded-(--cell-radius)",
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn(
+ "absolute inset-0 bg-popover opacity-0",
+ defaultClassNames.dropdown
+ ),
+ caption_label: cn(
+ "font-medium select-none",
+ captionLayout === "label"
+ ? "text-sm"
+ : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
+ defaultClassNames.caption_label
+ ),
+ month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
+ defaultClassNames.weekday
+ ),
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
+ week_number_header: cn(
+ "w-(--cell-size) select-none",
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ "text-[0.8rem] text-muted-foreground select-none",
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
+ props.showWeekNumber
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
+ : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ "relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_start
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn(
+ "relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_end
+ ),
+ today: cn(
+ "rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
+ defaultClassNames.today
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ )
+ }
+
+ if (orientation === "right") {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: ({ ...props }) => (
+
+ ),
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ locale,
+ ...props
+}: React.ComponentProps & { locale?: Partial }) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+ span]:text-xs [&>span]:opacity-70",
+ defaultClassNames.day,
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+export { Calendar, CalendarDayButton }
diff --git a/frontend/src/components/ui/popover.tsx b/frontend/src/components/ui/popover.tsx
new file mode 100644
index 0000000..0ad11df
--- /dev/null
+++ b/frontend/src/components/ui/popover.tsx
@@ -0,0 +1,87 @@
+import * as React from "react"
+import { Popover as PopoverPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Popover({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverContent({
+ className,
+ align = "center",
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function PopoverAnchor({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
+ return (
+
+ )
+}
+
+function PopoverDescription({
+ className,
+ ...props
+}: React.ComponentProps<"p">) {
+ return (
+
+ )
+}
+
+export {
+ Popover,
+ PopoverAnchor,
+ PopoverContent,
+ PopoverDescription,
+ PopoverHeader,
+ PopoverTitle,
+ PopoverTrigger,
+}
diff --git a/frontend/src/components/ui/table.tsx b/frontend/src/components/ui/table.tsx
new file mode 100644
index 0000000..ac9585e
--- /dev/null
+++ b/frontend/src/components/ui/table.tsx
@@ -0,0 +1,114 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Table({ className, ...props }: React.ComponentProps<"table">) {
+ return (
+
+ )
+}
+
+function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
+ return (
+
+ )
+}
+
+function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
+ return (
+
+ )
+}
+
+function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
+ return (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ )
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+
+ )
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}: React.ComponentProps<"caption">) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/frontend/src/components/userDashboard/UserDash-sidebar.tsx b/frontend/src/components/userDashboard/UserDash-sidebar.tsx
new file mode 100644
index 0000000..c9f770f
--- /dev/null
+++ b/frontend/src/components/userDashboard/UserDash-sidebar.tsx
@@ -0,0 +1,45 @@
+"use client"
+
+import * as React from "react"
+
+import { ConferenceMenus } from "@/components/userDashboard/conference-menus-list"
+import { CommitteeMenus } from "@/components/userDashboard/committee-menus-list"
+import { NavUser } from "@/components/userDashboard/nav-user"
+import { MUNSwitcher } from "@/components/userDashboard/mun-switcher"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarRail,
+} from "@/components/ui/sidebar"
+
+
+
+// This is sample data.
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/app-sidebar.tsx b/frontend/src/components/userDashboard/app-sidebar.tsx
deleted file mode 100644
index 7c5aa85..0000000
--- a/frontend/src/components/userDashboard/app-sidebar.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-"use client"
-
-import * as React from "react"
-
-import { NavMain } from "@/components/userDashboard/nav-main"
-import { NavProjects } from "@/components/userDashboard/nav-projects"
-import { NavUser } from "@/components/userDashboard/nav-user"
-import { TeamSwitcher } from "@/components/userDashboard/team-switcher"
-import {
- Sidebar,
- SidebarContent,
- SidebarFooter,
- SidebarHeader,
- SidebarRail,
-} from "@/components/ui/sidebar"
-import { GalleryVerticalEndIcon, AudioLinesIcon, TerminalIcon, TerminalSquareIcon, BotIcon, BookOpenIcon, Settings2Icon, FrameIcon, PieChartIcon, MapIcon } from "lucide-react"
-
-// This is sample data.
-const data = {
- user: {
- name: "shadcn",
- email: "m@example.com",
- avatar: "/avatars/shadcn.jpg",
- },
- teams: [
- {
- name: "Acme Inc",
- logo: (
-
- ),
- plan: "Enterprise",
- },
- {
- name: "Acme Corp.",
- logo: (
-
- ),
- plan: "Startup",
- },
- {
- name: "Evil Corp.",
- logo: (
-
- ),
- plan: "Free",
- },
- ],
- navMain: [
- {
- title: "Playground",
- url: "#",
- icon: (
-
- ),
- isActive: true,
- items: [
- {
- title: "History",
- url: "#",
- },
- {
- title: "Starred",
- url: "#",
- },
- {
- title: "Settings",
- url: "#",
- },
- ],
- },
- {
- title: "Models",
- url: "#",
- icon: (
-
- ),
- items: [
- {
- title: "Genesis",
- url: "#",
- },
- {
- title: "Explorer",
- url: "#",
- },
- {
- title: "Quantum",
- url: "#",
- },
- ],
- },
- {
- title: "Documentation",
- url: "#",
- icon: (
-
- ),
- items: [
- {
- title: "Introduction",
- url: "#",
- },
- {
- title: "Get Started",
- url: "#",
- },
- {
- title: "Tutorials",
- url: "#",
- },
- {
- title: "Changelog",
- url: "#",
- },
- ],
- },
- {
- title: "Settings",
- url: "#",
- icon: (
-
- ),
- items: [
- {
- title: "General",
- url: "#",
- },
- {
- title: "Team",
- url: "#",
- },
- {
- title: "Billing",
- url: "#",
- },
- {
- title: "Limits",
- url: "#",
- },
- ],
- },
- ],
- projects: [
- {
- name: "Design Engineering",
- url: "#",
- icon: (
-
- ),
- },
- {
- name: "Sales & Marketing",
- url: "#",
- icon: (
-
- ),
- },
- {
- name: "Travel",
- url: "#",
- icon: (
-
- ),
- },
- ],
-}
-
-export function AppSidebar({ ...props }: React.ComponentProps) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/frontend/src/components/userDashboard/committee-menus-list.tsx b/frontend/src/components/userDashboard/committee-menus-list.tsx
new file mode 100644
index 0000000..63e7aa2
--- /dev/null
+++ b/frontend/src/components/userDashboard/committee-menus-list.tsx
@@ -0,0 +1,92 @@
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/components/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/components/ui/sidebar"
+import { Link, useLocation } from "react-router-dom"
+import { ChevronRightIcon, Info } from "lucide-react"
+
+
+
+
+
+export function CommitteeMenus() {
+ const { pathname } = useLocation()
+
+ return (
+
+ Comites
+
+
+
+
+
+
+
+ [Committee Name]
+
+
+
+
+
+
+
+
+
+ Informacoes
+
+
+
+
+
+
+
+ Sessao
+
+
+
+
+
+
+
+ Docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/committeeMenus/committee-docs.tsx b/frontend/src/components/userDashboard/committeeMenus/committee-docs.tsx
new file mode 100644
index 0000000..fc832ac
--- /dev/null
+++ b/frontend/src/components/userDashboard/committeeMenus/committee-docs.tsx
@@ -0,0 +1,84 @@
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {Download} from "lucide-react"
+
+
+const documents = [
+ {
+ name: "Conference Rules.pdf",
+ uploadedAt: "Aug 02, 2026 - 09:30",
+ uploadedBy: "Secretariat",
+ downloadUrl: "#",
+ },
+ {
+ name: "Delegation List.xlsx",
+ uploadedAt: "Aug 02, 2026 - 10:15",
+ uploadedBy: "Admin Team",
+ downloadUrl: "#",
+ },
+ {
+ name: "Agenda Overview.pdf",
+ uploadedAt: "Aug 02, 2026 - 11:05",
+ uploadedBy: "Chair",
+ downloadUrl: "#",
+ },
+ {
+ name: "Country Positions.docx",
+ uploadedAt: "Aug 02, 2026 - 12:40",
+ uploadedBy: "Moderator",
+ downloadUrl: "#",
+ },
+ {
+ name: "Crisis Briefing.pdf",
+ uploadedAt: "Aug 02, 2026 - 13:20",
+ uploadedBy: "Secretariat",
+ downloadUrl: "#",
+ },
+]
+export default function CommitteeDocs() {
+ return (
+
+
Documentos do Comitê
+
+ Encontre aqui os documentos importantes do seu comitê.
+
+
+
+ Os arquivos aqui disponíveis para consulta e download sao de propriedade ou licenciados para a conferência, a cópia e distribuição não são permitidas sem autorização.
+
+
+ Nome do documento
+ Data e hora de upload
+ Uploaded by
+ Download
+
+
+
+ {documents.map((document) => (
+
+ {document.name}
+ {document.uploadedAt}
+ {document.uploadedBy}
+
+
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/committeeMenus/committee-info.tsx b/frontend/src/components/userDashboard/committeeMenus/committee-info.tsx
new file mode 100644
index 0000000..a898ae6
--- /dev/null
+++ b/frontend/src/components/userDashboard/committeeMenus/committee-info.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function CommitteeInfo() {
+ return (
+
+
This is the Committee Info Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/committeeMenus/committee-sessions.tsx b/frontend/src/components/userDashboard/committeeMenus/committee-sessions.tsx
new file mode 100644
index 0000000..7a66778
--- /dev/null
+++ b/frontend/src/components/userDashboard/committeeMenus/committee-sessions.tsx
@@ -0,0 +1,96 @@
+
+import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemGroup,
+ ItemHeader,
+ ItemTitle,
+} from "@/components/ui/item"
+
+type CommitteeSession = {
+ id: string
+ sessionNumber: number
+ startTime: string
+ endTime: string
+ joinHref?: string
+}
+
+type CommitteeSessionsProps = {
+ sessions?: CommitteeSession[]
+ onJoin?: (session: CommitteeSession) => void
+}
+
+const defaultSessions: CommitteeSession[] = [
+ {
+ id: "session-1",
+ sessionNumber: 1,
+ startTime: "09:00",
+ endTime: "10:30",
+ joinHref: "/session/1",
+ },
+ {
+ id: "session-2",
+ sessionNumber: 2,
+ startTime: "11:00",
+ endTime: "12:30",
+ joinHref: "/session/2",
+ },
+ {
+ id: "session-3",
+ sessionNumber: 3,
+ startTime: "14:00",
+ endTime: "15:30",
+ joinHref: "/session/3",
+ },
+]
+
+export default function CommitteeSessions({
+ sessions = defaultSessions,
+ onJoin,
+}: CommitteeSessionsProps) {
+ return (
+
+
+
+
+ {sessions.map((session) => (
+ -
+
+
+
+ Session {session.sessionNumber}
+
+
+ Start: {session.startTime} | End: {session.endTime}
+
+
+
+
+ {session.joinHref ? (
+
+ Join
+
+ ) : (
+ onJoin?.(session)}>Join
+ )}
+
+
+
+ ))}
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conference-menus-list.tsx b/frontend/src/components/userDashboard/conference-menus-list.tsx
new file mode 100644
index 0000000..35581d6
--- /dev/null
+++ b/frontend/src/components/userDashboard/conference-menus-list.tsx
@@ -0,0 +1,217 @@
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/components/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/components/ui/sidebar"
+import { Link, useLocation } from "react-router-dom"
+import { ChevronRightIcon, LayoutDashboard, Users, Network, Info, Gavel } from "lucide-react"
+
+
+
+
+
+export function ConferenceMenus() {
+ const { pathname } = useLocation()
+
+ return (
+
+ Conferencia
+
+
+
+
+
+
+ Visao Geral
+
+
+
+
+
+
+
+
+
+ Sobre a MUN
+
+
+
+
+
+
+
+
+
+ Informações básicas
+
+
+
+
+
+
+
+ Cronograma
+
+
+
+
+
+
+
+ Docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Comites
+
+
+
+
+
+
+
+
+
+
+ Equipe
+
+
+
+
+
+
+
+
+
+ Papeis e Permissões
+
+
+
+
+
+
+
+ Adicionar e Alocar
+
+
+
+
+
+
+
+ Listagem e Organograma
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Participantes
+
+
+
+
+
+
+
+
+
+ Inscricao
+
+
+
+
+
+
+
+ Listagem e Alocação
+
+
+
+
+
+
+
+
+ Presenca e certificados
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/about/conference-basic-info.tsx b/frontend/src/components/userDashboard/conferenceMenus/about/conference-basic-info.tsx
new file mode 100644
index 0000000..649f49d
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/about/conference-basic-info.tsx
@@ -0,0 +1,212 @@
+import * as React from "react"
+import { format } from "date-fns"
+import { ptBR } from "date-fns/locale"
+import { ChevronDownIcon, ImageUp } from "lucide-react"
+
+import { Button } from "@/components/ui/button"
+import { Calendar } from "@/components/ui/calendar"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover"
+
+function isValidHexColor(value: string) {
+ return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value)
+}
+
+function normalizeHexColor(value: string) {
+ const trimmedValue = value.trim()
+
+ if (!trimmedValue) {
+ return ""
+ }
+
+ const prefixedValue = trimmedValue.startsWith("#")
+ ? trimmedValue
+ : `#${trimmedValue}`
+
+ if (/^#([0-9a-fA-F]{3})$/.test(prefixedValue)) {
+ const [, red, green, blue] = prefixedValue
+ return `#${red}${red}${green}${green}${blue}${blue}`.toLowerCase()
+ }
+
+ return prefixedValue.toLowerCase()
+}
+
+function isPreviewableHexColor(value: string) {
+ return /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value)
+}
+
+function DatePickerField({
+ label,
+ placeholder,
+ date,
+ onDateChange,
+}: {
+ label: string
+ placeholder: string
+ date?: Date
+ onDateChange: (date?: Date) => void
+}) {
+ return (
+
+
{label}
+
+
+
+ {date ? format(date, "PPP", { locale: ptBR }) : {placeholder} }
+
+
+
+
+
+
+
+
+ )
+}
+//TODO: Build an actual colour picker
+function ColorPickerField({
+ label,
+ color,
+ onColorChange,
+}: {
+ label: string
+ color: string
+ onColorChange: (color: string) => void
+}) {
+ const [draftColor, setDraftColor] = React.useState(color)
+
+ return (
+
+
{label}
+
+
+
+
+ {
+ const nextColor = event.target.value
+ setDraftColor(nextColor)
+
+ const normalizedColor = normalizeHexColor(nextColor)
+ if (isValidHexColor(normalizedColor)) {
+ onColorChange(normalizedColor)
+ }
+ }}
+ onBlur={() => {
+ const normalizedColor = normalizeHexColor(draftColor)
+ if (isValidHexColor(normalizedColor)) {
+ setDraftColor(normalizedColor)
+ onColorChange(normalizedColor)
+ } else {
+ setDraftColor(color)
+ }
+ }}
+ placeholder="#0f172a"
+ inputMode="text"
+ autoComplete="off"
+ className="font-mono uppercase"
+ />
+
+
+
+ A cor tema será usada em partes da interface da sua conferência, incluindo o site, materiais e comunicações.
+
+
+
+ )
+}
+
+export default function BasicInfo() {
+ const [startDate, setStartDate] = React.useState()
+ const [endDate, setEndDate] = React.useState()
+ const [themeColor, setThemeColor] = React.useState("#0f172a")
+
+ return (
+
+
Informações básicas sobre a sua MUN
+
+ Coloque aqui as informações principais da sua conferência para que os participantes possam identificar o evento com facilidade.
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/about/conference-docs.tsx b/frontend/src/components/userDashboard/conferenceMenus/about/conference-docs.tsx
new file mode 100644
index 0000000..31af2e8
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/about/conference-docs.tsx
@@ -0,0 +1,84 @@
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {Download} from "lucide-react"
+
+
+const documents = [
+ {
+ name: "Conference Rules.pdf",
+ uploadedAt: "Aug 02, 2026 - 09:30",
+ uploadedBy: "Secretariat",
+ downloadUrl: "#",
+ },
+ {
+ name: "Delegation List.xlsx",
+ uploadedAt: "Aug 02, 2026 - 10:15",
+ uploadedBy: "Admin Team",
+ downloadUrl: "#",
+ },
+ {
+ name: "Agenda Overview.pdf",
+ uploadedAt: "Aug 02, 2026 - 11:05",
+ uploadedBy: "Chair",
+ downloadUrl: "#",
+ },
+ {
+ name: "Country Positions.docx",
+ uploadedAt: "Aug 02, 2026 - 12:40",
+ uploadedBy: "Moderator",
+ downloadUrl: "#",
+ },
+ {
+ name: "Crisis Briefing.pdf",
+ uploadedAt: "Aug 02, 2026 - 13:20",
+ uploadedBy: "Secretariat",
+ downloadUrl: "#",
+ },
+]
+export default function ConferenceDocs() {
+ return (
+
+
Documentos da Conferência
+
+ Encontre aqui os documentos importantes da sua conferência como un todo.
+
+
+
+ Os arquivos aqui disponíveis para consulta e download sao de propriedade ou licenciados para a conferência, a cópia e distribuição não são permitidas sem autorização.
+
+
+ Nome do documento
+ Data e hora de upload
+ Uploaded by
+ Download
+
+
+
+ {documents.map((document) => (
+
+ {document.name}
+ {document.uploadedAt}
+ {document.uploadedBy}
+
+
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conferenceMenus/about/conference-schedule.tsx b/frontend/src/components/userDashboard/conferenceMenus/about/conference-schedule.tsx
new file mode 100644
index 0000000..1605c81
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/about/conference-schedule.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function ConferenceSchedule() {
+ return (
+
+
This is the Conference Schedule Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conferenceMenus/committee-managemant.tsx b/frontend/src/components/userDashboard/conferenceMenus/committee-managemant.tsx
new file mode 100644
index 0000000..e149e8b
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/committee-managemant.tsx
@@ -0,0 +1,594 @@
+import * as React from "react"
+import {
+ type ColumnDef,
+ type ColumnFiltersState,
+ type SortingState,
+ type VisibilityState,
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { ArrowUpDown, ChevronDown } from "lucide-react"
+
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+
+type Committee = {
+ id: string
+ name: string
+ logo: string
+ colour: string
+ acronym: string
+ type: string
+}
+
+type CommitteeDraft = Omit
+
+const committeeTypes = [
+ "Traditional",
+ "WIMUN",
+ "Consensus",
+ "Teatro de Operações",
+ "Crisis",
+ "Specialized",
+]
+
+function createLogoDataUrl(acronym: string, colour: string) {
+ const safeAcronym = acronym.trim() || "CM"
+ const safeColour = colour || "#64748b"
+
+ return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(
+ `
+
+ ${safeAcronym}
+ `
+ )}`
+}
+
+const initialCommittees: Committee[] = [
+ {
+ id: "c4c5f1d6-0f6e-41ed-a610-52e2cbb1b54a",
+ name: "Security Council",
+ logo: "SC",
+ colour: "#1d4ed8",
+ acronym: "SC",
+ type: "Traditional",
+ },
+ {
+ id: "7f0a82f5-ae0b-4f5d-9c2c-7d2f72cbda0e",
+ name: "United Nations Children’s Fund",
+ logo: "UNICEF",
+ colour: "#0f766e",
+ acronym: "UNICEF",
+ type: "WIMUN",
+ },
+ {
+ id: "86e0f1d0-6d76-4b70-86ec-061d4e8e4f57",
+ name: "World Health Organization",
+ logo: "WHO",
+ colour: "#15803d",
+ acronym: "WHO",
+ type: "Consensus",
+ },
+ {
+ id: "bb64d5ce-0d34-4e8a-8c22-5f0b4db7ff90",
+ name: "Crisis Room",
+ logo: "CR",
+ colour: "#b45309",
+ acronym: "CR",
+ type: "Teatro de Operações",
+ },
+]
+
+function CommitteeLogoPreview({ logo, colour }: { logo: string; colour: string }) {
+ const displayLabel = logo.trim() || "--"
+ const isImageLogo = logo.startsWith("data:image/") || logo.startsWith("blob:") || /^https?:\/\//.test(logo)
+
+ return (
+
+ {isImageLogo ? (
+
+ ) : (
+
+ {displayLabel}
+
+ )}
+
+ )
+}
+
+export default function CommitteeManagement() {
+ const [committees, setCommittees] = React.useState(initialCommittees)
+ const [sorting, setSorting] = React.useState([])
+ const [columnFilters, setColumnFilters] = React.useState([])
+ const [columnVisibility, setColumnVisibility] = React.useState({})
+ const [rowSelection, setRowSelection] = React.useState({})
+ const [editingCommitteeId, setEditingCommitteeId] = React.useState(null)
+ const [draftCommittee, setDraftCommittee] = React.useState(null)
+
+ const editingCommittee = React.useMemo(
+ () => committees.find((committee) => committee.id === editingCommitteeId) ?? null,
+ [committees, editingCommitteeId]
+ )
+
+ React.useEffect(() => {
+ if (!editingCommittee) {
+ setDraftCommittee(null)
+ return
+ }
+
+ setDraftCommittee({
+ name: editingCommittee.name,
+ logo: editingCommittee.logo,
+ colour: editingCommittee.colour,
+ acronym: editingCommittee.acronym,
+ type: editingCommittee.type,
+ })
+ }, [editingCommittee])
+
+ function openEditor(id: string) {
+ setEditingCommitteeId(id)
+ }
+
+ function closeEditor() {
+ setEditingCommitteeId(null)
+ }
+
+ async function handleLogoFileChange(file: File | null) {
+ if (!file) {
+ return
+ }
+
+ const nextLogo = await new Promise((resolve, reject) => {
+ const reader = new FileReader()
+
+ reader.onload = () => resolve(String(reader.result ?? ""))
+ reader.onerror = () => reject(reader.error)
+ reader.readAsDataURL(file)
+ })
+
+ setDraftCommittee((currentDraft) =>
+ currentDraft ? { ...currentDraft, logo: nextLogo } : currentDraft
+ )
+ }
+
+ function saveDraftCommittee() {
+ if (!editingCommittee || !draftCommittee) {
+ return
+ }
+
+ setCommittees((currentCommittees) =>
+ currentCommittees.map((committee) =>
+ committee.id === editingCommittee.id
+ ? {
+ ...committee,
+ ...draftCommittee,
+ logo:
+ draftCommittee.logo ||
+ createLogoDataUrl(draftCommittee.acronym, draftCommittee.colour),
+ }
+ : committee
+ )
+ )
+ closeEditor()
+ }
+
+ const columns = React.useMemo[]>(
+ () => [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Selecionar todos"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Selecionar comitê"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Nome
+
+
+ ),
+ cell: ({ row }) => {row.original.name} ,
+ },
+ {
+ accessorKey: "logo",
+ header: "Logo",
+ cell: ({ row }) => (
+
+
+ Imagem do comitê
+
+ ),
+ },
+ {
+ accessorKey: "colour",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Cor
+
+
+ ),
+ cell: ({ row }) => (
+
+
+ {row.original.colour}
+
+ ),
+ },
+ {
+ accessorKey: "acronym",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Acrônimo
+
+
+ ),
+ cell: ({ row }) => {row.original.acronym} ,
+ },
+ {
+ accessorKey: "type",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Tipo
+
+
+ ),
+ cell: ({ row }) => {row.original.type} ,
+ },
+ {
+ id: "actions",
+ header: "Ações",
+ cell: ({ row }) => (
+ openEditor(row.original.id)}>
+ Editar
+
+ ),
+ },
+ ],
+ []
+ )
+
+ // eslint-disable-next-line react-hooks/incompatible-library -- React Compiler is not enabled; TanStack Table's mutable table API is intentional.
+ const table = useReactTable({
+ data: committees,
+ columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ })
+
+ function deleteSelectedCommittees() {
+ const selectedIds = new Set(
+ table.getFilteredSelectedRowModel().rows.map((row) => row.original.id)
+ )
+
+ if (selectedIds.size === 0) {
+ return
+ }
+
+ setCommittees((currentCommittees) =>
+ currentCommittees.filter((committee) => !selectedIds.has(committee.id))
+ )
+ setRowSelection({})
+ }
+
+ const selectedCount = table.getFilteredSelectedRowModel().rows.length
+
+ return (
+
+
+
Gestão de comitês
+
+ Edite nome, logo, cor, acrônimo e tipo dos comitês da conferência.
+
+
+
+
+
+
+ table.getColumn("name")?.setFilterValue(event.target.value)
+ }
+ className="w-full md:max-w-sm"
+ />
+
+
+
+
+
+ Colunas
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => (
+ column.toggleVisibility(!!value)}
+ >
+ {column.id}
+
+ ))}
+
+
+
+
+ Excluir selecionados
+
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ Nenhum comitê encontrado.
+
+
+ )}
+
+
+
+
+
+
+ {selectedCount} de {table.getFilteredRowModel().rows.length} comitê(s) selecionado(s).
+
+
+
+ table.previousPage()}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Anterior
+
+ table.nextPage()}
+ disabled={!table.getCanNextPage()}
+ >
+ Próxima
+
+
+
+
+
+
!open && closeEditor()}>
+
+
+ Editar comitê
+
+ Atualize os atributos do comitê no formulário. O logo é enviado como arquivo de imagem.
+
+
+
+ {draftCommittee ? (
+
+
+ Nome
+
+ setDraftCommittee((currentDraft) =>
+ currentDraft ? { ...currentDraft, name: event.target.value } : currentDraft
+ )
+ }
+ />
+
+
+
+ Acrônimo
+
+ setDraftCommittee((currentDraft) =>
+ currentDraft
+ ? { ...currentDraft, acronym: event.target.value.toUpperCase() }
+ : currentDraft
+ )
+ }
+ className="uppercase"
+ />
+
+
+
+ Tipo
+
+ setDraftCommittee((currentDraft) =>
+ currentDraft ? { ...currentDraft, type: value } : currentDraft
+ )
+ }
+ >
+
+
+
+
+ {committeeTypes.map((type) => (
+
+ {type}
+
+ ))}
+
+
+
+
+
+ Cor
+
+ setDraftCommittee((currentDraft) =>
+ currentDraft ? { ...currentDraft, colour: event.target.value } : currentDraft
+ )
+ }
+ className="h-10 w-full p-1"
+ />
+
+
+
+
Logo
+
+
+
+
{
+ void handleLogoFileChange(event.target.files?.[0] ?? null)
+ }}
+ />
+
+ Se você escolher um novo arquivo, ele substituirá o logo atual.
+
+
+
+
+
+ ) : null}
+
+
+
+ Cancelar
+
+ Salvar alterações
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/conference-overview.tsx b/frontend/src/components/userDashboard/conferenceMenus/conference-overview.tsx
new file mode 100644
index 0000000..6cf708c
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/conference-overview.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function ConferenceOverview() {
+ return (
+
+
This is the Conference Overview Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conferenceMenus/participants/enrollment.tsx b/frontend/src/components/userDashboard/conferenceMenus/participants/enrollment.tsx
new file mode 100644
index 0000000..61568c4
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/participants/enrollment.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function Enrollment() {
+ return (
+
+
This is the Enrollment Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conferenceMenus/participants/list-allocation.tsx b/frontend/src/components/userDashboard/conferenceMenus/participants/list-allocation.tsx
new file mode 100644
index 0000000..39e0782
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/participants/list-allocation.tsx
@@ -0,0 +1,396 @@
+import * as React from "react"
+import {
+ type ColumnDef,
+ type ColumnFiltersState,
+ type SortingState,
+ type VisibilityState,
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { ArrowUpDown, ChevronDown } from "lucide-react"
+
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { Input } from "@/components/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+
+type Participant = {
+ id: string
+ name: string
+ email: string
+ committee: string
+ representation: string
+}
+
+const committeeOptions = [
+ "Security Council",
+ "UNICEF",
+ "UNHRC",
+ "WHO",
+ "ECOSOC",
+]
+
+const representationOptions = [
+ "Brazil",
+ "United States",
+ "France",
+ "Japan",
+ "Nigeria",
+ "Mexico",
+]
+
+const initialParticipants: Participant[] = [
+ {
+ id: "33f0e8f6-0cc7-4cc8-a6ff-f4f6bca6a467",
+ name: "Ana Martins",
+ email: "ana.martins@example.com",
+ committee: "Security Council",
+ representation: "Brazil",
+ },
+ {
+ id: "88c48914-d4e2-48bf-b4f2-8d0f23f035e9",
+ name: "Diego Costa",
+ email: "diego.costa@example.com",
+ committee: "UNICEF",
+ representation: "Mexico",
+ },
+ {
+ id: "8f66643e-11c9-4ee0-adbe-906cefd86f45",
+ name: "Sara Thompson",
+ email: "sara.thompson@example.com",
+ committee: "WHO",
+ representation: "United States",
+ },
+ {
+ id: "fd177bd7-1cbc-454b-b7a6-041566a73ed3",
+ name: "Kenji Sato",
+ email: "kenji.sato@example.com",
+ committee: "UNHRC",
+ representation: "Japan",
+ },
+ {
+ id: "131902c1-9c6f-4657-b46f-a6f8247693c4",
+ name: "Claire Dubois",
+ email: "claire.dubois@example.com",
+ committee: "ECOSOC",
+ representation: "France",
+ },
+]
+
+export default function ParticipantListAllocation() {
+ const [participants, setParticipants] = React.useState(initialParticipants)
+ const [sorting, setSorting] = React.useState([])
+ const [columnFilters, setColumnFilters] = React.useState([])
+ const [columnVisibility, setColumnVisibility] = React.useState({})
+ const [rowSelection, setRowSelection] = React.useState({})
+
+ const updateParticipant = React.useCallback(
+ (id: string, field: "committee" | "representation", value: string) => {
+ setParticipants((currentParticipants) =>
+ currentParticipants.map((participant) =>
+ participant.id === id ? { ...participant, [field]: value } : participant
+ )
+ )
+ },
+ []
+ )
+
+ const columns = React.useMemo[]>(
+ () => [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Selecionar todos"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Selecionar participante"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Nome
+
+
+ ),
+ },
+ {
+ accessorKey: "email",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Email
+
+
+ ),
+ },
+ {
+ accessorKey: "committee",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Comitê
+
+
+ ),
+ cell: ({ row }) => (
+ updateParticipant(row.original.id, "committee", value)}
+ >
+
+
+
+
+ {committeeOptions.map((committee) => (
+
+ {committee}
+
+ ))}
+
+
+ ),
+ },
+ {
+ accessorKey: "representation",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Representação
+
+
+ ),
+ cell: ({ row }) => (
+
+ updateParticipant(row.original.id, "representation", value)
+ }
+ >
+
+
+
+
+ {representationOptions.map((representation) => (
+
+ {representation}
+
+ ))}
+
+
+ ),
+ },
+ ],
+ [updateParticipant]
+ )
+
+ // eslint-disable-next-line react-hooks/incompatible-library -- React Compiler is not enabled; TanStack Table's mutable table API is intentional.
+ const table = useReactTable({
+ data: participants,
+ columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ })
+
+ function deleteSelectedParticipants() {
+ const selectedIds = new Set(
+ table.getFilteredSelectedRowModel().rows.map((row) => row.original.id)
+ )
+
+ if (selectedIds.size === 0) {
+ return
+ }
+
+ setParticipants((currentParticipants) =>
+ currentParticipants.filter((participant) => !selectedIds.has(participant.id))
+ )
+ setRowSelection({})
+ }
+
+ const selectedCount = table.getFilteredSelectedRowModel().rows.length
+
+ return (
+
+
+
Listagem e alocação de participantes
+
+ Visualize participantes, pesquise por nome e ajuste comitê e representação.
+
+
+
+
+
+
+ table.getColumn("name")?.setFilterValue(event.target.value)
+ }
+ className="w-full md:max-w-sm"
+ />
+
+
+
+
+
+ Colunas
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => (
+ column.toggleVisibility(!!value)}
+ >
+ {column.id}
+
+ ))}
+
+
+
+
+ Excluir selecionados
+
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ Nenhum participante encontrado.
+
+
+ )}
+
+
+
+
+
+
+ {selectedCount} de {table.getFilteredRowModel().rows.length} participante(s) selecionado(s).
+
+
+
+ table.previousPage()}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Anterior
+
+ table.nextPage()}
+ disabled={!table.getCanNextPage()}
+ >
+ Próxima
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/participants/presence-certificates.tsx b/frontend/src/components/userDashboard/conferenceMenus/participants/presence-certificates.tsx
new file mode 100644
index 0000000..18d3fa6
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/participants/presence-certificates.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function PresenceCertificates() {
+ return (
+
+
This is the Presence Certificates Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/conferenceMenus/team/manage-perms-roles.tsx b/frontend/src/components/userDashboard/conferenceMenus/team/manage-perms-roles.tsx
new file mode 100644
index 0000000..f53d207
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/team/manage-perms-roles.tsx
@@ -0,0 +1,378 @@
+import * as React from "react"
+import {
+ type ColumnDef,
+ type ColumnFiltersState,
+ type SortingState,
+ type VisibilityState,
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { ArrowUpDown, ChevronDown } from "lucide-react"
+
+import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { Input } from "@/components/ui/input"
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
+
+type RoleRecord = {
+ id: string
+ name: string
+ description: string
+ permissions: string[]
+}
+
+const permissionOptions = [
+ "View dashboard",
+ "Manage participants",
+ "Manage teams",
+ "Manage committees",
+ "Manage docs",
+ "Manage session",
+]
+
+const initialRoles: RoleRecord[] = [
+ {
+ id: "role-1",
+ name: "Secretary General",
+ description: "Top-level conference administration and oversight.",
+ permissions: [
+ "View dashboard",
+ "Manage participants",
+ "Manage teams",
+ "Manage committees",
+ "Manage docs",
+ "Manage session",
+ ],
+ },
+ {
+ id: "role-2",
+ name: "Director",
+ description: "Committee leadership and operational setup.",
+ permissions: ["View dashboard", "Manage participants", "Manage committees", "Manage session"],
+ },
+ {
+ id: "role-3",
+ name: "Moderator",
+ description: "Debate moderation and speaking flow support.",
+ permissions: ["View dashboard", "Manage session"],
+ },
+ {
+ id: "role-4",
+ name: "Press Team",
+ description: "Media coverage, announcements, and internal reporting.",
+ permissions: ["View dashboard", "Manage docs"],
+ },
+]
+
+function PermissionBadges({ permissions }: { permissions: string[] }) {
+ return (
+
+ {permissions.map((permission) => (
+
+ {permission}
+
+ ))}
+
+ )
+}
+
+function PermissionSelector({
+ permissions,
+ onChange,
+}: {
+ permissions: string[]
+ onChange: (permissions: string[]) => void
+}) {
+ const [open, setOpen] = React.useState(false)
+ const [query, setQuery] = React.useState("")
+
+ const visiblePermissions = React.useMemo(
+ () =>
+ permissionOptions.filter((permission) =>
+ permission.toLowerCase().includes(query.trim().toLowerCase())
+ ),
+ [query]
+ )
+
+ function togglePermission(permission: string) {
+ const nextPermissions = permissions.includes(permission)
+ ? permissions.filter((item) => item !== permission)
+ : [...permissions, permission]
+
+ onChange(nextPermissions)
+ }
+
+ return (
+
+
+
+
+ {permissions.length > 0 ? `${permissions.length} permission(s) selected` : "Select permissions"}
+
+
+
+
+
+
+ setQuery(event.target.value)}
+ placeholder="Search permissions..."
+ />
+
+
+ {visiblePermissions.length > 0 ? (
+
+ {visiblePermissions.map((permission) => {
+ const isSelected = permissions.includes(permission)
+
+ return (
+ togglePermission(permission)}
+ className="flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm hover:bg-muted"
+ >
+
+ {permission}
+
+ )
+ })}
+
+ ) : (
+
No permissions found.
+ )}
+
+
+
+ )
+}
+
+export default function ManagePermsRoles() {
+ const [roles, setRoles] = React.useState(initialRoles)
+ const [sorting, setSorting] = React.useState([])
+ const [columnFilters, setColumnFilters] = React.useState([])
+ const [columnVisibility, setColumnVisibility] = React.useState({})
+ const [rowSelection, setRowSelection] = React.useState({})
+
+ const updatePermissions = React.useCallback((id: string, permissions: string[]) => {
+ setRoles((currentRoles) =>
+ currentRoles.map((role) => (role.id === id ? { ...role, permissions } : role))
+ )
+ }, [])
+
+ const columns = React.useMemo[]>(
+ () => [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Select row"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc") }>
+ Role
+
+
+ ),
+ cell: ({ row }) => {row.original.name} ,
+ },
+ {
+ accessorKey: "description",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc") }>
+ Description
+
+
+ ),
+ cell: ({ row }) => {row.original.description} ,
+ },
+ {
+ accessorKey: "permissions",
+ header: "Permissions",
+ cell: ({ row }) => (
+
+
updatePermissions(row.original.id, nextPermissions)}
+ />
+
+
+ ),
+ },
+ ],
+ [updatePermissions]
+ )
+
+ // eslint-disable-next-line react-hooks/incompatible-library -- React Compiler is not enabled; TanStack Table's mutable table API is intentional.
+ const table = useReactTable({
+ data: roles,
+ columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ })
+
+ function deleteSelectedRoles() {
+ const selectedIds = new Set(
+ table.getFilteredSelectedRowModel().rows.map((row) => row.original.id)
+ )
+
+ if (selectedIds.size === 0) {
+ return
+ }
+
+ setRoles((currentRoles) => currentRoles.filter((role) => !selectedIds.has(role.id)))
+ setRowSelection({})
+ }
+
+ const selectedCount = table.getFilteredSelectedRowModel().rows.length
+
+ return (
+
+
+
Roles and permissions
+
+ Review roles and toggle the permissions granted to each role.
+
+
+
+
+
+
table.getColumn("name")?.setFilterValue(event.target.value)}
+ className="w-full md:max-w-sm"
+ />
+
+
+
+
+
+ Columns
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => (
+ column.toggleVisibility(!!value)}
+ >
+ {column.id}
+
+ ))}
+
+
+
+
+ Delete selected
+
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(header.column.columnDef.header, header.getContext())}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ No roles found.
+
+
+ )}
+
+
+
+
+
+
+ {selectedCount} of {table.getFilteredRowModel().rows.length} role(s) selected.
+
+
+
+ table.previousPage()} disabled={!table.getCanPreviousPage()}>
+ Previous
+
+ table.nextPage()} disabled={!table.getCanNextPage()}>
+ Next
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/team/team-alocate.tsx b/frontend/src/components/userDashboard/conferenceMenus/team/team-alocate.tsx
new file mode 100644
index 0000000..406d3dd
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/team/team-alocate.tsx
@@ -0,0 +1,457 @@
+import * as React from "react"
+import {
+ type ColumnDef,
+ type ColumnFiltersState,
+ type SortingState,
+ type VisibilityState,
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { ArrowUpDown, ChevronDown } from "lucide-react"
+
+import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { Input } from "@/components/ui/input"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+
+type TeamMember = {
+ id: string
+ name: string
+ email: string
+ roles: string[]
+ assignments: string[]
+}
+
+const roleOptions = [
+ "Secretary General",
+ "Director",
+ "Moderator",
+ "Rapporteur",
+ "Crisis Staff",
+ "Press",
+]
+
+const assignmentOptions = [
+ "Core Team",
+ "Logistics",
+ "Security Council",
+ "UNICEF",
+ "UNHRC",
+ "WHO",
+]
+
+const initialTeamMembers: TeamMember[] = [
+ {
+ id: "a2787617-43d1-4d75-a78e-359b758436fa",
+ name: "Luisa Almeida",
+ email: "luisa.almeida@example.com",
+ roles: ["Director", "Moderator"],
+ assignments: ["Security Council"],
+ },
+ {
+ id: "8521bc6a-bd1d-4eee-a802-f98f85a0cf03",
+ name: "Mateus Silva",
+ email: "mateus.silva@example.com",
+ roles: ["Rapporteur"],
+ assignments: ["UNICEF", "Logistics"],
+ },
+ {
+ id: "f83ab80f-c4a1-430f-8ac7-c95e3f29fdb6",
+ name: "Carla Ribeiro",
+ email: "carla.ribeiro@example.com",
+ roles: ["Secretary General"],
+ assignments: ["Core Team"],
+ },
+ {
+ id: "b90ea379-8673-4094-95a5-ae8ea88a2f9f",
+ name: "Henrique Santos",
+ email: "henrique.santos@example.com",
+ roles: ["Crisis Staff", "Press"],
+ assignments: ["WHO", "UNHRC"],
+ },
+]
+
+function toSortedUniqueList(items: string[]) {
+ return [...new Set(items)].sort((first, second) => first.localeCompare(second))
+}
+
+function MultiAssignCell({
+ value,
+ options,
+ onToggle,
+ buttonLabel,
+}: {
+ value: string[]
+ options: string[]
+ onToggle: (option: string, checked: boolean) => void
+ buttonLabel: string
+}) {
+ const selected = toSortedUniqueList(value)
+
+ return (
+
+
+
+
+ {selected.length > 0 ? `${selected.length} selecionado(s)` : buttonLabel}
+
+
+
+
+
+ {buttonLabel}
+
+ {options.map((option) => (
+ onToggle(option, !!checked)}
+ >
+ {option}
+
+ ))}
+
+
+ )
+}
+
+function AssignmentBadges({ values }: { values: string[] }) {
+ const sortedValues = toSortedUniqueList(values)
+
+ if (sortedValues.length === 0) {
+ return Nenhum
+ }
+
+ return (
+
+ {sortedValues.map((value) => (
+
+ {value}
+
+ ))}
+
+ )
+}
+
+export default function TeamAllocate() {
+ const [teamMembers, setTeamMembers] = React.useState(initialTeamMembers)
+ const [sorting, setSorting] = React.useState([])
+ const [columnFilters, setColumnFilters] = React.useState([])
+ const [columnVisibility, setColumnVisibility] = React.useState({})
+ const [rowSelection, setRowSelection] = React.useState({})
+
+ const toggleArrayValue = React.useCallback(
+ (
+ memberId: string,
+ field: "roles" | "assignments",
+ option: string,
+ checked: boolean
+ ) => {
+ setTeamMembers((currentMembers) =>
+ currentMembers.map((member) => {
+ if (member.id !== memberId) {
+ return member
+ }
+
+ const currentValues = member[field]
+ const nextValues = checked
+ ? [...currentValues, option]
+ : currentValues.filter((item) => item !== option)
+
+ return {
+ ...member,
+ [field]: toSortedUniqueList(nextValues),
+ }
+ })
+ )
+ },
+ []
+ )
+
+ const columns = React.useMemo[]>(
+ () => [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Selecionar todos"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Selecionar membro"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Nome
+
+
+ ),
+ },
+ {
+ accessorKey: "email",
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Email
+
+
+ ),
+ },
+ {
+ id: "roles",
+ accessorFn: (member) => member.roles.join(", "),
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Papéis
+
+
+ ),
+ cell: ({ row }) => (
+
+
+ toggleArrayValue(row.original.id, "roles", option, checked)
+ }
+ />
+
+
+ ),
+ },
+ {
+ id: "assignments",
+ accessorFn: (member) => member.assignments.join(", "),
+ header: ({ column }) => (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Equipes e Comitês
+
+
+ ),
+ cell: ({ row }) => (
+
+
+ toggleArrayValue(row.original.id, "assignments", option, checked)
+ }
+ />
+
+
+ ),
+ },
+ ],
+ [toggleArrayValue]
+ )
+
+ // eslint-disable-next-line react-hooks/incompatible-library -- React Compiler is not enabled; TanStack Table's mutable table API is intentional.
+ const table = useReactTable({
+ data: teamMembers,
+ columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ })
+
+ function deleteSelectedMembers() {
+ const selectedIds = new Set(
+ table.getFilteredSelectedRowModel().rows.map((row) => row.original.id)
+ )
+
+ if (selectedIds.size === 0) {
+ return
+ }
+
+ setTeamMembers((currentMembers) =>
+ currentMembers.filter((member) => !selectedIds.has(member.id))
+ )
+ setRowSelection({})
+ }
+
+ const selectedCount = table.getFilteredSelectedRowModel().rows.length
+
+ return (
+
+
+
Listagem e alocação da equipe
+
+ Gerencie membros da equipe, atribua papéis e distribua equipes/comitês.
+
+
+
+
+
+
+ table.getColumn("name")?.setFilterValue(event.target.value)
+ }
+ className="w-full md:max-w-sm"
+ />
+
+
+
+
+
+ Colunas
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => (
+ column.toggleVisibility(!!value)}
+ >
+ {column.id}
+
+ ))}
+
+
+
+
+ Excluir selecionados
+
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.length > 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ Nenhum membro encontrado.
+
+
+ )}
+
+
+
+
+
+
+ {selectedCount} de {table.getFilteredRowModel().rows.length} membro(s) selecionado(s).
+
+
+
+ table.previousPage()}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Anterior
+
+ table.nextPage()}
+ disabled={!table.getCanNextPage()}
+ >
+ Próxima
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/userDashboard/conferenceMenus/team/team-structure.tsx b/frontend/src/components/userDashboard/conferenceMenus/team/team-structure.tsx
new file mode 100644
index 0000000..bd224e4
--- /dev/null
+++ b/frontend/src/components/userDashboard/conferenceMenus/team/team-structure.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function CommitteeInfo() {
+ return (
+
+
This is the Team Structure Page
+
+ Such Info! much knowledge.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/dash-home.tsx b/frontend/src/components/userDashboard/dash-home.tsx
new file mode 100644
index 0000000..f1d509f
--- /dev/null
+++ b/frontend/src/components/userDashboard/dash-home.tsx
@@ -0,0 +1,13 @@
+
+
+
+export default function DashHome() {
+ return (
+
+
Bem-vindo ao WebMun [USERNAME]!
+
+ Explore as funcionalidades do WebMun e gerencie sua conferência de forma eficiente.
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/userDashboard/team-switcher.tsx b/frontend/src/components/userDashboard/mun-switcher.tsx
similarity index 73%
rename from frontend/src/components/userDashboard/team-switcher.tsx
rename to frontend/src/components/userDashboard/mun-switcher.tsx
index d58930a..793adbe 100644
--- a/frontend/src/components/userDashboard/team-switcher.tsx
+++ b/frontend/src/components/userDashboard/mun-switcher.tsx
@@ -17,23 +17,37 @@ import {
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar"
-import { ChevronsUpDownIcon, PlusIcon } from "lucide-react"
+import { AudioLinesIcon, ChevronsUpDownIcon, GalleryVerticalEndIcon, PlusIcon, TerminalIcon } from "lucide-react"
-export function TeamSwitcher({
- teams,
-}: {
- teams: {
- name: string
- logo: React.ReactNode
- plan: string
- }[]
-}) {
+
+
+export function MUNSwitcher() {
+
+ const Conferences = [
+ {
+ name: "Acme Inc",
+ logo: ,
+ plan: "Enterprise",
+ },
+ {
+ name: "Acme Corp.",
+ logo: ,
+ plan: "Startup",
+ },
+ {
+ name: "Evil Corp.",
+ logo: ,
+ plan: "Free",
+ },
+]
+
const { isMobile } = useSidebar()
- const [activeTeam, setActiveTeam] = React.useState(teams[0])
+ const [activeConference, setActiveTeam] = React.useState(Conferences[0])
- if (!activeTeam) {
+ if (!activeConference) {
return null
}
+
return (
@@ -45,11 +59,11 @@ export function TeamSwitcher({
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
- {activeTeam.logo}
+ {activeConference.logo}
- {activeTeam.name}
- {activeTeam.plan}
+ {activeConference.name}
+ {activeConference.plan}
@@ -61,9 +75,9 @@ export function TeamSwitcher({
sideOffset={4}
>
- Teams
+ Conferences
- {teams.map((team, index) => (
+ {Conferences.map((team, index) => (
setActiveTeam(team)}
@@ -81,7 +95,7 @@ export function TeamSwitcher({
- Add team
+ Create a new conference
diff --git a/frontend/src/components/userDashboard/nav-main.tsx b/frontend/src/components/userDashboard/nav-main.tsx
deleted file mode 100644
index 7af114f..0000000
--- a/frontend/src/components/userDashboard/nav-main.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import {
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
-} from "@/components/ui/collapsible"
-import {
- SidebarGroup,
- SidebarGroupLabel,
- SidebarMenu,
- SidebarMenuButton,
- SidebarMenuItem,
- SidebarMenuSub,
- SidebarMenuSubButton,
- SidebarMenuSubItem,
-} from "@/components/ui/sidebar"
-import { ChevronRightIcon } from "lucide-react"
-
-export function NavMain({
- items,
-}: {
- items: {
- title: string
- url: string
- icon?: React.ReactNode
- isActive?: boolean
- items?: {
- title: string
- url: string
- }[]
- }[]
-}) {
- return (
-
- Platform
-
- {items.map((item) => (
-
-
-
-
- {item.icon}
- {item.title}
-
-
-
-
-
- {item.items?.map((subItem) => (
-
-
-
- {subItem.title}
-
-
-
- ))}
-
-
-
-
- ))}
-
-
- )
-}
diff --git a/frontend/src/components/userDashboard/nav-projects.tsx b/frontend/src/components/userDashboard/nav-projects.tsx
deleted file mode 100644
index ed317a2..0000000
--- a/frontend/src/components/userDashboard/nav-projects.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-"use client"
-
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
-import {
- SidebarGroup,
- SidebarGroupLabel,
- SidebarMenu,
- SidebarMenuAction,
- SidebarMenuButton,
- SidebarMenuItem,
- useSidebar,
-} from "@/components/ui/sidebar"
-import { MoreHorizontalIcon, FolderIcon, ArrowRightIcon, Trash2Icon } from "lucide-react"
-
-export function NavProjects({
- projects,
-}: {
- projects: {
- name: string
- url: string
- icon: React.ReactNode
- }[]
-}) {
- const { isMobile } = useSidebar()
-
- return (
-
- Projects
-
- {projects.map((item) => (
-
-
-
- {item.icon}
- {item.name}
-
-
-
-
-
-
- More
-
-
-
-
-
- View Project
-
-
-
- Share Project
-
-
-
-
- Delete Project
-
-
-
-
- ))}
-
-
-
- More
-
-
-
-
- )
-}
diff --git a/frontend/src/pages/Info Pages/Contact-us.tsx b/frontend/src/pages/Info Pages/Contact-us.tsx
new file mode 100644
index 0000000..872eccb
--- /dev/null
+++ b/frontend/src/pages/Info Pages/Contact-us.tsx
@@ -0,0 +1,135 @@
+import Navbar from "@/components/homepage/navbar"
+import Footer from "@/components/homepage/footer"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { Textarea } from "@/components/ui/textarea"
+
+import { FileQuestionMark, Headset, Mail, Megaphone, Newspaper, Phone, Send } from 'lucide-react';
+
+const waveIcons = [Phone, Headset, Mail, Send, Megaphone, Newspaper, FileQuestionMark]
+const waveTrack = Array.from({ length: 5 }, () => waveIcons).flat()
+const waveRows = Array.from({ length: 7 }, (_, index) => ({
+ direction: index % 2 === 0 ? "up" : "down",
+ duration: 13 + index * 0.35,
+}))
+
+
+
+export default function ContactUs() {
+ return (
+
+
+
+
+ {waveRows.map((row, index) => (
+
+
+ {waveTrack.map((Icon, iconIndex) => (
+
+ ))}
+
+
+ ))}
+
+
+
Contate-nos
+
+
+
+
+
+
Entre em contato
+
+ Envie sua mensagem para a equipe de suporte
+
+
+ Use o formulário abaixo para escolher o motivo do contato, informar seus dados e descrever sua mensagem.
+ A equipe de suporte receberá tudo depois que você enviar.
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/pages/Info Pages/OurTeam.tsx b/frontend/src/pages/Info Pages/OurTeam.tsx
new file mode 100644
index 0000000..e68783c
--- /dev/null
+++ b/frontend/src/pages/Info Pages/OurTeam.tsx
@@ -0,0 +1,190 @@
+import Navbar from "@/components/homepage/navbar"
+import Footer from "@/components/homepage/footer"
+import { Badge } from "@/components/ui/badge"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemGroup,
+ ItemHeader,
+ ItemMedia,
+ ItemTitle,
+} from "@/components/ui/item"
+const teamMembers = [
+ "Antoine Russo Lecointre",
+ "Milena Silva",
+ "Rafael Lombardi",
+ "Ryan de Oliveira",
+ "Vitor Messias",
+]
+
+const marqueeRows = Array.from({ length: 5 }, (_, index) => ({
+ direction: index % 2 === 0 ? "left" : "right",
+ duration: 48 + index * 2,
+}))
+
+const marqueeCycle = teamMembers.flatMap((member) => [member, "✳"])
+const marqueeTrack = [...marqueeCycle, ...marqueeCycle]
+
+const teamCards = [
+ {
+ id: 1,
+ name: "Antoine Russo Lecointre",
+ role: "Cargo / Função",
+ description: "Breve descrição do que essa pessoa faz no projeto. O time pode substituir por uma bio real depois.",
+ image: "https://placehold.co/800x600/png?text=Foto",
+ },
+ {
+ id: 2,
+ name: "Milena Silva",
+ role: "Cargo / Função",
+ description: "Breve descrição do que essa pessoa faz no projeto. O time pode substituir por uma bio real depois.",
+ image: "https://placehold.co/800x600/png?text=Foto",
+ },
+ {
+ id: 3,
+ name: "Rafael Lombardi",
+ role: "Cargo / Função",
+ description: "Breve descrição do que essa pessoa faz no projeto. O time pode substituir por uma bio real depois.",
+ image: "https://placehold.co/800x600/png?text=Foto",
+ },
+ {
+ id: 4,
+ name: "Ryan de Oliveira",
+ role: "Cargo / Função",
+ description: "Breve descrição do que essa pessoa faz no projeto. O time pode substituir por uma bio real depois.",
+ image: "https://placehold.co/800x600/png?text=Foto",
+ },
+ {
+ id: 5,
+ name: "Vitor Messias",
+ role: "Cargo / Função",
+ description: "Breve descrição do que essa pessoa faz no projeto. O time pode substituir por uma bio real depois.",
+ image: "https://placehold.co/800x600/png?text=Foto",
+ },
+]
+
+const acknowledgements = [
+ {
+ id: 1,
+ name: "Luisa Saad",
+ thanksFor: "Design da logo",
+ },
+
+]
+
+export default function OurTeam() {
+ return (
+
+
+
+
+ {marqueeRows.map((row, index) => (
+
+
+ {marqueeTrack.map((item, itemIndex) => (
+
+ {item}
+
+ ))}
+
+
+ ))}
+
+
+
Nossa Equipe
+
+
+
+
+
+
Equipe
+
+ Membros da equipe do WebMUN
+
+
+ Espaço reservado para os cards oficiais do time. Cada membro pode adicionar foto, cargo, nome e uma breve descrição quando os cartões finais estiverem prontos.
+
+
+
+
+ {teamCards.map((member) => (
+ -
+
+
+
+
+
+
+
+ {member.name}
+
+
+ {member.role}
+
+
+
+ {member.description}
+
+
+
+ ))}
+
+
+
+
+ Agradecimentos Especiais
+
+ Agradecemos especialmente às pessoas que contribuíram significativamente para o sucesso do WebMUN.
+
+
+ {acknowledgements.map((person) => (
+ -
+
+
+ {person.name}
+
+
+ {person.thanksFor}.
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/pages/Signup.tsx b/frontend/src/pages/Signup.tsx
new file mode 100644
index 0000000..2cdb0c2
--- /dev/null
+++ b/frontend/src/pages/Signup.tsx
@@ -0,0 +1,28 @@
+import { Link } from "react-router-dom"
+import { SignupForm } from "@/components/auth/signup-form"
+export default function SignupPage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/pages/UserDashboard.tsx b/frontend/src/pages/UserDashboard.tsx
index 64d8642..c0645af 100644
--- a/frontend/src/pages/UserDashboard.tsx
+++ b/frontend/src/pages/UserDashboard.tsx
@@ -1,4 +1,4 @@
-import { AppSidebar } from "@/components/userDashboard/app-sidebar"
+import { AppSidebar } from "@/components/userDashboard/UserDash-sidebar"
import {
Breadcrumb,
BreadcrumbItem,
@@ -13,8 +13,28 @@ import {
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
+import { Link, Outlet, useLocation } from "react-router-dom"
+
+function formatBreadcrumb(pathname: string) {
+ if (pathname === "/dashboard") {
+ return "Overview"
+ }
+
+ const segment = pathname.split("/").filter(Boolean).at(-1)
+
+ if (!segment) {
+ return "Overview"
+ }
+
+ return segment
+ .replace(/[-_]/g, " ")
+ .replace(/\b\w/g, (character) => character.toUpperCase())
+}
export default function UserDash() {
+ const { pathname } = useLocation()
+ const currentSection = formatBreadcrumb(pathname)
+
return (
@@ -29,25 +49,20 @@ export default function UserDash() {
-
- Build Your Application
+
+ Dashboard
- Data Fetching
+ {currentSection}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..1bd6a95
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "webmun",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/package.json
@@ -0,0 +1 @@
+{}