Skip to content

Commit

Permalink
refactor: optimize states in users/tasks context (#71)
Browse files Browse the repository at this point in the history
* refactor: optimize states in tasks context

* refactor: optimize states in users context
  • Loading branch information
satnaing authored Dec 27, 2024
1 parent c47d3df commit 7e965da
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 183 deletions.
4 changes: 2 additions & 2 deletions src/features/tasks/components/data-table-row-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { useTasksContext } from '../context/tasks-context'
import { useTasks } from '../context/tasks-context'
import { labels } from '../data/data'
import { taskSchema } from '../data/schema'

Expand All @@ -28,7 +28,7 @@ export function DataTableRowActions<TData>({
}: DataTableRowActionsProps<TData>) {
const task = taskSchema.parse(row.original)

const { setOpen, setCurrentRow } = useTasksContext()
const { setOpen, setCurrentRow } = useTasks()

return (
<DropdownMenu modal={false}>
Expand Down
78 changes: 78 additions & 0 deletions src/features/tasks/components/tasks-dialogs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { toast } from '@/hooks/use-toast'
import { ConfirmDialog } from '@/components/confirm-dialog'
import { useTasks } from '../context/tasks-context'
import { TasksImportDialog } from './tasks-import-dialog'
import { TasksMutateDrawer } from './tasks-mutate-drawer'

export function TasksDialogs() {
const { open, setOpen, currentRow, setCurrentRow } = useTasks()
return (
<>
<TasksMutateDrawer
key='task-create'
open={open === 'create'}
onOpenChange={() => setOpen('create')}
/>

<TasksImportDialog
key='tasks-import'
open={open === 'import'}
onOpenChange={() => setOpen('import')}
/>

{currentRow && (
<>
<TasksMutateDrawer
key={`task-update-${currentRow.id}`}
open={open === 'update'}
onOpenChange={() => {
setOpen('update')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
currentRow={currentRow}
/>

<ConfirmDialog
key='task-delete'
destructive
open={open === 'delete'}
onOpenChange={() => {
setOpen('delete')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
handleConfirm={() => {
setOpen(null)
setTimeout(() => {
setCurrentRow(null)
}, 500)
toast({
title: 'The following task has been deleted:',
description: (
<pre className='mt-2 w-[340px] rounded-md bg-slate-950 p-4'>
<code className='text-white'>
{JSON.stringify(currentRow, null, 2)}
</code>
</pre>
),
})
}}
className='max-w-md'
title={`Delete this task: ${currentRow.id} ?`}
desc={
<>
You are about to delete a task with the ID{' '}
<strong>{currentRow.id}</strong>. <br />
This action cannot be undone.
</>
}
confirmText='Delete'
/>
</>
)}
</>
)
}
21 changes: 21 additions & 0 deletions src/features/tasks/components/tasks-primary-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IconDownload, IconPlus } from '@tabler/icons-react'
import { Button } from '@/components/ui/button'
import { useTasks } from '../context/tasks-context'

export function TasksPrimaryButtons() {
const { setOpen } = useTasks()
return (
<div className='flex gap-2'>
<Button
variant='outline'
className='space-x-1'
onClick={() => setOpen('import')}
>
<span>Import</span> <IconDownload size={18} />
</Button>
<Button className='space-x-1' onClick={() => setOpen('create')}>
<span>Create</span> <IconPlus size={18} />
</Button>
</div>
)
}
20 changes: 12 additions & 8 deletions src/features/tasks/context/tasks-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import React, { useState } from 'react'
import useDialogState from '@/hooks/use-dialog-state'
import { Task } from '../data/schema'

export type TasksDialogType = 'create' | 'update' | 'delete' | 'import'
Expand All @@ -14,21 +15,24 @@ const TasksContext = React.createContext<TasksContextType | null>(null)

interface Props {
children: React.ReactNode
value: TasksContextType
}

export default function TasksContextProvider({ children, value }: Props) {
return <TasksContext.Provider value={value}>{children}</TasksContext.Provider>
export default function TasksProvider({ children }: Props) {
const [open, setOpen] = useDialogState<TasksDialogType>(null)
const [currentRow, setCurrentRow] = useState<Task | null>(null)
return (
<TasksContext value={{ open, setOpen, currentRow, setCurrentRow }}>
{children}
</TasksContext>
)
}

// eslint-disable-next-line react-refresh/only-export-components
export const useTasksContext = () => {
export const useTasks = () => {
const tasksContext = React.useContext(TasksContext)

if (!tasksContext) {
throw new Error(
'useTasksContext has to be used within <TasksContext.Provider>'
)
throw new Error('useTasks has to be used within <TasksContext>')
}

return tasksContext
Expand Down
101 changes: 7 additions & 94 deletions src/features/tasks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
import { useState } from 'react'
import { IconDownload, IconPlus } from '@tabler/icons-react'
import useDialogState from '@/hooks/use-dialog-state'
import { toast } from '@/hooks/use-toast'
import { Button } from '@/components/ui/button'
import { ConfirmDialog } from '@/components/confirm-dialog'
import { Header } from '@/components/layout/header'
import { Main } from '@/components/layout/main'
import { ProfileDropdown } from '@/components/profile-dropdown'
import { Search } from '@/components/search'
import { ThemeSwitch } from '@/components/theme-switch'
import { columns } from './components/columns'
import { DataTable } from './components/data-table'
import { TasksImportDialog } from './components/tasks-import-dialog'
import { TasksMutateDrawer } from './components/tasks-mutate-drawer'
import TasksContextProvider, { TasksDialogType } from './context/tasks-context'
import { Task } from './data/schema'
import { TasksDialogs } from './components/tasks-dialogs'
import { TasksPrimaryButtons } from './components/tasks-primary-buttons'
import TasksProvider from './context/tasks-context'
import { tasks } from './data/tasks'

export default function Tasks() {
// Local states
const [currentRow, setCurrentRow] = useState<Task | null>(null)
const [open, setOpen] = useDialogState<TasksDialogType>(null)

return (
<TasksContextProvider value={{ open, setOpen, currentRow, setCurrentRow }}>
{/* ===== Top Heading ===== */}
<TasksProvider>
<Header fixed>
<Search />
<div className='ml-auto flex items-center space-x-4'>
Expand All @@ -41,89 +29,14 @@ export default function Tasks() {
Here&apos;s a list of your tasks for this month!
</p>
</div>
<div className='flex gap-2'>
<Button
variant='outline'
className='space-x-1'
onClick={() => setOpen('import')}
>
<span>Import</span> <IconDownload size={18} />
</Button>
<Button className='space-x-1' onClick={() => setOpen('create')}>
<span>Create</span> <IconPlus size={18} />
</Button>
</div>
<TasksPrimaryButtons />
</div>
<div className='-mx-4 flex-1 overflow-auto px-4 py-1 lg:flex-row lg:space-x-12 lg:space-y-0'>
<DataTable data={tasks} columns={columns} />
</div>
</Main>

<TasksMutateDrawer
key='task-create'
open={open === 'create'}
onOpenChange={() => setOpen('create')}
/>

<TasksImportDialog
key='tasks-import'
open={open === 'import'}
onOpenChange={() => setOpen('import')}
/>

{currentRow && (
<>
<TasksMutateDrawer
key={`task-update-${currentRow.id}`}
open={open === 'update'}
onOpenChange={() => {
setOpen('update')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
currentRow={currentRow}
/>

<ConfirmDialog
key='task-delete'
destructive
open={open === 'delete'}
onOpenChange={() => {
setOpen('delete')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
handleConfirm={() => {
setOpen(null)
setTimeout(() => {
setCurrentRow(null)
}, 500)
toast({
title: 'The following task has been deleted:',
description: (
<pre className='mt-2 w-[340px] rounded-md bg-slate-950 p-4'>
<code className='text-white'>
{JSON.stringify(currentRow, null, 2)}
</code>
</pre>
),
})
}}
className='max-w-md'
title={`Delete this task: ${currentRow.id} ?`}
desc={
<>
You are about to delete a task with the ID{' '}
<strong>{currentRow.id}</strong>. <br />
This action cannot be undone.
</>
}
confirmText='Delete'
/>
</>
)}
</TasksContextProvider>
<TasksDialogs />
</TasksProvider>
)
}
4 changes: 2 additions & 2 deletions src/features/users/components/data-table-row-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import {
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { useUsersContext } from '../context/users-context'
import { useUsers } from '../context/users-context'
import { User } from '../data/schema'

interface DataTableRowActionsProps {
row: Row<User>
}

export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { setOpen, setCurrentRow } = useUsersContext()
const { setOpen, setCurrentRow } = useUsers()
return (
<>
<DropdownMenu modal={false}>
Expand Down
51 changes: 51 additions & 0 deletions src/features/users/components/users-dialogs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useUsers } from '../context/users-context'
import { UsersActionDialog } from './users-action-dialog'
import { UsersDeleteDialog } from './users-delete-dialog'
import { UsersInviteDialog } from './users-invite-dialog'

export function UsersDialogs() {
const { open, setOpen, currentRow, setCurrentRow } = useUsers()
return (
<>
<UsersActionDialog
key='user-add'
open={open === 'add'}
onOpenChange={() => setOpen('add')}
/>

<UsersInviteDialog
key='user-invite'
open={open === 'invite'}
onOpenChange={() => setOpen('invite')}
/>

{currentRow && (
<>
<UsersActionDialog
key={`user-edit-${currentRow.id}`}
open={open === 'edit'}
onOpenChange={() => {
setOpen('edit')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
currentRow={currentRow}
/>

<UsersDeleteDialog
key={`user-delete-${currentRow.id}`}
open={open === 'delete'}
onOpenChange={() => {
setOpen('delete')
setTimeout(() => {
setCurrentRow(null)
}, 500)
}}
currentRow={currentRow}
/>
</>
)}
</>
)
}
21 changes: 21 additions & 0 deletions src/features/users/components/users-primary-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IconMailPlus, IconUserPlus } from '@tabler/icons-react'
import { Button } from '@/components/ui/button'
import { useUsers } from '../context/users-context'

export function UsersPrimaryButtons() {
const { setOpen } = useUsers()
return (
<div className='flex gap-2'>
<Button
variant='outline'
className='space-x-1'
onClick={() => setOpen('invite')}
>
<span>Invite User</span> <IconMailPlus size={18} />
</Button>
<Button className='space-x-1' onClick={() => setOpen('add')}>
<span>Add User</span> <IconUserPlus size={18} />
</Button>
</div>
)
}
Loading

0 comments on commit 7e965da

Please sign in to comment.