diff --git a/apps/client/src/components/aside/aside.tsx b/apps/client/src/components/layouts/dashboard/aside/aside.tsx similarity index 100% rename from apps/client/src/components/aside/aside.tsx rename to apps/client/src/components/layouts/dashboard/aside/aside.tsx diff --git a/apps/client/src/components/layouts/header.tsx b/apps/client/src/components/layouts/dashboard/header.tsx similarity index 100% rename from apps/client/src/components/layouts/header.tsx rename to apps/client/src/components/layouts/dashboard/header.tsx diff --git a/apps/client/src/components/layouts/shell.module.css b/apps/client/src/components/layouts/dashboard/shell.module.css similarity index 100% rename from apps/client/src/components/layouts/shell.module.css rename to apps/client/src/components/layouts/dashboard/shell.module.css diff --git a/apps/client/src/components/layouts/shell.tsx b/apps/client/src/components/layouts/dashboard/shell.tsx similarity index 100% rename from apps/client/src/components/layouts/shell.tsx rename to apps/client/src/components/layouts/dashboard/shell.tsx diff --git a/apps/client/src/components/emoji-picker.tsx b/apps/client/src/components/ui/emoji-picker.tsx similarity index 92% rename from apps/client/src/components/emoji-picker.tsx rename to apps/client/src/components/ui/emoji-picker.tsx index 5011bc3..8c81a87 100644 --- a/apps/client/src/components/emoji-picker.tsx +++ b/apps/client/src/components/ui/emoji-picker.tsx @@ -32,7 +32,7 @@ function EmojiPicker({ onEmojiSelect, icon, removeEmojiAction }: EmojiPickerInte position="bottom" > - + {icon} @@ -42,7 +42,7 @@ function EmojiPicker({ onEmojiSelect, icon, removeEmojiAction }: EmojiPickerInte skinTonePosition='search' theme={colorScheme} /> - - } - /> - - ); -} - diff --git a/apps/client/src/features/settings/account/settings/components/change-email.tsx b/apps/client/src/features/settings/account/settings/components/change-email.tsx deleted file mode 100644 index e10d481..0000000 --- a/apps/client/src/features/settings/account/settings/components/change-email.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Modal, TextInput, Button, Text, Group, PasswordInput } from '@mantine/core'; -import * as z from 'zod'; -import { useState } from 'react'; -import { useAtom } from 'jotai'; -import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import { useDisclosure } from '@mantine/hooks'; -import * as React from 'react'; -import { useForm, zodResolver } from '@mantine/form'; - - -export default function ChangeEmail() { - const [currentUser] = useAtom(currentUserAtom); - const [opened, { open, close }] = useDisclosure(false); - - return ( - -
- Email - - {currentUser.user.email} - -
- - - - - To change your email, you have to enter your password and new email. - - -
- ); -} - -const formSchema = z.object({ - email: z.string({ required_error: 'New email is required' }).email(), - password: z.string({ required_error: 'your current password is required' }).min(8), -}); - -type FormValues = z.infer - -function ChangePasswordForm() { - const [isLoading, setIsLoading] = useState(false); - - const form = useForm({ - validate: zodResolver(formSchema), - initialValues: { - password: '', - email: '', - }, - }); - - function handleSubmit(data: FormValues) { - setIsLoading(true); - console.log(data); - } - - return ( -
- - - - - - - - ); -} diff --git a/apps/client/src/features/settings/account/settings/components/change-password.tsx b/apps/client/src/features/settings/account/settings/components/change-password.tsx deleted file mode 100644 index 30534f6..0000000 --- a/apps/client/src/features/settings/account/settings/components/change-password.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Button, Group, Text, Modal, PasswordInput } from '@mantine/core'; -import * as z from 'zod'; -import { useState } from 'react'; -import { useDisclosure } from '@mantine/hooks'; -import * as React from 'react'; -import { useForm, zodResolver } from '@mantine/form'; - - -export default function ChangePassword() { - const [opened, { open, close }] = useDisclosure(false); - - return ( - -
- Password - - You can change your password here. - -
- - - - - Your password must be a minimum of 8 characters. - - - -
- ); -} - -const formSchema = z.object({ - current: z.string({ required_error: 'your current password is required' }).min(1), - password: z.string({ required_error: 'New password is required' }).min(8), - confirm_password: z.string({ required_error: 'Password confirmation is required' }).min(8), -}).refine(data => data.password === data.confirm_password, { - message: 'Your new password and confirmation does not match.', - path: ['confirm_password'], -}); - -type FormValues = z.infer - -function ChangePasswordForm() { - const [isLoading, setIsLoading] = useState(false); - - const form = useForm({ - validate: zodResolver(formSchema), - initialValues: { - current: '', - password: '', - confirm_password: '', - }, - }); - - function handleSubmit(data: FormValues) { - setIsLoading(true); - console.log(data); - } - - return ( -
- - - - - - - - ); -} diff --git a/apps/client/src/features/settings/workspace/members/components/workspace-invite-section.tsx b/apps/client/src/features/settings/workspace/members/components/workspace-invite-section.tsx deleted file mode 100644 index d357631..0000000 --- a/apps/client/src/features/settings/workspace/members/components/workspace-invite-section.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useAtom } from 'jotai'; -import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import React, { useEffect, useState } from 'react'; -import { Button, CopyButton, Text, TextInput } from '@mantine/core'; - -export default function WorkspaceInviteSection() { - const [currentUser] = useAtom(currentUserAtom); - const [inviteLink, setInviteLink] = useState(''); - - useEffect(() => { - setInviteLink(`${window.location.origin}/invite/${currentUser.workspace.inviteCode}`); - }, [currentUser.workspace.inviteCode]); - - return ( - <> -
- Invite link - - Anyone with this link can join this workspace. - -
- - - {({ copied, copy }) => ( - - )} - - } - /> - - - - ); -} diff --git a/apps/client/src/features/settings/workspace/members/components/workspace-members-table.tsx b/apps/client/src/features/settings/workspace/members/components/workspace-members-table.tsx deleted file mode 100644 index 3c38d8e..0000000 --- a/apps/client/src/features/settings/workspace/members/components/workspace-members-table.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useAtom } from 'jotai'; -import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import { useQuery } from '@tanstack/react-query'; -import { getWorkspaceUsers } from '@/features/workspace/services/workspace-service'; -import { Group, Table, Avatar, Text, Badge } from '@mantine/core'; - -export default function WorkspaceMembersTable() { - const [currentUser] = useAtom(currentUserAtom); - - const workspaceUsers = useQuery({ - queryKey: ['workspaceUsers', currentUser.workspace.id], - queryFn: async () => { - return await getWorkspaceUsers(); - }, - }); - - const { data, isLoading, isSuccess } = workspaceUsers; - - return ( - <> - {isSuccess && - - - - - Name - Status - Role - - - - - - { - data['users']?.map((user, index) => ( - - - - - -
- - {user.name} - - - {user.email} - -
-
-
- - - - Active - - - - {user.workspaceRole} -
- )) - } - -
-
- } - - ); -} diff --git a/apps/client/src/features/settings/workspace/members/workspace-members.tsx b/apps/client/src/features/settings/workspace/members/workspace-members.tsx deleted file mode 100644 index 5be83fa..0000000 --- a/apps/client/src/features/settings/workspace/members/workspace-members.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import WorkspaceInviteSection from '@/features/settings/workspace/members/components/workspace-invite-section'; -import React from 'react'; -import WorkspaceInviteModal from '@/features/settings/workspace/members/components/workspace-invite-modal'; -import { Divider, Group, Space, Text } from '@mantine/core'; - -const WorkspaceMembersTable = React.lazy(() => import('@/features/settings/workspace/members/components/workspace-members-table')); - -export default function WorkspaceMembers() { - return ( - <> - - - - - - Members - - - - - - - - - - - ); -} diff --git a/apps/client/src/features/settings/workspace/settings/components/workspace-name-form.tsx b/apps/client/src/features/settings/workspace/settings/components/workspace-name-form.tsx deleted file mode 100644 index 7649d85..0000000 --- a/apps/client/src/features/settings/workspace/settings/components/workspace-name-form.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import { useAtom } from 'jotai'; -import * as z from 'zod'; -import { useState } from 'react'; -import { focusAtom } from 'jotai-optics'; -import { updateWorkspace } from '@/features/workspace/services/workspace-service'; -import { IWorkspace } from '@/features/workspace/types/workspace.types'; -import { TextInput, Button } from '@mantine/core'; -import { useForm, zodResolver } from '@mantine/form'; -import { notifications } from '@mantine/notifications'; - -const formSchema = z.object({ - name: z.string().nonempty('Workspace name cannot be blank'), -}); - -type FormValues = z.infer; - -const workspaceAtom = focusAtom(currentUserAtom, (optic) => optic.prop('workspace')); - -export default function WorkspaceNameForm() { - const [isLoading, setIsLoading] = useState(false); - const [currentUser] = useAtom(currentUserAtom); - const [, setWorkspace] = useAtom(workspaceAtom); - - const form = useForm({ - validate: zodResolver(formSchema), - initialValues: { - name: currentUser?.workspace?.name, - }, - }); - - async function handleSubmit(data: Partial) { - setIsLoading(true); - - try { - const updatedWorkspace = await updateWorkspace(data); - setWorkspace(updatedWorkspace); - notifications.show({ message: 'Updated successfully' }); - } catch (err) { - console.log(err); - notifications.show({ - message: 'Failed to update data', - color: 'red', - }); - } - setIsLoading(false); - } - - return ( -
- - Save - - } - /> - - ); -} diff --git a/apps/client/src/features/settings/workspace/settings/workspace-settings.tsx b/apps/client/src/features/settings/workspace/settings/workspace-settings.tsx deleted file mode 100644 index 1c80cb1..0000000 --- a/apps/client/src/features/settings/workspace/settings/workspace-settings.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import WorkspaceNameForm from '@/features/settings/workspace/settings/components/workspace-name-form'; - -export default function WorkspaceSettings() { - - return (); -} diff --git a/apps/client/src/features/settings/workspace/members/components/workspace-invite-form.tsx b/apps/client/src/features/workspace/components/members/components/workspace-invite-form.tsx similarity index 71% rename from apps/client/src/features/settings/workspace/members/components/workspace-invite-form.tsx rename to apps/client/src/features/workspace/components/members/components/workspace-invite-form.tsx index 0080916..0e6e612 100644 --- a/apps/client/src/features/settings/workspace/members/components/workspace-invite-form.tsx +++ b/apps/client/src/features/workspace/components/members/components/workspace-invite-form.tsx @@ -1,23 +1,14 @@ -import { - Group, - Box, - Text, - Button, - TagsInput, - Space, Select, -} from '@mantine/core'; -import WorkspaceInviteSection from '@/features/settings/workspace/members/components/workspace-invite-section'; -import React from 'react'; +import { Group, Box, Button, TagsInput, Space, Select } from "@mantine/core"; +import WorkspaceInviteSection from "@/features/settings/workspace/members/components/workspace-invite-section"; +import React from "react"; enum UserRole { - GUEST = 'Guest', - MEMBER = 'Member', - OWNER = 'Owner', + OWNER = "Owner", + ADMIN = "Admin", + MEMBER = "Member", } - export function WorkspaceInviteForm() { - function handleSubmit(data) { console.log(data); } @@ -25,7 +16,6 @@ export function WorkspaceInviteForm() { return ( <> - @@ -35,7 +25,7 @@ export function WorkspaceInviteForm() { label="Invite from email" placeholder="enter valid emails addresses" variant="filled" - splitChars={[',', ' ']} + splitChars={[",", " "]} maxDropdownHeight={200} maxTags={50} /> @@ -54,12 +44,9 @@ export function WorkspaceInviteForm() { /> - + - ); - } diff --git a/apps/client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx b/apps/client/src/features/workspace/components/members/components/workspace-invite-modal.tsx similarity index 71% rename from apps/client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx rename to apps/client/src/features/workspace/components/members/components/workspace-invite-modal.tsx index 623be3a..20f4cf6 100644 --- a/apps/client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx +++ b/apps/client/src/features/workspace/components/members/components/workspace-invite-modal.tsx @@ -1,6 +1,5 @@ -import { IconUserPlus } from '@tabler/icons-react'; import { WorkspaceInviteForm } from '@/features/settings/workspace/members/components/workspace-invite-form'; -import { Button, Divider, Modal, ScrollArea, Text } from '@mantine/core'; +import { Button, Divider, Modal, ScrollArea } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; export default function WorkspaceInviteModal() { @@ -8,8 +7,8 @@ export default function WorkspaceInviteModal() { return ( <> - diff --git a/apps/client/src/features/workspace/components/members/components/workspace-invite-section.tsx b/apps/client/src/features/workspace/components/members/components/workspace-invite-section.tsx new file mode 100644 index 0000000..bc694a5 --- /dev/null +++ b/apps/client/src/features/workspace/components/members/components/workspace-invite-section.tsx @@ -0,0 +1,41 @@ +import { useAtom } from "jotai"; +import { currentUserAtom } from "@/features/user/atoms/current-user-atom"; +import { useEffect, useState } from "react"; +import { Button, CopyButton, Group, Text, TextInput } from "@mantine/core"; + +export default function WorkspaceInviteSection() { + const [currentUser] = useAtom(currentUserAtom); + const [inviteLink, setInviteLink] = useState(""); + + useEffect(() => { + setInviteLink( + `${window.location.origin}/invite/${currentUser.workspace.inviteCode}`, + ); + }, [currentUser.workspace.inviteCode]); + + return ( + <> +
+ + Invite link + + + Anyone with this link can join this workspace. + +
+ + +
+ +
+ + {({ copied, copy }) => ( + + )} + +
+ + ); +} diff --git a/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx new file mode 100644 index 0000000..d85da11 --- /dev/null +++ b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx @@ -0,0 +1,50 @@ +import { Group, Table, Avatar, Text, Badge } from "@mantine/core"; +import { useWorkspaceMembersQuery } from "@/features/workspace/queries/workspace-query"; +import { UserAvatar } from "@/components/ui/user-avatar.tsx"; +import React from "react"; + +export default function WorkspaceMembersTable() { + const { data, isLoading } = useWorkspaceMembersQuery(); + + return ( + <> + {data && ( + + + + User + Status + Role + + + + + {data?.items.map((user, index) => ( + + + + +
+ + {user.name} + + + {user.email} + +
+
+
+ + + Active + + + {user.role} +
+ ))} +
+
+ )} + + ); +} diff --git a/apps/client/src/features/workspace/components/settings/components/workspace-name-form.tsx b/apps/client/src/features/workspace/components/settings/components/workspace-name-form.tsx new file mode 100644 index 0000000..ce06b4a --- /dev/null +++ b/apps/client/src/features/workspace/components/settings/components/workspace-name-form.tsx @@ -0,0 +1,65 @@ +import { currentUserAtom } from "@/features/user/atoms/current-user-atom"; +import { useAtom } from "jotai"; +import * as z from "zod"; +import { useState } from "react"; +import { focusAtom } from "jotai-optics"; +import { updateWorkspace } from "@/features/workspace/services/workspace-service"; +import { IWorkspace } from "@/features/workspace/types/workspace.types"; +import { TextInput, Button } from "@mantine/core"; +import { useForm, zodResolver } from "@mantine/form"; +import { notifications } from "@mantine/notifications"; + +const formSchema = z.object({ + name: z.string().nonempty("Workspace name cannot be blank"), +}); + +type FormValues = z.infer; + +const workspaceAtom = focusAtom(currentUserAtom, (optic) => + optic.prop("workspace"), +); + +export default function WorkspaceNameForm() { + const [isLoading, setIsLoading] = useState(false); + const [currentUser] = useAtom(currentUserAtom); + const [, setWorkspace] = useAtom(workspaceAtom); + + const form = useForm({ + validate: zodResolver(formSchema), + initialValues: { + name: currentUser?.workspace?.name, + }, + }); + + async function handleSubmit(data: Partial) { + setIsLoading(true); + + try { + const updatedWorkspace = await updateWorkspace(data); + setWorkspace(updatedWorkspace); + notifications.show({ message: "Updated successfully" }); + } catch (err) { + console.log(err); + notifications.show({ + message: "Failed to update data", + color: "red", + }); + } + setIsLoading(false); + } + + return ( +
+ + + + ); +}