ar-saas 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/package.json +1 -1
  2. package/templates/backend/.env.example +13 -3
  3. package/templates/backend/README.md +22 -3
  4. package/templates/backend/package-lock.json +165 -2
  5. package/templates/backend/package.json +2 -0
  6. package/templates/backend/src/app.module.ts +14 -0
  7. package/templates/backend/src/common/guards/github-auth.guard.ts +5 -0
  8. package/templates/backend/src/main.ts +2 -2
  9. package/templates/backend/src/modules/auth/auth.controller.ts +51 -3
  10. package/templates/backend/src/modules/auth/auth.module.ts +2 -1
  11. package/templates/backend/src/modules/auth/auth.service.ts +96 -11
  12. package/templates/backend/src/modules/auth/strategies/github.strategy.ts +46 -0
  13. package/templates/backend/src/modules/clients/clients.controller.ts +91 -0
  14. package/templates/backend/src/modules/clients/clients.module.ts +16 -0
  15. package/templates/backend/src/modules/clients/clients.repository.ts +14 -0
  16. package/templates/backend/src/modules/clients/clients.service.ts +52 -0
  17. package/templates/backend/src/modules/clients/dto/create-client.dto.ts +40 -0
  18. package/templates/backend/src/modules/clients/dto/query-client.dto.ts +30 -0
  19. package/templates/backend/src/modules/clients/dto/update-client.dto.ts +4 -0
  20. package/templates/backend/src/modules/clients/schemas/client.schema.ts +32 -0
  21. package/templates/backend/src/modules/invoices/dto/create-invoice.dto.ts +79 -0
  22. package/templates/backend/src/modules/invoices/dto/invoice-item.dto.ts +23 -0
  23. package/templates/backend/src/modules/invoices/dto/query-invoice.dto.ts +40 -0
  24. package/templates/backend/src/modules/invoices/dto/update-invoice.dto.ts +4 -0
  25. package/templates/backend/src/modules/invoices/invoices.controller.ts +91 -0
  26. package/templates/backend/src/modules/invoices/invoices.module.ts +18 -0
  27. package/templates/backend/src/modules/invoices/invoices.repository.ts +14 -0
  28. package/templates/backend/src/modules/invoices/invoices.service.ts +104 -0
  29. package/templates/backend/src/modules/invoices/schemas/invoice.schema.ts +75 -0
  30. package/templates/backend/src/modules/notifications/dto/create-notification.dto.ts +45 -0
  31. package/templates/backend/src/modules/notifications/dto/query-notification.dto.ts +30 -0
  32. package/templates/backend/src/modules/notifications/dto/update-notification.dto.ts +4 -0
  33. package/templates/backend/src/modules/notifications/notifications.controller.ts +119 -0
  34. package/templates/backend/src/modules/notifications/notifications.module.ts +16 -0
  35. package/templates/backend/src/modules/notifications/notifications.repository.ts +31 -0
  36. package/templates/backend/src/modules/notifications/notifications.service.ts +64 -0
  37. package/templates/backend/src/modules/notifications/schemas/notification.schema.ts +38 -0
  38. package/templates/backend/src/modules/pipeline/dto/create-deal.dto.ts +40 -0
  39. package/templates/backend/src/modules/pipeline/dto/query-deal.dto.ts +35 -0
  40. package/templates/backend/src/modules/pipeline/dto/update-deal.dto.ts +4 -0
  41. package/templates/backend/src/modules/pipeline/pipeline.controller.ts +91 -0
  42. package/templates/backend/src/modules/pipeline/pipeline.module.ts +18 -0
  43. package/templates/backend/src/modules/pipeline/pipeline.repository.ts +14 -0
  44. package/templates/backend/src/modules/pipeline/pipeline.service.ts +64 -0
  45. package/templates/backend/src/modules/pipeline/schemas/deal.schema.ts +39 -0
  46. package/templates/backend/src/modules/planner/dto/create-planner-block.dto.ts +66 -0
  47. package/templates/backend/src/modules/planner/dto/query-planner-block.dto.ts +48 -0
  48. package/templates/backend/src/modules/planner/dto/update-block-status.dto.ts +10 -0
  49. package/templates/backend/src/modules/planner/dto/update-planner-block.dto.ts +4 -0
  50. package/templates/backend/src/modules/planner/planner.controller.ts +124 -0
  51. package/templates/backend/src/modules/planner/planner.module.ts +16 -0
  52. package/templates/backend/src/modules/planner/planner.repository.ts +45 -0
  53. package/templates/backend/src/modules/planner/planner.service.ts +104 -0
  54. package/templates/backend/src/modules/planner/schemas/planner-block.schema.ts +56 -0
  55. package/templates/backend/src/modules/task-columns/dto/create-task-column.dto.ts +20 -0
  56. package/templates/backend/src/modules/task-columns/dto/reorder-columns.dto.ts +9 -0
  57. package/templates/backend/src/modules/task-columns/dto/update-task-column.dto.ts +4 -0
  58. package/templates/backend/src/modules/task-columns/schemas/task-column.schema.ts +21 -0
  59. package/templates/backend/src/modules/task-columns/task-columns.controller.ts +86 -0
  60. package/templates/backend/src/modules/task-columns/task-columns.module.ts +16 -0
  61. package/templates/backend/src/modules/task-columns/task-columns.repository.ts +15 -0
  62. package/templates/backend/src/modules/task-columns/task-columns.service.ts +49 -0
  63. package/templates/backend/src/modules/tasks/dto/checklist-item.dto.ts +13 -0
  64. package/templates/backend/src/modules/tasks/dto/create-task.dto.ts +67 -0
  65. package/templates/backend/src/modules/tasks/dto/label.dto.ts +12 -0
  66. package/templates/backend/src/modules/tasks/dto/move-task.dto.ts +15 -0
  67. package/templates/backend/src/modules/tasks/dto/query-task.dto.ts +40 -0
  68. package/templates/backend/src/modules/tasks/dto/update-task.dto.ts +4 -0
  69. package/templates/backend/src/modules/tasks/schemas/task.schema.ts +66 -0
  70. package/templates/backend/src/modules/tasks/tasks.controller.ts +104 -0
  71. package/templates/backend/src/modules/tasks/tasks.module.ts +18 -0
  72. package/templates/backend/src/modules/tasks/tasks.repository.ts +14 -0
  73. package/templates/backend/src/modules/tasks/tasks.service.ts +76 -0
  74. package/templates/backend/src/modules/users/schemas/user.schema.ts +3 -0
  75. package/templates/backend/src/modules/users/users.repository.ts +8 -0
  76. package/templates/backend/src/modules/users/users.service.ts +34 -0
  77. package/templates/frontend/.env.local.example +1 -1
  78. package/templates/frontend/README.md +43 -1
  79. package/templates/frontend/package.json +48 -45
  80. package/templates/frontend/pnpm-lock.yaml +5096 -5012
  81. package/templates/frontend/src/app/(auth)/layout.tsx +7 -1
  82. package/templates/frontend/src/app/(auth)/login/page.tsx +13 -0
  83. package/templates/frontend/src/app/(auth)/register/page.tsx +13 -0
  84. package/templates/frontend/src/app/(dashboard)/clients/page.tsx +295 -0
  85. package/templates/frontend/src/app/(dashboard)/invoices/page.tsx +305 -0
  86. package/templates/frontend/src/app/(dashboard)/notifications/page.tsx +173 -0
  87. package/templates/frontend/src/app/(dashboard)/pipeline/page.tsx +244 -0
  88. package/templates/frontend/src/app/(dashboard)/planner/page.tsx +287 -0
  89. package/templates/frontend/src/app/(dashboard)/settings/page.tsx +165 -128
  90. package/templates/frontend/src/app/(dashboard)/tasks/page.tsx +366 -0
  91. package/templates/frontend/src/app/auth/github/callback/page.tsx +82 -0
  92. package/templates/frontend/src/app/landing/page.tsx +21 -0
  93. package/templates/frontend/src/app/page.tsx +5 -5
  94. package/templates/frontend/src/app/setup/page.tsx +15 -14
  95. package/templates/frontend/src/components/auth/github-button.tsx +25 -0
  96. package/templates/frontend/src/components/dashboard/sidebar.tsx +90 -71
  97. package/templates/frontend/src/components/ui/alert-dialog.tsx +141 -0
  98. package/templates/frontend/src/components/ui/button.tsx +56 -52
  99. package/templates/frontend/src/components/ui/popover.tsx +31 -0
  100. package/templates/frontend/src/components/ui/select.tsx +160 -0
  101. package/templates/frontend/src/components/ui/sheet.tsx +140 -0
  102. package/templates/frontend/src/lib/api/auth.ts +7 -0
  103. package/templates/frontend/src/lib/api/clients.ts +17 -0
  104. package/templates/frontend/src/lib/api/invoices.ts +18 -0
  105. package/templates/frontend/src/lib/api/notifications.ts +27 -0
  106. package/templates/frontend/src/lib/api/pipeline.ts +18 -0
  107. package/templates/frontend/src/lib/api/planner.ts +26 -0
  108. package/templates/frontend/src/lib/api/task-columns.ts +17 -0
  109. package/templates/frontend/src/lib/api/tasks.ts +21 -0
  110. package/templates/frontend/src/lib/hooks/use-unread-notifications.ts +23 -0
  111. package/templates/frontend/src/providers/auth-provider.tsx +7 -1
  112. package/templates/frontend/src/types/clients.ts +38 -0
  113. package/templates/frontend/src/types/invoices.ts +51 -0
  114. package/templates/frontend/src/types/notifications.ts +30 -0
  115. package/templates/frontend/src/types/pipeline.ts +35 -0
  116. package/templates/frontend/src/types/planner.ts +49 -0
  117. package/templates/frontend/src/types/tasks.ts +65 -0
