more brainstorming

This commit is contained in:
z1glr
2025-01-06 17:03:18 +00:00
parent 26030a5026
commit 9a7a80ac80
21 changed files with 2767 additions and 443 deletions

View File

@@ -15,7 +15,6 @@
// "postCreateCommand": "yarn install", // "postCreateCommand": "yarn install",
"postCreateCommand": "cd client && npm install" "postCreateCommand": "cd client && npm install"
// Configure tool-specific properties. // Configure tool-specific properties.
// "customizations": {}, // "customizations": {},

2210
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,22 @@
}, },
"dependencies": { "dependencies": {
"@carbon/icons-react": "^11.53.0", "@carbon/icons-react": "^11.53.0",
"@internationalized/date": "^3.6.0",
"@nextui-org/button": "^2.2.9",
"@nextui-org/card": "^2.2.9",
"@nextui-org/checkbox": "^2.3.8",
"@nextui-org/date-picker": "^2.3.9",
"@nextui-org/divider": "^2.2.5",
"@nextui-org/input": "^2.4.8",
"@nextui-org/modal": "^2.2.7",
"@nextui-org/select": "^2.4.9",
"@nextui-org/system": "^2.4.6",
"@nextui-org/table": "^2.2.8",
"@nextui-org/theme": "^2.4.5",
"@nextui-org/tooltip": "^2.2.7",
"@react-aria/i18n": "^3.12.4",
"@react-stately/data": "^3.12.0",
"framer-motion": "^11.15.0",
"next": "15.1.3", "next": "15.1.3",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",

76
client/src/Zustand.ts Normal file
View File

@@ -0,0 +1,76 @@
import {
DateFormatter as IntlDateFormatter,
parseZonedDateTime,
ZonedDateTime,
} from "@internationalized/date";
import { create } from "zustand";
export type Task = string;
export const Tasks: Task[] = [
"Audio",
"Livestream",
"Camera",
"Light",
"Stream Audio",
];
export type Availability = string;
export const Availabilities: Availability[] = ["yes", "maybe", "no"];
export interface EventData {
id: number;
date: ZonedDateTime;
tasks: Partial<Record<Task, string | undefined>>;
volunteers: Partial<Record<string, Availability>>;
description: string;
}
interface Zustand {
events: EventData[];
addEvent: (event: EventData) => void;
}
const zustand = create<Zustand>()((set) => ({
events: [
{
id: 0,
// date: parseDateTime("2025-01-05T11:00[Europe/Berlin]").toString(),
date: parseZonedDateTime("2025-01-05T11:00[Europe/Berlin]"),
tasks: {
Audio: "Mark",
Livestream: undefined,
"Stream Audio": undefined,
},
volunteers: { Mark: "yes", Simon: "maybe", Sophie: "no" },
description: "neuer Prädikant",
},
{
id: 1,
date: parseZonedDateTime("2025-01-12T11:00[Europe/Berlin]"),
tasks: {
Audio: "Mark",
Livestream: undefined,
},
volunteers: { Mark: "yes", Simon: "maybe" },
description: "",
},
],
addEvent: (event: EventData) =>
set((state) => ({ events: state.events.toSpliced(-1, 0, event) })),
}));
export class DateFormatter {
private formatter;
constructor(locale: string, options?: Intl.DateTimeFormatOptions) {
this.formatter = new IntlDateFormatter(locale, options);
}
format(dt: Date) {
return this.formatter.format(dt);
}
}
export default zustand;

View File

@@ -0,0 +1 @@
export default function ManageEvents() {}

View File

@@ -0,0 +1,46 @@
"use client";
import { Add } from "@carbon/icons-react";
import Event from "../components/Event/Event";
import { useState } from "react";
import AddEvent from "../components/Event/AddEvent";
import zustand from "../Zustand";
import { Button } from "@nextui-org/button";
export default function EventVolunteer() {
const [showAddItemDialogue, setShowAddItemDialogue] = useState(false);
return (
<div className="relative flex-1 p-4">
<h2 className="mb-4 text-center text-4xl">Overview</h2>
<div className="flex flex-wrap justify-center gap-4">
{zustand.getState().events.map((ee, ii) => (
<Event
key={ii}
date={ee.date}
description={ee.description}
id={ee.id}
tasks={ee.tasks}
volunteers={ee.volunteers}
/>
))}
</div>
<Button
color="primary"
isIconOnly
radius="full"
className="absolute bottom-0 right-0"
onPress={() => setShowAddItemDialogue(true)}
>
<Add size={32} />
</Button>
<AddEvent
className="border-2 border-accent-3"
isOpen={showAddItemDialogue}
onOpenChange={setShowAddItemDialogue}
/>
</div>
);
}

View File

@@ -0,0 +1,253 @@
"use client";
import AddEvent from "@/components/Event/AddEvent";
import LocalDate from "@/components/Event/LocalDate";
import zustand, { Availability, EventData, Task, Tasks } from "@/Zustand";
import { Add, Copy, Edit, TrashCan } from "@carbon/icons-react";
import { Button, ButtonGroup } from "@nextui-org/button";
import {
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
} from "@nextui-org/modal";
import { Select, SelectItem } from "@nextui-org/select";
import {
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
} from "@nextui-org/table";
import { Tooltip } from "@nextui-org/tooltip";
import { useAsyncList } from "@react-stately/data";
import React, { Key, useState } from "react";
export default function AdminPanel() {
const tasks = [
{ key: "date", label: "Date" },
{ key: "description", label: "Description" },
...Tasks.map((task) => ({ label: task, key: task })),
{ key: "actions", label: "Action" },
];
const list = useAsyncList({
async load() {
return {
items: [...zustand.getState().events],
};
},
async sort({ items, sortDescriptor }) {
return {
items: items.sort((a, b) => {
let cmp = 0;
// if it is the date-column, convert to a date
if (sortDescriptor.column === "date") {
const first = a[sortDescriptor.column];
const second = b[sortDescriptor.column];
cmp = first < second ? -1 : 1;
}
if (sortDescriptor.direction === "descending") {
cmp *= -1;
}
return cmp;
}),
};
},
});
function getKeyValue(event: EventData, key: Key): React.ReactNode {
switch (key) {
case "date":
return (
<LocalDate
options={{ dateStyle: "medium", timeStyle: "short" }}
className="font-bold"
>
{event[key].toDate()}
</LocalDate>
);
case "description":
return <span className="italic">{event[key]}</span>;
case "actions":
return (
<div className="flex justify-end">
<ButtonGroup isIconOnly variant="light" size="sm">
<Button>
<Tooltip content="Edit event">
<Edit />
</Tooltip>
</Button>
<Button>
<Tooltip content="Duplicate event">
<Copy />
</Tooltip>
</Button>
<Button
color="danger"
onPress={() => {
setActiveEvent(event);
setShowDeleteConfirm(true);
}}
>
<Tooltip content="Delete event">
<TrashCan />
</Tooltip>
</Button>
</ButtonGroup>
</div>
);
default:
function availability2Tailwind(availability?: Availability) {
switch (availability) {
case "yes":
return "";
default:
return "italic";
}
}
function availability2Color(availability?: Availability) {
switch (availability) {
case "yes":
return "default";
case "maybe":
return "warning";
default:
return "danger";
}
}
return (
<Select
variant="underlined"
fullWidth
selectedKeys={new Set([event.tasks[key as Task] ?? ""])}
classNames={{
popoverContent: "w-fit",
value: "mr-6",
label: "mr-6",
}}
className="[&_*]:overflow-visible"
>
{Object.entries(event.volunteers).map(
([volunteer, availability]) => (
<SelectItem
key={volunteer}
color={availability2Color(availability)}
className={[
"text-" + availability2Color(availability),
availability2Tailwind(availability),
].join(" ")}
>
{volunteer}
</SelectItem>
),
)}
</Select>
);
}
}
const [showAddEvent, setShowAddEvent] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [activeEvent, setActiveEvent] = useState(zustand.getState().events[0]);
const topContent = (
<div>
<Button
color="primary"
startContent={<Add size={32} />}
onPress={() => setShowAddEvent(true)}
>
Add
</Button>
</div>
);
return (
<div className="relative flex flex-col items-center">
<h2 className="mb-4 text-center text-4xl">Event Managment</h2>
<Table
aria-label="Table with all the events"
shadow="none"
topContent={topContent}
topContentPlacement="outside"
isHeaderSticky
sortDescriptor={list.sortDescriptor}
onSortChange={list.sort}
classNames={{
wrapper: "bg-accent-4",
tr: "even:bg-accent-5 ",
th: "font-subheadline text-xl text-accent-1 bg-transparent ",
thead: "[&>tr]:first:!shadow-border",
}}
className="w-fit"
>
<TableHeader columns={tasks}>
{(task) => (
<TableColumn
allowsSorting={task.key === "date"}
key={task.key}
className=""
>
{task.label}
</TableColumn>
)}
</TableHeader>
<TableBody items={list.items} emptyContent={"No events scheduled"}>
{(event) => (
<TableRow key={event.id}>
{(columnKey) => (
<TableCell>{getKeyValue(event, columnKey)}</TableCell>
)}
</TableRow>
)}
</TableBody>
</Table>
<AddEvent isOpen={showAddEvent} onOpenChange={setShowAddEvent} />
<Modal
isOpen={showDeleteConfirm}
onOpenChange={setShowDeleteConfirm}
shadow={"none" as "sm"}
backdrop="blur"
className="bg-accent-5"
>
<ModalContent>
<ModalHeader>
<h1 className="text-2xl">Confirm event deletion</h1>
</ModalHeader>
<ModalBody>
The event{" "}
<span className="font-numbers text-accent-1">
<LocalDate options={{ dateStyle: "long", timeStyle: "short" }}>
{activeEvent.date.toDate()}
</LocalDate>
</span>{" "}
will be deleted.
</ModalBody>
<ModalFooter>
<Button startContent={<TrashCan />} color="danger">
Delete event
</Button>
<Button
variant="bordered"
onPress={() => setShowDeleteConfirm(false)}
>
Cancel
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</div>
);
}

View File

@@ -1,16 +0,0 @@
import React, { MouseEventHandler } from "react";
export default function Button(props: {
children: React.ReactNode;
className?: string;
onClick?: MouseEventHandler<HTMLDivElement>;
}) {
return (
<div
onClick={props.onClick}
className={`${props.className ?? ""} inline-block cursor-pointer rounded-full bg-accent-2 p-2`}
>
{props.children}
</div>
);
}

View File

@@ -1,13 +0,0 @@
import { CheckboxChecked, Checkbox } from "@carbon/icons-react";
import { MouseEventHandler } from "react";
export default function CheckBox(props: {
state: boolean;
onClick?: MouseEventHandler<HTMLDivElement>;
}) {
return (
<div onClick={props.onClick} className="inline-block cursor-pointer">
{props.state ? <CheckboxChecked /> : <Checkbox />}
</div>
);
}

View File

@@ -1,93 +0,0 @@
import { useState } from "react";
import CheckBox from "../CheckBox";
import { AddLarge } from "@carbon/icons-react";
import Button from "../Button";
import zustand, { EventData, ISODate, Task, Tasks } from "../Zustand";
interface state {
date: string;
description: string;
tasks: Task[];
}
export default function AddEvent(props: {
className?: string;
onClose: () => void;
}) {
const [state, setState] = useState<state>({
date: ISODate(new Date()),
description: "",
tasks: [],
});
function toggleTask(task: Task) {
const new_tasks = state.tasks.slice();
const index = new_tasks.indexOf(task);
if (index != -1) {
new_tasks.splice(index, 1);
} else {
new_tasks.push(task);
}
setState({
...state,
tasks: new_tasks,
});
}
function addEvent() {
const eventData: EventData = {
date: state.date,
description: state.description,
id: zustand.getState().events.slice(-1)[0].id + 1,
tasks: {},
};
// add all the tasks
state.tasks.forEach((task) => {
eventData.tasks[task] = undefined;
});
zustand.getState().addEvent(eventData);
props.onClose();
}
return (
<div
className={`${props.className ?? ""} flex w-64 flex-col gap-2 rounded-xl bg-accent-5 p-4`}
>
<h1 className="text-2xl">Add Event</h1>
<input
type="date"
value={state.date}
onChange={(e) => console.log(e.target.value)}
/>
<input
type="text"
placeholder="Description"
value={state.description}
onChange={(e) => setState({ ...state, description: e.target.value })}
/>
{Tasks.map((task, ii) => (
<div
key={ii}
onClick={() => toggleTask(task)}
className="flex cursor-default items-center gap-2"
>
<CheckBox state={state.tasks.includes(task)} />
{task}
</div>
))}
<Button
className="ml-auto flex w-fit items-center justify-center gap-2 pr-4"
onClick={addEvent}
>
<AddLarge size={32} />
Add
</Button>
</div>
);
}

View File

@@ -1,27 +0,0 @@
import { EventData } from "../Zustand";
export default function Event(props: EventData) {
return (
<div
key={props.id}
className="flex w-64 flex-col gap-2 rounded-xl bg-accent-5 p-4"
>
<h3 className="bold mb-1 text-2xl">{props.date}</h3>
<div>{props.description}</div>
<table>
<caption>
<h4>Task assignment</h4>
</caption>
<tbody>
{Object.entries(props.tasks).map(([task, person], ii) => (
<tr key={ii}>
<th className="pr-4 text-left">{task}</th>
<td>{person ?? <span className="text-primary">missing</span>}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}

View File

@@ -1,53 +0,0 @@
"use client";
import { AddLarge, CloseLarge } from "@carbon/icons-react";
import Event from "./Event/Event";
import { useState } from "react";
import AddEvent from "./Event/AddEvent";
import Button from "./Button";
import zustand from "./Zustand";
export default function EventVolunteer() {
const [showAddItemDialogue, setShowAddItemDialogue] = useState(false);
return (
<div className="relative flex-1 p-4">
<h2 className="mb-4 text-center text-4xl">Overview</h2>
<div className="flex flex-wrap justify-center gap-4">
{zustand.getState().events.map((ee) => Event(ee))}
</div>
<Button
className="absolute bottom-0 right-0 aspect-square"
onClick={() => setShowAddItemDialogue(true)}
>
<AddLarge size={32} />
</Button>
{showAddItemDialogue ? (
<div
className="absolute inset-0 flex flex-col items-center backdrop-blur"
onClick={(e) => {
if (e.target === e.currentTarget) {
setShowAddItemDialogue(false);
e.preventDefault();
}
}}
>
<div className="relative">
<AddEvent
className="border-2 border-accent-3"
onClose={() => setShowAddItemDialogue(false)}
/>
<Button
className="absolute right-2 top-2 aspect-square"
onClick={() => setShowAddItemDialogue(false)}
>
<CloseLarge />
</Button>
</div>
</div>
) : null}
</div>
);
}

View File

@@ -1,68 +0,0 @@
import { create } from "zustand";
export enum Task {
Audio = "Audio",
Livestream = "Livestream",
Camera = "Camera",
Light = "Light",
StreamAudio = "Stream Audio",
}
export type TaskKey = keyof typeof Task;
export const Tasks = Object.values(Task) as Task[];
export interface EventData {
id: number;
date: string;
tasks: Partial<Record<Task, string | undefined>>;
description: string;
}
interface Zustand {
events: EventData[];
addEvent: (event: EventData) => void;
}
const zustand = create<Zustand>()((set) => ({
events: [
{
id: 0,
date: "2025-01-05",
tasks: {
Audio: "Mark",
Livestream: undefined,
"Stream Audio": undefined,
},
description: "neuer Prädikant",
},
{
id: 1,
date: "2025-01-12",
tasks: {
Audio: "Mark",
Livestream: undefined,
},
description: "",
},
],
addEvent: (event: EventData) =>
set((state) => ({ events: state.events.toSpliced(-1, 0, event) })),
}));
export function ISODate(dt: string | Date): string {
if (typeof dt === "string") {
dt = new Date(dt);
}
const year = String(dt.getFullYear()).padStart(4, "0");
const month = String(dt.getMonth() + 1).padStart(2, "0");
const day = String(dt.getDate()).padStart(2, "0");
const date = `${year}-${month}-${day}`;
console.debug(date);
return date;
}
export default zustand;

View File

@@ -31,17 +31,6 @@
src: URL("/fonts/uncut-sans/Webfonts/UncutSans-Regular.woff2") format("woff2"); src: URL("/fonts/uncut-sans/Webfonts/UncutSans-Regular.woff2") format("woff2");
} }
:root {
--primary: #ff5053;
--highlight: #fef2ff;
--accent-1: #b2aaff;
--accent-2: #6a5fdb;
--accent-3: #261a66;
--accent-4: #29114c;
--accent-5: #190b2f;
--background: #0f000a;
}
@layer base { @layer base {
h1, h1,
h2, h2,
@@ -49,14 +38,10 @@
h4, h4,
h5, h5,
h6 { h6 {
@apply font-headline text-primary; @apply font-headline text-highlight;
} }
input { input {
@apply border-2 border-accent-1 bg-transparent; @apply border-2 border-accent-1 bg-transparent;
} }
html {
@apply bg-background font-body text-highlight;
}
} }

View File

@@ -1,19 +1,31 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import "./globals.css"; import "./globals.css";
import { NextUIProvider } from "@nextui-org/system";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "Create Next App",
description: "Generated by create next app" description: "Generated by create next app",
}; };
export default function RootLayout({ export default function RootLayout({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en"> <html>
<body className="antialiased">{children}</body> <body className="bg-background text-foreground antialiased">
<NextUIProvider>
<div className="flex min-h-screen flex-col p-4">
<header>
<h1 className="text-center font-display-headline text-8xl">
Volunteer Scheduler
</h1>
</header>
<main className="flex min-h-full flex-1 flex-col">{children}</main>
</div>
</NextUIProvider>
</body>
</html> </html>
); );
} }

View File

@@ -0,0 +1,3 @@
export default function OverviewPersonal() {
return <div>foobar</div>;
}

View File

@@ -1,16 +1,5 @@
import EventVolunteer from "./components/Overview"; import EventVolunteer from "./Overview";
export default function Home() { export default function Home() {
return ( return <EventVolunteer />;
<div className="p-4 min-h-screen flex flex-col">
<header>
<h1 className="text-center text-8xl font-display-headline">
Volunteer schedluer
</h1>
</header>
<main className="min-h-full flex-1 flex">
<EventVolunteer />
</main>
</div>
);
} }

View File

@@ -0,0 +1,106 @@
import { useState } from "react";
import { Add } from "@carbon/icons-react";
import zustand, { EventData, Task, Tasks } from "../../Zustand";
import { Button } from "@nextui-org/button";
import { Checkbox, CheckboxGroup } from "@nextui-org/checkbox";
import { DatePicker } from "@nextui-org/date-picker";
import { getLocalTimeZone, now, ZonedDateTime } from "@internationalized/date";
import { Textarea } from "@nextui-org/input";
import {
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
} from "@nextui-org/modal";
interface state {
date: ZonedDateTime;
description: string;
tasks: Task[];
}
export default function AddEvent(props: {
className?: string;
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
}) {
const [state, setState] = useState<state>({
date: now(getLocalTimeZone()),
description: "",
tasks: [],
});
function addEvent() {
const eventData: EventData = {
date: state.date.toString(),
description: state.description,
id: zustand.getState().events.slice(-1)[0].id + 1,
tasks: {},
volunteers: {},
};
// add all the tasks
state.tasks.forEach((task) => {
eventData.tasks[task] = undefined;
});
zustand.getState().addEvent(eventData);
}
return (
<Modal
isOpen={props.isOpen}
shadow={"none" as "sm"} // somehow "none" isn't allowed
onOpenChange={props.onOpenChange}
backdrop="blur"
classNames={{
base: "bg-accent-5 ",
}}
>
<ModalContent>
<ModalHeader>
<h1 className="text-2xl">Add Event</h1>
</ModalHeader>
<ModalBody>
<DatePicker
label="Event date"
variant="bordered"
hideTimeZone
granularity="minute"
value={state.date}
onChange={(dt) => (!!dt ? setState({ ...state, date: dt }) : null)}
/>
<Textarea
variant="bordered"
placeholder="Description"
value={state.description}
onValueChange={(desc) => setState({ ...state, description: desc })}
/>
<CheckboxGroup
value={state.tasks}
onValueChange={(newTasks) =>
setState({ ...state, tasks: newTasks })
}
>
{Tasks.map((task, ii) => (
<div key={ii}>
<Checkbox value={task}>{task}</Checkbox>
</div>
))}
</CheckboxGroup>
</ModalBody>
<ModalFooter>
<Button
color="primary"
radius="full"
startContent={<Add size={32} />}
onPress={addEvent}
>
Add
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}

View File

@@ -0,0 +1,50 @@
import { Card, CardBody, CardHeader } from "@nextui-org/card";
import { Divider } from "@nextui-org/divider";
import LocalDate from "./LocalDate";
import { EventData } from "@/Zustand";
export default function Event(props: EventData) {
return (
<Card
classNames={{
base: "bg-accent-4 w-64",
body: "flex flex-col gap-4",
}}
shadow="none"
>
<CardHeader>
<h3 className="bold mb-1 text-2xl">
<LocalDate
options={{
dateStyle: "short",
timeStyle: "short",
timeZone: "Europe/Berlin", // TODO: check with actual backend
}}
>
{props.date.toDate()}
</LocalDate>
</h3>
</CardHeader>
<Divider />
<CardBody>
<div>{props.description}</div>
<table>
<caption>
<h4>Task assignment</h4>
</caption>
<tbody>
{Object.entries(props.tasks).map(([task, person], ii) => (
<tr key={ii}>
<th className="pr-4 text-left">{task}</th>
<td>
{person ?? <span className="text-highlight">missing</span>}
</td>
</tr>
))}
</tbody>
</table>
</CardBody>
</Card>
);
}

View File

@@ -0,0 +1,16 @@
"use local";
import { DateFormatter } from "@/Zustand";
import { useLocale } from "@react-aria/i18n";
export default function LocalDate(props: {
children: Date;
className?: string;
options: Intl.DateTimeFormatOptions;
}) {
const formatter = new DateFormatter(useLocale().locale, props.options);
return (
<span className={props.className}>{formatter.format(props.children)}</span>
);
}

View File

@@ -1,31 +1,115 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import { nextui } from "@nextui-org/theme";
const HIGHLIGHT = "#ff5053";
const FOREGROUND = "#fef2ff";
const ACCENT1 = "#b2aaff";
const ACCENT2 = "#6a5fdb";
const ACCENT3 = "#261a66";
const ACCENT4 = "#29114c";
const ACCENT5 = "#190b2f";
const BACKGROUND = "#0f000a";
export default { export default {
content: [ content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}" "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
], ],
theme: { theme: {
extend: { extend: {
colors: { colors: {
background: "var(--background)", highlight: HIGHLIGHT,
primary: "var(--primary)", foreground: {
highlight: "var(--highlight)", DEFAULT: FOREGROUND,
"accent-1": "var(--accent-1)", "50": FOREGROUND,
"accent-2": "var(--accent-2)", "100": "#fce8ff",
"accent-3": "var(--accent-3)", "200": "#fad0fe",
"accent-4": "var(--accent-4)", "300": "#f8abfc",
"accent-5": "var(--accent-5)" "400": "#f579f9",
} "500": "#eb46ef",
"600": "#d226d3",
"700": "#af1cad",
"800": "#8f198c",
"900": "#751a70",
"950": "#4e044a",
},
"accent-1": ACCENT1,
"accent-2": ACCENT2,
"accent-3": ACCENT3,
"accent-4": ACCENT4,
"accent-5": ACCENT5,
background: BACKGROUND,
},
boxShadow: {
border: `inset 0 0 0 2px ${ACCENT2}`,
},
}, },
fontFamily: { fontFamily: {
"display-headline": ["pilowlava"], "display-headline": ["pilowlava"],
headline: ["spacegrotesk"], headline: ["spacegrotesk"],
subheadline: ["uncut-sans"], subheadline: ["uncut-sans"],
body: ["uncut-sans"], body: ["uncut-sans"],
numbers: ["space-mono"] numbers: ["space-mono"],
} },
}, },
plugins: [] darkMode: "class",
plugins: [
nextui({
defaultTheme: "dark",
defaultExtendTheme: "dark",
themes: {
dark: {
colors: {
// default: {
// DEFAULT: ACCENT2,
// },
primary: {
DEFAULT: ACCENT2,
"50": "#39357a",
"100": "#42399a",
"200": "#5144be",
"300": ACCENT2,
"400": "#6f6ee6",
"500": "#8b91ee",
"600": "#acb7f5",
"700": "#cbd3fa",
"800": "#e2e7fd",
"900": "#eff3fe",
},
secondary: {
DEFAULT: ACCENT3,
"50": "#3b288a",
"100": "#462fa8",
"200": "#5538c9",
"300": "#634add",
"400": "#776ae8",
"500": "#9a95f0",
"600": "#bdbcf6",
"700": "#dadbfa",
"800": "#ebebfc",
"900": "#f4f4fe",
},
// background: {
// DEFAULT: BACKGROUND,
// },
danger: {
DEFAULT: HIGHLIGHT,
"50": "#fff1f1",
"100": "#ffe1e2",
"200": "#ffc7c8",
"300": "#ffa0a2",
"400": HIGHLIGHT,
"500": "#f83b3e",
"600": "#e51d20",
"700": "#c11417",
"800": "#a01416",
"900": "#84181a",
},
},
},
},
}),
],
} satisfies Config; } satisfies Config;