diff --git a/src/components/common/Modal/Modal.tsx b/src/components/common/Modal/Modal.tsx index 0cac8cf..adce0d9 100644 --- a/src/components/common/Modal/Modal.tsx +++ b/src/components/common/Modal/Modal.tsx @@ -20,7 +20,7 @@ const overlayStyles = tv({ }); const modalStyles = tv({ - base: "p-5 w-full max-w-md max-h-full rounded-2xl bg-gray-subtle forced-colors:bg-[Canvas] flex flex-col items-start gap-4 shadow-2xl bg-clip-padding border border-gray-dim", + base: "p-5 w-[400px] max-w-full max-h-full rounded-2xl bg-gray-subtle forced-colors:bg-[Canvas] flex flex-col items-start gap-4 shadow-2xl bg-clip-padding border border-gray-dim", variants: { isEntering: { true: "animate-in zoom-in-105 ease-out duration-2", @@ -58,3 +58,11 @@ export function ModalHeader({ title, children }: ModalHeaderProps) { ); } + +export function ModalFooter({ children }: { children: React.ReactNode }) { + return ( + + ); +} diff --git a/src/components/quests/EditQuestCostsModal/EditQuestCostsModal.tsx b/src/components/quests/EditQuestCostsModal/EditQuestCostsModal.tsx index 2683178..d2371a9 100644 --- a/src/components/quests/EditQuestCostsModal/EditQuestCostsModal.tsx +++ b/src/components/quests/EditQuestCostsModal/EditQuestCostsModal.tsx @@ -30,7 +30,7 @@ const CostInput = memo(function CostInput({
diff --git a/src/components/quests/EditQuestTimeRequiredModal/EditQuestTimeRequiredModal.tsx b/src/components/quests/EditQuestTimeRequiredModal/EditQuestTimeRequiredModal.tsx index 2d1515d..bf9a15f 100644 --- a/src/components/quests/EditQuestTimeRequiredModal/EditQuestTimeRequiredModal.tsx +++ b/src/components/quests/EditQuestTimeRequiredModal/EditQuestTimeRequiredModal.tsx @@ -39,7 +39,7 @@ const TimeRequiredInput = memo(function TimeRequiredInput({
@@ -51,7 +51,7 @@ const TimeRequiredInput = memo(function TimeRequiredInput({ /> @@ -63,7 +63,7 @@ const TimeRequiredInput = memo(function TimeRequiredInput({ /> setBirthplace(key as Jurisdiction)} + placeholder="Select state" + className="w-full" + > + {Object.entries(JURISDICTIONS).map(([value, label]) => ( + + {label} + + ))} + + + + + + + + ); +}; + +type EditBirthplaceSettingProps = { + user: Doc<"users">; +}; + +export const EditBirthplaceSetting = ({ user }: EditBirthplaceSettingProps) => { + const [isBirthplaceModalOpen, setIsBirthplaceModalOpen] = useState(false); + + return ( + + + setIsBirthplaceModalOpen(false)} + /> + + ); +}; diff --git a/src/components/settings/EditBirthplaceSetting/index.ts b/src/components/settings/EditBirthplaceSetting/index.ts new file mode 100644 index 0000000..84c173f --- /dev/null +++ b/src/components/settings/EditBirthplaceSetting/index.ts @@ -0,0 +1 @@ +export * from "./EditBirthplaceSetting"; diff --git a/src/components/settings/EditMinorSetting/EditMinorSetting.tsx b/src/components/settings/EditMinorSetting/EditMinorSetting.tsx new file mode 100644 index 0000000..3d5f0e3 --- /dev/null +++ b/src/components/settings/EditMinorSetting/EditMinorSetting.tsx @@ -0,0 +1,28 @@ +import { Switch } from "@/components/common"; +import { SettingsItem } from "@/components/settings"; +import { api } from "@convex/_generated/api"; +import type { Doc } from "@convex/_generated/dataModel"; +import { useMutation } from "convex/react"; + +type EditMinorSettingProps = { + user: Doc<"users">; +}; + +export const EditMinorSetting = ({ user }: EditMinorSettingProps) => { + const updateIsMinor = useMutation(api.users.setCurrentUserIsMinor); + + return ( + + updateIsMinor({ isMinor: !user.isMinor })} + > + Is minor + + + ); +}; diff --git a/src/components/settings/EditMinorSetting/index.ts b/src/components/settings/EditMinorSetting/index.ts new file mode 100644 index 0000000..9daf4da --- /dev/null +++ b/src/components/settings/EditMinorSetting/index.ts @@ -0,0 +1 @@ +export * from "./EditMinorSetting"; diff --git a/src/components/settings/EditNameSetting/EditNameSetting.tsx b/src/components/settings/EditNameSetting/EditNameSetting.tsx new file mode 100644 index 0000000..c73642c --- /dev/null +++ b/src/components/settings/EditNameSetting/EditNameSetting.tsx @@ -0,0 +1,85 @@ +import { + Button, + Form, + Modal, + ModalFooter, + ModalHeader, + TextField, +} from "@/components/common"; +import { api } from "@convex/_generated/api"; +import type { Doc } from "@convex/_generated/dataModel"; +import { useMutation } from "convex/react"; +import { Pencil } from "lucide-react"; +import { useState } from "react"; +import { SettingsItem } from "../SettingsItem"; + +type EditNameModalProps = { + defaultName: string; + isOpen: boolean; + onOpenChange: (isOpen: boolean) => void; + onSubmit: () => void; +}; + +const EditNameModal = ({ + defaultName, + isOpen, + onOpenChange, + onSubmit, +}: EditNameModalProps) => { + const updateName = useMutation(api.users.setName); + const [name, setName] = useState(defaultName); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + updateName({ name }); + onSubmit(); + }; + + return ( + + +
+ + + + + + +
+ ); +}; + +type EditNameSettingProps = { + user: Doc<"users">; +}; + +export const EditNameSetting = ({ user }: EditNameSettingProps) => { + const [isNameModalOpen, setIsNameModalOpen] = useState(false); + + return ( + + + setIsNameModalOpen(false)} + /> + + ); +}; diff --git a/src/components/settings/EditNameSetting/index.ts b/src/components/settings/EditNameSetting/index.ts new file mode 100644 index 0000000..90caf83 --- /dev/null +++ b/src/components/settings/EditNameSetting/index.ts @@ -0,0 +1 @@ +export * from "./EditNameSetting"; diff --git a/src/components/settings/EditResidenceSetting/EditResidenceSetting.tsx b/src/components/settings/EditResidenceSetting/EditResidenceSetting.tsx new file mode 100644 index 0000000..b4c9bcf --- /dev/null +++ b/src/components/settings/EditResidenceSetting/EditResidenceSetting.tsx @@ -0,0 +1,96 @@ +import { + Button, + Form, + Modal, + ModalFooter, + ModalHeader, + Select, + SelectItem, +} from "@/components/common"; +import { SettingsItem } from "@/components/settings"; +import { api } from "@convex/_generated/api"; +import type { Doc } from "@convex/_generated/dataModel"; +import { JURISDICTIONS, type Jurisdiction } from "@convex/constants"; +import { useMutation } from "convex/react"; +import { Pencil } from "lucide-react"; +import { useState } from "react"; + +type EditResidenceModalProps = { + defaultResidence: Jurisdiction; + isOpen: boolean; + onOpenChange: (isOpen: boolean) => void; + onSubmit: () => void; +}; + +const EditResidenceModal = ({ + defaultResidence, + isOpen, + onOpenChange, + onSubmit, +}: EditResidenceModalProps) => { + const updateResidence = useMutation(api.users.setResidence); + const [residence, setResidence] = useState(defaultResidence); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + updateResidence({ residence }); + onSubmit(); + }; + + return ( + + +
+ + + + + +
+
+ ); +}; + +type EditResidenceSettingProps = { + user: Doc<"users">; +}; + +export const EditResidenceSetting = ({ user }: EditResidenceSettingProps) => { + const [isResidenceModalOpen, setIsResidenceModalOpen] = useState(false); + + return ( + + + setIsResidenceModalOpen(false)} + /> + + ); +}; diff --git a/src/components/settings/EditResidenceSetting/index.ts b/src/components/settings/EditResidenceSetting/index.ts new file mode 100644 index 0000000..0e859fa --- /dev/null +++ b/src/components/settings/EditResidenceSetting/index.ts @@ -0,0 +1 @@ +export * from "./EditResidenceSetting"; diff --git a/src/components/settings/EditThemeSetting/EditThemeSetting.tsx b/src/components/settings/EditThemeSetting/EditThemeSetting.tsx new file mode 100644 index 0000000..2d7b17a --- /dev/null +++ b/src/components/settings/EditThemeSetting/EditThemeSetting.tsx @@ -0,0 +1,25 @@ +import { ToggleButton, ToggleButtonGroup } from "@/components/common"; +import { useTheme } from "@/utils/useTheme"; +import { THEMES } from "@convex/constants"; +import { SettingsItem } from "../SettingsItem"; + +export const EditThemeSetting = () => { + const { themeSelection, setTheme } = useTheme(); + + return ( + + + {Object.entries(THEMES).map(([theme, details]) => ( + + {details.label} + + ))} + + + ); +}; diff --git a/src/components/settings/EditThemeSetting/index.ts b/src/components/settings/EditThemeSetting/index.ts new file mode 100644 index 0000000..2cd9e6a --- /dev/null +++ b/src/components/settings/EditThemeSetting/index.ts @@ -0,0 +1 @@ +export * from "./EditThemeSetting"; diff --git a/src/components/settings/SettingsGroup/SettingsGroup.tsx b/src/components/settings/SettingsGroup/SettingsGroup.tsx new file mode 100644 index 0000000..9ac8d5a --- /dev/null +++ b/src/components/settings/SettingsGroup/SettingsGroup.tsx @@ -0,0 +1,16 @@ +import { Card } from "@/components/common"; +import { Heading } from "react-aria-components"; + +type SettingsGroupProps = { + title: string; + children: React.ReactNode; +}; + +export const SettingsGroup = ({ title, children }: SettingsGroupProps) => ( +
+ {title} + + {children} + +
+); diff --git a/src/components/settings/SettingsGroup/index.ts b/src/components/settings/SettingsGroup/index.ts new file mode 100644 index 0000000..e5ade9c --- /dev/null +++ b/src/components/settings/SettingsGroup/index.ts @@ -0,0 +1 @@ +export * from "./SettingsGroup"; diff --git a/src/components/settings/SettingsItem/SettingsItem.tsx b/src/components/settings/SettingsItem/SettingsItem.tsx new file mode 100644 index 0000000..821a269 --- /dev/null +++ b/src/components/settings/SettingsItem/SettingsItem.tsx @@ -0,0 +1,25 @@ +import { Heading } from "react-aria-components"; + +type SettingsItemProps = { + label: string; + description?: string; + children: React.ReactNode; +}; + +export const SettingsItem = ({ + label, + description, + children, +}: SettingsItemProps) => ( +
+
+ {label} + {description && ( +

+ {description} +

+ )} +
+
{children}
+
+); diff --git a/src/components/settings/SettingsItem/index.ts b/src/components/settings/SettingsItem/index.ts new file mode 100644 index 0000000..d356523 --- /dev/null +++ b/src/components/settings/SettingsItem/index.ts @@ -0,0 +1 @@ +export * from "./SettingsItem"; diff --git a/src/components/settings/index.ts b/src/components/settings/index.ts new file mode 100644 index 0000000..5b7239d --- /dev/null +++ b/src/components/settings/index.ts @@ -0,0 +1,8 @@ +export * from "./DeleteAccountSetting"; +export * from "./EditBirthplaceSetting"; +export * from "./EditNameSetting"; +export * from "./EditMinorSetting"; +export * from "./EditResidenceSetting"; +export * from "./EditThemeSetting"; +export * from "./SettingsGroup"; +export * from "./SettingsItem"; diff --git a/src/routes/_authenticated/settings/account.tsx b/src/routes/_authenticated/settings/account.tsx index 10d38ef..ff7d3f2 100644 --- a/src/routes/_authenticated/settings/account.tsx +++ b/src/routes/_authenticated/settings/account.tsx @@ -1,251 +1,23 @@ import { PageHeader } from "@/components/app"; import { - Button, - Card, - Form, - Modal, - Select, - SelectItem, - Switch, - TextField, - ToggleButton, - ToggleButtonGroup, -} from "@/components/common"; -import { useTheme } from "@/utils/useTheme"; -import { useAuthActions } from "@convex-dev/auth/react"; + DeleteAccountSetting, + EditBirthplaceSetting, + EditMinorSetting, + EditNameSetting, + EditResidenceSetting, + EditThemeSetting, + SettingsGroup, +} from "@/components/settings"; import { api } from "@convex/_generated/api"; -import { JURISDICTIONS, type Jurisdiction, THEMES } from "@convex/constants"; import { createFileRoute } from "@tanstack/react-router"; -import { useMutation, useQuery } from "convex/react"; -import { Pencil, Trash } from "lucide-react"; -import { useState } from "react"; -import { Header } from "react-aria-components"; +import { useQuery } from "convex/react"; export const Route = createFileRoute("/_authenticated/settings/account")({ component: SettingsAccountRoute, }); -const SettingsSection = ({ - title, - children, -}: { - title: string; - children: React.ReactNode; -}) => ( -
-

{title}

- - {children} - -
-); - -const SettingsItem = ({ - label, - description, - children, -}: { - label: string; - description?: string; - children: React.ReactNode; -}) => ( -
-
-

{label}

- {description && ( -

- {description} -

- )} -
-
{children}
-
-); - -const EditNameDialog = ({ - defaultName, - isOpen, - onOpenChange, - onSubmit, -}: { - defaultName: string; - isOpen: boolean; - onOpenChange: (isOpen: boolean) => void; - onSubmit: () => void; -}) => { - const updateName = useMutation(api.users.setName); - const [name, setName] = useState(defaultName); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - updateName({ name }); - onSubmit(); - }; - - return ( - -
- Edit name - -
- - -
- -
- ); -}; - -const EditResidenceDialog = ({ - defaultResidence, - isOpen, - onOpenChange, - onSubmit, -}: { - defaultResidence: Jurisdiction; - isOpen: boolean; - onOpenChange: (isOpen: boolean) => void; - onSubmit: () => void; -}) => { - const updateResidence = useMutation(api.users.setResidence); - const [residence, setResidence] = useState(defaultResidence); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - updateResidence({ residence }); - onSubmit(); - }; - - return ( - -
- Edit residence - -
- - -
-
-
- ); -}; - -const EditBirthplaceDialog = ({ - defaultBirthplace, - isOpen, - onOpenChange, - onSubmit, -}: { - defaultBirthplace: Jurisdiction; - isOpen: boolean; - onOpenChange: (isOpen: boolean) => void; - onSubmit: () => void; -}) => { - const updateBirthplace = useMutation(api.users.setBirthplace); - const [birthplace, setBirthplace] = useState(defaultBirthplace); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - updateBirthplace({ birthplace }); - onSubmit(); - }; - - return ( - -
- Edit birthplace - -
- - -
-
-
- ); -}; - -const DeleteAccountDialog = ({ - isOpen, - onOpenChange, - onSubmit, -}: { - isOpen: boolean; - onOpenChange: (isOpen: boolean) => void; - onSubmit: () => void; -}) => { - const { signOut } = useAuthActions(); - const clearLocalStorage = () => { - localStorage.removeItem("theme"); - }; - const deleteAccount = useMutation(api.users.deleteCurrentUser); - - const handleSubmit = () => { - clearLocalStorage(); - deleteAccount(); - signOut(); - onSubmit(); - }; - - return ( - -
Delete account?
-

This will permanently erase your account and all data.

-
- - -
-
- ); -}; - function SettingsAccountRoute() { const user = useQuery(api.users.getCurrentUser); - const { themeSelection, setTheme } = useTheme(); - - const [isNameDialogOpen, setIsNameDialogOpen] = useState(false); - const [isDeleteAccountDialogOpen, setIsDeleteAccountDialogOpen] = - useState(false); - const [isResidenceDialogOpen, setIsResidenceDialogOpen] = useState(false); - const [isBirthplaceDialogOpen, setIsBirthplaceDialogOpen] = useState(false); - const updateIsMinor = useMutation(api.users.setCurrentUserIsMinor); return ( <> @@ -256,106 +28,18 @@ function SettingsAccountRoute() { "User not found, please reload" ) : ( <> - - - - setIsNameDialogOpen(false)} - /> - - - updateIsMinor({ isMinor: !user.isMinor })} - > - Is minor - - - - - setIsResidenceDialogOpen(false)} - /> - - - - setIsBirthplaceDialogOpen(false)} - /> - - - - - - {Object.entries(THEMES).map(([theme, details]) => ( - - {details.label} - - ))} - - - - - - - setIsDeleteAccountDialogOpen(false)} - /> - - + + + + + + + + + + + + )}