@@ -0,0 +1,140 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SheetPrimitive from "@radix-ui/react-dialog"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { X } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+
10
+ const Sheet = SheetPrimitive.Root
11
+
12
+ const SheetTrigger = SheetPrimitive.Trigger
13
+
14
+ const SheetClose = SheetPrimitive.Close
15
+
16
+ const SheetPortal = SheetPrimitive.Portal
17
+
18
+ const SheetOverlay = React.forwardRef<
19
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
20
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
21
+ >(({ className, ...props }, ref) => (
22
+ <SheetPrimitive.Overlay
23
+ className={cn(
24
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25
+ className
26
+ )}
27
+ {...props}
28
+ ref={ref}
29
+ />
30
+ ))
31
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32
+
33
+ const sheetVariants = cva(
34
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35
+ {
36
+ variants: {
37
+ side: {
38
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39
+ bottom:
40
+ "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42
+ right:
43
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ side: "right",
48
+ },
49
+ }
50
+ )
51
+
52
+ interface SheetContentProps
53
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
54
+ VariantProps<typeof sheetVariants> {}
55
+
56
+ const SheetContent = React.forwardRef<
57
+ React.ElementRef<typeof SheetPrimitive.Content>,
58
+ SheetContentProps
59
+ >(({ side = "right", className, children, ...props }, ref) => (
60
+ <SheetPortal>
61
+ <SheetOverlay />
62
+ <SheetPrimitive.Content
63
+ ref={ref}
64
+ className={cn(sheetVariants({ side }), className)}
65
+ {...props}
66
+ >
67
+ {children}
68
+ <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
69
+ <X className="h-4 w-4" />
70
+ <span className="sr-only">Close</span>
71
+ </SheetPrimitive.Close>
72
+ </SheetPrimitive.Content>
73
+ </SheetPortal>
74
+ ))
75
+ SheetContent.displayName = SheetPrimitive.Content.displayName
76
+
77
+ const SheetHeader = ({
78
+ className,
79
+ ...props
80
+ }: React.HTMLAttributes<HTMLDivElement>) => (
81
+ <div
82
+ className={cn(
83
+ "flex flex-col space-y-2 text-center sm:text-left",
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ )
89
+ SheetHeader.displayName = "SheetHeader"
90
+
91
+ const SheetFooter = ({
92
+ className,
93
+ ...props
94
+ }: React.HTMLAttributes<HTMLDivElement>) => (
95
+ <div
96
+ className={cn(
97
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ )
103
+ SheetFooter.displayName = "SheetFooter"
104
+
105
+ const SheetTitle = React.forwardRef<
106
+ React.ElementRef<typeof SheetPrimitive.Title>,
107
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
108
+ >(({ className, ...props }, ref) => (
109
+ <SheetPrimitive.Title
110
+ ref={ref}
111
+ className={cn("text-lg font-semibold text-foreground", className)}
112
+ {...props}
113
+ />
114
+ ))
115
+ SheetTitle.displayName = SheetPrimitive.Title.displayName
116
+
117
+ const SheetDescription = React.forwardRef<
118
+ React.ElementRef<typeof SheetPrimitive.Description>,
119
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
120
+ >(({ className, ...props }, ref) => (
121
+ <SheetPrimitive.Description
122
+ ref={ref}
123
+ className={cn("text-sm text-muted-foreground", className)}
124
+ {...props}
125
+ />
126
+ ))
127
+ SheetDescription.displayName = SheetPrimitive.Description.displayName
128
+
129
+ export {
130
+ Sheet,
131
+ SheetPortal,
132
+ SheetOverlay,
133
+ SheetTrigger,
134
+ SheetClose,
135
+ SheetContent,
136
+ SheetHeader,
137
+ SheetFooter,
138
+ SheetTitle,
139
+ SheetDescription,
140
+ }
@@ -36,4 +36,11 @@ export const authApi = {
36
36
  getMe() {
37
37
  return apiClient.get<never, User>('/api/auth/me')
38
38
  },
39
+
40
+ exchangeGithubCode(code: string) {
41
+ return apiClient.post<never, { success: boolean; alreadyExisted: boolean }>(
42
+ '/api/auth/github/exchange',
43
+ { code },
44
+ )
45
+ },
39
46
  }
@@ -0,0 +1,17 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { Client, CreateClientDto, PaginatedResult, QueryClientDto, UpdateClientDto } from '@/types/clients'
3
+
4
+ export const getClients = (params?: QueryClientDto): Promise<PaginatedResult<Client>> =>
5
+ apiClient.get('/api/clients', { params }) as Promise<PaginatedResult<Client>>
6
+
7
+ export const getClient = (id: string): Promise<Client> =>
8
+ apiClient.get(`/api/clients/${id}`) as Promise<Client>
9
+
10
+ export const createClient = (dto: CreateClientDto): Promise<Client> =>
11
+ apiClient.post('/api/clients', dto) as Promise<Client>
12
+
13
+ export const updateClient = (id: string, dto: UpdateClientDto): Promise<Client> =>
14
+ apiClient.patch(`/api/clients/${id}`, dto) as Promise<Client>
15
+
16
+ export const deleteClient = (id: string): Promise<void> =>
17
+ apiClient.delete(`/api/clients/${id}`) as Promise<void>
@@ -0,0 +1,18 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreateInvoiceDto, Invoice, QueryInvoiceDto, UpdateInvoiceDto } from '@/types/invoices'
3
+ import type { PaginatedResult } from '@/types/clients'
4
+
5
+ export const getInvoices = (params?: QueryInvoiceDto): Promise<PaginatedResult<Invoice>> =>
6
+ apiClient.get('/api/invoices', { params }) as Promise<PaginatedResult<Invoice>>
7
+
8
+ export const getInvoice = (id: string): Promise<Invoice> =>
9
+ apiClient.get(`/api/invoices/${id}`) as Promise<Invoice>
10
+
11
+ export const createInvoice = (dto: CreateInvoiceDto): Promise<Invoice> =>
12
+ apiClient.post('/api/invoices', dto) as Promise<Invoice>
13
+
14
+ export const updateInvoice = (id: string, dto: UpdateInvoiceDto): Promise<Invoice> =>
15
+ apiClient.patch(`/api/invoices/${id}`, dto) as Promise<Invoice>
16
+
17
+ export const deleteInvoice = (id: string): Promise<void> =>
18
+ apiClient.delete(`/api/invoices/${id}`) as Promise<void>
@@ -0,0 +1,27 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreateNotificationDto, Notification, QueryNotificationDto } from '@/types/notifications'
3
+ import type { PaginatedResult } from '@/types/clients'
4
+
5
+ export const getNotifications = (params?: QueryNotificationDto): Promise<PaginatedResult<Notification>> =>
6
+ apiClient.get('/api/notifications', { params }) as Promise<PaginatedResult<Notification>>
7
+
8
+ export const getNotification = (id: string): Promise<Notification> =>
9
+ apiClient.get(`/api/notifications/${id}`) as Promise<Notification>
10
+
11
+ export const createNotification = (dto: CreateNotificationDto): Promise<Notification> =>
12
+ apiClient.post('/api/notifications', dto) as Promise<Notification>
13
+
14
+ export const markAsRead = (id: string): Promise<Notification> =>
15
+ apiClient.patch(`/api/notifications/${id}/read`) as Promise<Notification>
16
+
17
+ export const markAsUnread = (id: string): Promise<Notification> =>
18
+ apiClient.patch(`/api/notifications/${id}/unread`) as Promise<Notification>
19
+
20
+ export const markAllAsRead = (): Promise<{ message: string }> =>
21
+ apiClient.post('/api/notifications/mark-all-read') as Promise<{ message: string }>
22
+
23
+ export const getUnreadCount = (): Promise<{ count: number }> =>
24
+ apiClient.get('/api/notifications/unread-count') as Promise<{ count: number }>
25
+
26
+ export const deleteNotification = (id: string): Promise<void> =>
27
+ apiClient.delete(`/api/notifications/${id}`) as Promise<void>
@@ -0,0 +1,18 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreateDealDto, Deal, QueryDealDto, UpdateDealDto } from '@/types/pipeline'
3
+ import type { PaginatedResult } from '@/types/clients'
4
+
5
+ export const getDeals = (params?: QueryDealDto): Promise<PaginatedResult<Deal>> =>
6
+ apiClient.get('/api/pipeline', { params }) as Promise<PaginatedResult<Deal>>
7
+
8
+ export const getDeal = (id: string): Promise<Deal> =>
9
+ apiClient.get(`/api/pipeline/${id}`) as Promise<Deal>
10
+
11
+ export const createDeal = (dto: CreateDealDto): Promise<Deal> =>
12
+ apiClient.post('/api/pipeline', dto) as Promise<Deal>
13
+
14
+ export const updateDeal = (id: string, dto: UpdateDealDto): Promise<Deal> =>
15
+ apiClient.patch(`/api/pipeline/${id}`, dto) as Promise<Deal>
16
+
17
+ export const deleteDeal = (id: string): Promise<void> =>
18
+ apiClient.delete(`/api/pipeline/${id}`) as Promise<void>
@@ -0,0 +1,26 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreatePlannerBlockDto, PlannerBlock, QueryPlannerBlockDto, UpdatePlannerBlockDto } from '@/types/planner'
3
+ import type { BlockStatus } from '@/types/planner'
4
+
5
+ export const getPlannerBlocks = (params?: QueryPlannerBlockDto): Promise<{ data: PlannerBlock[]; total: number }> =>
6
+ apiClient.get('/api/planner-blocks', { params }) as Promise<{ data: PlannerBlock[]; total: number }>
7
+
8
+ export const getPlannerBlock = (id: string): Promise<PlannerBlock> =>
9
+ apiClient.get(`/api/planner-blocks/${id}`) as Promise<PlannerBlock>
10
+
11
+ export const createPlannerBlock = (dto: CreatePlannerBlockDto): Promise<PlannerBlock> =>
12
+ apiClient.post('/api/planner-blocks', dto) as Promise<PlannerBlock>
13
+
14
+ export const updatePlannerBlock = (id: string, dto: UpdatePlannerBlockDto): Promise<PlannerBlock> =>
15
+ apiClient.patch(`/api/planner-blocks/${id}`, dto) as Promise<PlannerBlock>
16
+
17
+ export const updateBlockStatus = (id: string, status: BlockStatus): Promise<PlannerBlock> =>
18
+ apiClient.patch(`/api/planner-blocks/${id}/status`, { status }) as Promise<PlannerBlock>
19
+
20
+ export const duplicatePlannerBlock = (id: string, targetDate?: string): Promise<PlannerBlock> =>
21
+ apiClient.post(`/api/planner-blocks/${id}/duplicate`, null, {
22
+ params: targetDate ? { targetDate } : undefined,
23
+ }) as Promise<PlannerBlock>
24
+
25
+ export const deletePlannerBlock = (id: string): Promise<void> =>
26
+ apiClient.delete(`/api/planner-blocks/${id}`) as Promise<void>
@@ -0,0 +1,17 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreateTaskColumnDto, TaskColumn, UpdateTaskColumnDto } from '@/types/tasks'
3
+
4
+ export const getTaskColumns = (): Promise<TaskColumn[]> =>
5
+ apiClient.get('/api/task-columns') as Promise<TaskColumn[]>
6
+
7
+ export const createTaskColumn = (dto: CreateTaskColumnDto): Promise<TaskColumn> =>
8
+ apiClient.post('/api/task-columns', dto) as Promise<TaskColumn>
9
+
10
+ export const updateTaskColumn = (id: string, dto: UpdateTaskColumnDto): Promise<TaskColumn> =>
11
+ apiClient.patch(`/api/task-columns/${id}`, dto) as Promise<TaskColumn>
12
+
13
+ export const reorderTaskColumns = (ids: string[]): Promise<TaskColumn[]> =>
14
+ apiClient.patch('/api/task-columns/reorder', { ids }) as Promise<TaskColumn[]>
15
+
16
+ export const deleteTaskColumn = (id: string): Promise<void> =>
17
+ apiClient.delete(`/api/task-columns/${id}`) as Promise<void>
@@ -0,0 +1,21 @@
1
+ import apiClient from '@/lib/api/client'
2
+ import type { CreateTaskDto, MoveTaskDto, Task, UpdateTaskDto } from '@/types/tasks'
3
+ import type { PaginatedResult } from '@/types/clients'
4
+
5
+ export const getTasks = (params?: Record<string, unknown>): Promise<PaginatedResult<Task>> =>
6
+ apiClient.get('/api/tasks', { params }) as Promise<PaginatedResult<Task>>
7
+
8
+ export const getTask = (id: string): Promise<Task> =>
9
+ apiClient.get(`/api/tasks/${id}`) as Promise<Task>
10
+
11
+ export const createTask = (dto: CreateTaskDto): Promise<Task> =>
12
+ apiClient.post('/api/tasks', dto) as Promise<Task>
13
+
14
+ export const updateTask = (id: string, dto: UpdateTaskDto): Promise<Task> =>
15
+ apiClient.patch(`/api/tasks/${id}`, dto) as Promise<Task>
16
+
17
+ export const moveTask = (id: string, dto: MoveTaskDto): Promise<Task> =>
18
+ apiClient.patch(`/api/tasks/${id}/move`, dto) as Promise<Task>
19
+
20
+ export const deleteTask = (id: string): Promise<void> =>
21
+ apiClient.delete(`/api/tasks/${id}`) as Promise<void>
@@ -0,0 +1,23 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { getUnreadCount } from '@/lib/api/notifications'
3
+
4
+ export function useUnreadNotifications(intervalMs = 30000) {
5
+ const [count, setCount] = useState(0)
6
+
7
+ useEffect(() => {
8
+ let mounted = true
9
+
10
+ const fetch = async () => {
11
+ try {
12
+ const res = await getUnreadCount()
13
+ if (mounted) setCount(res.count)
14
+ } catch { /* silent */ }
15
+ }
16
+
17
+ fetch()
18
+ const timer = setInterval(fetch, intervalMs)
19
+ return () => { mounted = false; clearInterval(timer) }
20
+ }, [intervalMs])
21
+
22
+ return count
23
+ }
@@ -9,6 +9,7 @@ interface AuthContextValue extends AuthState {
9
9
  login: (email: string, password: string) => Promise<void>
10
10
  logout: () => Promise<void>
11
11
  register: (name: string, email: string, password: string) => Promise<{ message: string }>
12
+ refreshUser: () => Promise<void>
12
13
  }
13
14
 
14
15
  const AuthContext = createContext<AuthContextValue | null>(null)
@@ -46,8 +47,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
46
47
  [],
47
48
  )
48
49
 
50
+ const refreshUser = useCallback(async () => {
51
+ const user = await authApi.getMe()
52
+ setState({ user, isLoading: false, isAuthenticated: true })
53
+ }, [])
54
+
49
55
  return (
50
- <AuthContext.Provider value={{ ...state, login, logout, register }}>
56
+ <AuthContext.Provider value={{ ...state, login, logout, register, refreshUser }}>
51
57
  {children}
52
58
  </AuthContext.Provider>
53
59
  )
@@ -0,0 +1,38 @@
1
+ export interface Client {
2
+ _id: string
3
+ name: string
4
+ email?: string
5
+ phone?: string
6
+ address?: string
7
+ status: 'active' | 'archived'
8
+ notes?: string
9
+ workspaceId: string
10
+ createdAt: string
11
+ updatedAt: string
12
+ }
13
+
14
+ export interface CreateClientDto {
15
+ name: string
16
+ email?: string
17
+ phone?: string
18
+ address?: string
19
+ notes?: string
20
+ status?: 'active' | 'archived'
21
+ }
22
+
23
+ export type UpdateClientDto = Partial<CreateClientDto>
24
+
25
+ export interface QueryClientDto {
26
+ search?: string
27
+ status?: 'active' | 'archived'
28
+ page?: number
29
+ limit?: number
30
+ }
31
+
32
+ export interface PaginatedResult<T> {
33
+ data: T[]
34
+ total: number
35
+ page: number
36
+ limit: number
37
+ totalPages: number
38
+ }
@@ -0,0 +1,51 @@
1
+ export interface InvoiceItem {
2
+ description: string
3
+ quantity: number
4
+ unitPrice: number
5
+ amount: number
6
+ }
7
+
8
+ export interface Invoice {
9
+ _id: string
10
+ type: 'income' | 'expense'
11
+ number?: string
12
+ clientId?: string
13
+ status: 'draft' | 'pending' | 'paid' | 'overdue' | 'cancelled'
14
+ issueDate: string
15
+ dueDate?: string
16
+ items: InvoiceItem[]
17
+ subtotal: number
18
+ taxRate: number
19
+ taxAmount: number
20
+ total: number
21
+ currency: string
22
+ notes?: string
23
+ workspaceId: string
24
+ createdAt: string
25
+ updatedAt: string
26
+ }
27
+
28
+ export interface CreateInvoiceDto {
29
+ type?: 'income' | 'expense'
30
+ number?: string
31
+ clientId?: string
32
+ status?: 'draft' | 'pending' | 'paid' | 'overdue' | 'cancelled'
33
+ issueDate?: string
34
+ dueDate?: string
35
+ items?: InvoiceItem[]
36
+ taxRate?: number
37
+ currency?: string
38
+ notes?: string
39
+ total?: number
40
+ }
41
+
42
+ export type UpdateInvoiceDto = Partial<CreateInvoiceDto>
43
+
44
+ export interface QueryInvoiceDto {
45
+ search?: string
46
+ status?: string
47
+ type?: 'income' | 'expense'
48
+ clientId?: string
49
+ page?: number
50
+ limit?: number
51
+ }
@@ -0,0 +1,30 @@
1
+ export interface Notification {
2
+ _id: string
3
+ userId: string
4
+ title: string
5
+ message: string
6
+ type: 'info' | 'warning' | 'success' | 'error'
7
+ link?: string
8
+ isRead: boolean
9
+ refId?: string
10
+ refType?: string
11
+ workspaceId: string
12
+ createdAt: string
13
+ updatedAt: string
14
+ }
15
+
16
+ export interface CreateNotificationDto {
17
+ userId: string
18
+ title: string
19
+ message: string
20
+ type?: 'info' | 'warning' | 'success' | 'error'
21
+ link?: string
22
+ refId?: string
23
+ refType?: string
24
+ }
25
+
26
+ export interface QueryNotificationDto {
27
+ isRead?: boolean
28
+ page?: number
29
+ limit?: number
30
+ }
@@ -0,0 +1,35 @@
1
+ export type DealStage = 'lead' | 'contacted' | 'proposal' | 'won' | 'lost'
2
+
3
+ export interface Deal {
4
+ _id: string
5
+ title: string
6
+ clientId?: string
7
+ value: number
8
+ currency: string
9
+ stage: DealStage
10
+ expectedCloseDate?: string
11
+ notes?: string
12
+ workspaceId: string
13
+ createdAt: string
14
+ updatedAt: string
15
+ }
16
+
17
+ export interface CreateDealDto {
18
+ title: string
19
+ clientId?: string
20
+ value?: number
21
+ currency?: string
22
+ stage?: DealStage
23
+ expectedCloseDate?: string
24
+ notes?: string
25
+ }
26
+
27
+ export type UpdateDealDto = Partial<CreateDealDto>
28
+
29
+ export interface QueryDealDto {
30
+ search?: string
31
+ stage?: DealStage
32
+ clientId?: string
33
+ page?: number
34
+ limit?: number
35
+ }
@@ -0,0 +1,49 @@
1
+ export type BlockStatus = 'pending' | 'in-progress' | 'completed' | 'skipped'
2
+ export type BlockPriority = 'low' | 'medium' | 'high'
3
+
4
+ export interface PlannerBlock {
5
+ _id: string
6
+ userId: string
7
+ date: string
8
+ startTime: string
9
+ endTime: string
10
+ title: string
11
+ description?: string
12
+ category: string
13
+ priority: BlockPriority
14
+ status: BlockStatus
15
+ color: string
16
+ isFocusBlock: boolean
17
+ order: number
18
+ tags: string[]
19
+ workspaceId: string
20
+ createdAt: string
21
+ updatedAt: string
22
+ }
23
+
24
+ export interface CreatePlannerBlockDto {
25
+ date: string
26
+ startTime: string
27
+ endTime: string
28
+ title: string
29
+ description?: string
30
+ category?: string
31
+ priority?: BlockPriority
32
+ status?: BlockStatus
33
+ color?: string
34
+ isFocusBlock?: boolean
35
+ order?: number
36
+ tags?: string[]
37
+ }
38
+
39
+ export type UpdatePlannerBlockDto = Partial<CreatePlannerBlockDto>
40
+
41
+ export interface QueryPlannerBlockDto {
42
+ date?: string
43
+ dateFrom?: string
44
+ dateTo?: string
45
+ status?: BlockStatus
46
+ category?: string
47
+ page?: number
48
+ limit?: number
49
+ }
@@ -0,0 +1,65 @@
1
+ export interface ChecklistItem {
2
+ text: string
3
+ completed: boolean
4
+ }
5
+
6
+ export interface TaskLabel {
7
+ name: string
8
+ color: string
9
+ }
10
+
11
+ export type TaskStatus = 'todo' | 'in-progress' | 'done' | 'cancelled'
12
+ export type TaskPriority = 'low' | 'medium' | 'high' | 'urgent'
13
+
14
+ export interface Task {
15
+ _id: string
16
+ title: string
17
+ description?: string
18
+ status: TaskStatus
19
+ priority: TaskPriority
20
+ dueDate?: string
21
+ columnId: string | null
22
+ order: number
23
+ checklist: ChecklistItem[]
24
+ labels: TaskLabel[]
25
+ workspaceId: string
26
+ createdAt: string
27
+ updatedAt: string
28
+ }
29
+
30
+ export interface TaskColumn {
31
+ _id: string
32
+ name: string
33
+ color: string
34
+ order: number
35
+ workspaceId: string
36
+ createdAt: string
37
+ updatedAt: string
38
+ }
39
+
40
+ export interface CreateTaskDto {
41
+ title: string
42
+ description?: string
43
+ status?: TaskStatus
44
+ priority?: TaskPriority
45
+ dueDate?: string
46
+ columnId?: string | null
47
+ order?: number
48
+ checklist?: ChecklistItem[]
49
+ labels?: TaskLabel[]
50
+ }
51
+
52
+ export type UpdateTaskDto = Partial<CreateTaskDto>
53
+
54
+ export interface MoveTaskDto {
55
+ columnId: string | null
56
+ order?: number
57
+ }
58
+
59
+ export interface CreateTaskColumnDto {
60
+ name: string
61
+ color?: string
62
+ order?: number
63
+ }
64
+
65
+ export type UpdateTaskColumnDto = Partial<CreateTaskColumnDto>