@nextsparkjs/theme-productivity 0.1.0-beta.1
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.
- package/README.md +76 -0
- package/about.md +123 -0
- package/components/CardDetailModal.tsx +318 -0
- package/components/KanbanBoard.tsx +612 -0
- package/components/KanbanCard.tsx +218 -0
- package/components/KanbanColumn.tsx +264 -0
- package/components/SortableList.tsx +46 -0
- package/components/index.ts +4 -0
- package/config/app.config.ts +172 -0
- package/config/billing.config.ts +187 -0
- package/config/dashboard.config.ts +357 -0
- package/config/dev.config.ts +55 -0
- package/config/features.config.ts +256 -0
- package/config/flows.config.ts +484 -0
- package/config/permissions.config.ts +167 -0
- package/config/theme.config.ts +106 -0
- package/entities/boards/boards.config.ts +61 -0
- package/entities/boards/boards.fields.ts +154 -0
- package/entities/boards/boards.service.ts +256 -0
- package/entities/boards/boards.types.ts +57 -0
- package/entities/boards/messages/en.json +80 -0
- package/entities/boards/messages/es.json +80 -0
- package/entities/boards/migrations/001_boards_table.sql +83 -0
- package/entities/cards/cards.config.ts +61 -0
- package/entities/cards/cards.fields.ts +242 -0
- package/entities/cards/cards.service.ts +336 -0
- package/entities/cards/cards.types.ts +79 -0
- package/entities/cards/messages/en.json +114 -0
- package/entities/cards/messages/es.json +114 -0
- package/entities/cards/migrations/020_cards_table.sql +92 -0
- package/entities/lists/lists.config.ts +61 -0
- package/entities/lists/lists.fields.ts +105 -0
- package/entities/lists/lists.service.ts +252 -0
- package/entities/lists/lists.types.ts +55 -0
- package/entities/lists/messages/en.json +60 -0
- package/entities/lists/messages/es.json +60 -0
- package/entities/lists/migrations/010_lists_table.sql +79 -0
- package/lib/selectors.ts +206 -0
- package/messages/en.json +79 -0
- package/messages/es.json +79 -0
- package/migrations/999_theme_sample_data.sql +922 -0
- package/migrations/999a_initial_sample_data.sql +377 -0
- package/migrations/999b_abundant_sample_data.sql +346 -0
- package/package.json +17 -0
- package/permissions-matrix.md +122 -0
- package/styles/components.css +460 -0
- package/styles/globals.css +560 -0
- package/templates/dashboard/(main)/boards/[id]/[cardId]/page.tsx +238 -0
- package/templates/dashboard/(main)/boards/[id]/edit/page.tsx +390 -0
- package/templates/dashboard/(main)/boards/[id]/page.tsx +236 -0
- package/templates/dashboard/(main)/boards/create/page.tsx +236 -0
- package/templates/dashboard/(main)/boards/page.tsx +335 -0
- package/templates/dashboard/(main)/layout.tsx +32 -0
- package/templates/dashboard/(main)/page.tsx +592 -0
- package/templates/shared/ProductivityMobileNav.tsx +410 -0
- package/templates/shared/ProductivitySidebar.tsx +538 -0
- package/templates/shared/ProductivityTopBar.tsx +317 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Productivity Mobile Navigation
|
|
3
|
+
* Bottom navigation bar for mobile with boards sheet
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client'
|
|
7
|
+
|
|
8
|
+
import Link from 'next/link'
|
|
9
|
+
import { usePathname, useParams, useRouter } from 'next/navigation'
|
|
10
|
+
import { useState, useEffect, useCallback } from 'react'
|
|
11
|
+
import { cn } from '@nextsparkjs/core/lib/utils'
|
|
12
|
+
import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
|
|
13
|
+
import { useTeamsConfig } from '@nextsparkjs/core/hooks/useTeamsConfig'
|
|
14
|
+
import { Button } from '@nextsparkjs/core/components/ui/button'
|
|
15
|
+
import {
|
|
16
|
+
Sheet,
|
|
17
|
+
SheetContent,
|
|
18
|
+
SheetHeader,
|
|
19
|
+
SheetTitle,
|
|
20
|
+
SheetTrigger,
|
|
21
|
+
} from '@nextsparkjs/core/components/ui/sheet'
|
|
22
|
+
import {
|
|
23
|
+
Dialog,
|
|
24
|
+
DialogContent,
|
|
25
|
+
DialogHeader,
|
|
26
|
+
DialogTitle,
|
|
27
|
+
} from '@nextsparkjs/core/components/ui/dialog'
|
|
28
|
+
import { Input } from '@nextsparkjs/core/components/ui/input'
|
|
29
|
+
import { Label } from '@nextsparkjs/core/components/ui/label'
|
|
30
|
+
import {
|
|
31
|
+
LayoutGrid,
|
|
32
|
+
Plus,
|
|
33
|
+
Settings,
|
|
34
|
+
Users,
|
|
35
|
+
Loader2,
|
|
36
|
+
Kanban,
|
|
37
|
+
Check,
|
|
38
|
+
ChevronRight
|
|
39
|
+
} from 'lucide-react'
|
|
40
|
+
|
|
41
|
+
interface Board {
|
|
42
|
+
id: string
|
|
43
|
+
name: string
|
|
44
|
+
color: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Board colors
|
|
48
|
+
const BOARD_COLORS = [
|
|
49
|
+
{ id: 'blue', class: 'bg-[var(--board-blue)]' },
|
|
50
|
+
{ id: 'green', class: 'bg-[var(--board-green)]' },
|
|
51
|
+
{ id: 'purple', class: 'bg-[var(--board-purple)]' },
|
|
52
|
+
{ id: 'orange', class: 'bg-[var(--board-orange)]' },
|
|
53
|
+
{ id: 'red', class: 'bg-[var(--board-red)]' },
|
|
54
|
+
{ id: 'pink', class: 'bg-[var(--board-pink)]' },
|
|
55
|
+
{ id: 'gray', class: 'bg-[var(--board-gray)]' },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const getColorClass = (color: string) => {
|
|
59
|
+
return BOARD_COLORS.find(c => c.id === color)?.class || BOARD_COLORS[0].class
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function ProductivityMobileNav() {
|
|
63
|
+
const pathname = usePathname()
|
|
64
|
+
const params = useParams()
|
|
65
|
+
const router = useRouter()
|
|
66
|
+
const { currentTeam, userTeams, switchTeam } = useTeamContext()
|
|
67
|
+
const { canSwitch } = useTeamsConfig()
|
|
68
|
+
|
|
69
|
+
const [boards, setBoards] = useState<Board[]>([])
|
|
70
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
71
|
+
const [boardsSheetOpen, setBoardsSheetOpen] = useState(false)
|
|
72
|
+
const [teamsSheetOpen, setTeamsSheetOpen] = useState(false)
|
|
73
|
+
const [createDialogOpen, setCreateDialogOpen] = useState(false)
|
|
74
|
+
const [newBoardName, setNewBoardName] = useState('')
|
|
75
|
+
const [newBoardColor, setNewBoardColor] = useState('blue')
|
|
76
|
+
const [isCreating, setIsCreating] = useState(false)
|
|
77
|
+
|
|
78
|
+
const currentBoardId = params?.id as string
|
|
79
|
+
|
|
80
|
+
// Get current user's role in the active team
|
|
81
|
+
const currentMembership = userTeams.find(m => m.team.id === currentTeam?.id)
|
|
82
|
+
const userRole = currentMembership?.role || 'member'
|
|
83
|
+
|
|
84
|
+
// Only owners and admins can create boards
|
|
85
|
+
const canCreateBoard = userRole === 'owner' || userRole === 'admin'
|
|
86
|
+
|
|
87
|
+
// Fetch boards
|
|
88
|
+
const fetchBoards = useCallback(async () => {
|
|
89
|
+
if (!currentTeam?.id) {
|
|
90
|
+
setIsLoading(false)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
setIsLoading(true)
|
|
95
|
+
const response = await fetch('/api/v1/boards?limit=100', {
|
|
96
|
+
headers: {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
'x-team-id': currentTeam.id
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
if (response.ok) {
|
|
102
|
+
const data = await response.json()
|
|
103
|
+
setBoards(data.data || [])
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Failed to fetch boards:', error)
|
|
107
|
+
} finally {
|
|
108
|
+
setIsLoading(false)
|
|
109
|
+
}
|
|
110
|
+
}, [currentTeam?.id])
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
fetchBoards()
|
|
114
|
+
}, [fetchBoards])
|
|
115
|
+
|
|
116
|
+
const handleCreateBoard = async () => {
|
|
117
|
+
if (!newBoardName.trim() || !currentTeam?.id) return
|
|
118
|
+
|
|
119
|
+
setIsCreating(true)
|
|
120
|
+
try {
|
|
121
|
+
const response = await fetch('/api/v1/boards', {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: {
|
|
124
|
+
'Content-Type': 'application/json',
|
|
125
|
+
'x-team-id': currentTeam.id,
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify({ name: newBoardName.trim(), color: newBoardColor }),
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (response.ok) {
|
|
131
|
+
const data = await response.json()
|
|
132
|
+
setNewBoardName('')
|
|
133
|
+
setNewBoardColor('blue')
|
|
134
|
+
setCreateDialogOpen(false)
|
|
135
|
+
setBoardsSheetOpen(false)
|
|
136
|
+
fetchBoards()
|
|
137
|
+
router.push(`/dashboard/boards/${data.data.id}`)
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('Failed to create board:', error)
|
|
141
|
+
} finally {
|
|
142
|
+
setIsCreating(false)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const handleTeamSwitch = async (teamId: string) => {
|
|
147
|
+
try {
|
|
148
|
+
await switchTeam(teamId)
|
|
149
|
+
setTeamsSheetOpen(false)
|
|
150
|
+
router.push('/dashboard/boards')
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Failed to switch team:', error)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const getTeamInitials = (name: string) => {
|
|
157
|
+
return name.split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const isActive = (path: string) => {
|
|
161
|
+
if (path === '/dashboard/boards') {
|
|
162
|
+
return pathname.startsWith('/dashboard/boards')
|
|
163
|
+
}
|
|
164
|
+
return pathname === path || pathname.startsWith(path + '/')
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<nav className="lg:hidden fixed bottom-0 left-0 right-0 z-50 bg-background/95 backdrop-blur-md border-t border-border safe-area-bottom">
|
|
170
|
+
<div className="flex items-center justify-around h-16 px-2">
|
|
171
|
+
{/* Boards Sheet */}
|
|
172
|
+
<Sheet open={boardsSheetOpen} onOpenChange={setBoardsSheetOpen}>
|
|
173
|
+
<SheetTrigger asChild>
|
|
174
|
+
<button
|
|
175
|
+
className={cn(
|
|
176
|
+
'flex flex-col items-center gap-0.5 py-1 px-4 rounded-lg transition-colors',
|
|
177
|
+
isActive('/dashboard/boards')
|
|
178
|
+
? 'text-primary'
|
|
179
|
+
: 'text-muted-foreground'
|
|
180
|
+
)}
|
|
181
|
+
>
|
|
182
|
+
<LayoutGrid className="w-5 h-5" />
|
|
183
|
+
<span className="text-[10px] font-medium">Boards</span>
|
|
184
|
+
</button>
|
|
185
|
+
</SheetTrigger>
|
|
186
|
+
<SheetContent side="bottom" className="h-[80vh] rounded-t-2xl">
|
|
187
|
+
<SheetHeader className="pb-4">
|
|
188
|
+
<SheetTitle className="flex items-center justify-between">
|
|
189
|
+
<span>Your Boards</span>
|
|
190
|
+
{canCreateBoard && (
|
|
191
|
+
<Button
|
|
192
|
+
size="sm"
|
|
193
|
+
onClick={() => setCreateDialogOpen(true)}
|
|
194
|
+
>
|
|
195
|
+
<Plus className="w-4 h-4 mr-1" />
|
|
196
|
+
New
|
|
197
|
+
</Button>
|
|
198
|
+
)}
|
|
199
|
+
</SheetTitle>
|
|
200
|
+
</SheetHeader>
|
|
201
|
+
|
|
202
|
+
<div className="overflow-y-auto flex-1 -mx-6 px-6">
|
|
203
|
+
{isLoading ? (
|
|
204
|
+
<div className="flex items-center justify-center py-12">
|
|
205
|
+
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
|
206
|
+
</div>
|
|
207
|
+
) : boards.length === 0 ? (
|
|
208
|
+
<div className="text-center py-12">
|
|
209
|
+
<div className="w-16 h-16 rounded-2xl bg-muted flex items-center justify-center mx-auto mb-4">
|
|
210
|
+
<Kanban className="w-8 h-8 text-muted-foreground" />
|
|
211
|
+
</div>
|
|
212
|
+
<p className="text-muted-foreground mb-4">
|
|
213
|
+
{canCreateBoard ? 'No boards yet' : 'No boards available'}
|
|
214
|
+
</p>
|
|
215
|
+
{canCreateBoard && (
|
|
216
|
+
<Button onClick={() => setCreateDialogOpen(true)}>
|
|
217
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
218
|
+
Create Board
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
) : (
|
|
223
|
+
<div className="space-y-1">
|
|
224
|
+
{boards.map((board) => {
|
|
225
|
+
const colorClass = getColorClass(board.color)
|
|
226
|
+
const isCurrent = currentBoardId === board.id
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<Link
|
|
230
|
+
key={board.id}
|
|
231
|
+
href={`/dashboard/boards/${board.id}`}
|
|
232
|
+
onClick={() => setBoardsSheetOpen(false)}
|
|
233
|
+
className={cn(
|
|
234
|
+
'flex items-center gap-3 p-3 rounded-xl transition-all',
|
|
235
|
+
isCurrent
|
|
236
|
+
? 'bg-primary/10 text-primary'
|
|
237
|
+
: 'hover:bg-muted'
|
|
238
|
+
)}
|
|
239
|
+
>
|
|
240
|
+
<div className={cn('w-4 h-4 rounded', colorClass)} />
|
|
241
|
+
<span className="flex-1 font-medium">{board.name}</span>
|
|
242
|
+
{isCurrent && <Check className="w-4 h-4" />}
|
|
243
|
+
</Link>
|
|
244
|
+
)
|
|
245
|
+
})}
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
|
|
249
|
+
{/* View All Link */}
|
|
250
|
+
{boards.length > 0 && (
|
|
251
|
+
<div className="mt-4 pt-4 border-t">
|
|
252
|
+
<Link
|
|
253
|
+
href="/dashboard/boards"
|
|
254
|
+
onClick={() => setBoardsSheetOpen(false)}
|
|
255
|
+
className="flex items-center justify-between p-3 rounded-xl hover:bg-muted text-muted-foreground"
|
|
256
|
+
>
|
|
257
|
+
<span>View All Boards</span>
|
|
258
|
+
<ChevronRight className="w-4 h-4" />
|
|
259
|
+
</Link>
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
</SheetContent>
|
|
264
|
+
</Sheet>
|
|
265
|
+
|
|
266
|
+
{/* Create Button - Only shown for users with create permission */}
|
|
267
|
+
{canCreateBoard ? (
|
|
268
|
+
<button
|
|
269
|
+
onClick={() => setCreateDialogOpen(true)}
|
|
270
|
+
className="flex items-center justify-center w-14 h-14 -mt-6 rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/30 transition-transform active:scale-95"
|
|
271
|
+
>
|
|
272
|
+
<Plus className="w-7 h-7" />
|
|
273
|
+
</button>
|
|
274
|
+
) : (
|
|
275
|
+
<div className="w-14" /> // Spacer to maintain layout
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{/* Teams Sheet - Only visible when canSwitch is true (multi-tenant/hybrid modes) */}
|
|
279
|
+
{canSwitch ? (
|
|
280
|
+
<Sheet open={teamsSheetOpen} onOpenChange={setTeamsSheetOpen}>
|
|
281
|
+
<SheetTrigger asChild>
|
|
282
|
+
<button
|
|
283
|
+
className="flex flex-col items-center gap-0.5 py-1 px-4 rounded-lg transition-colors text-muted-foreground"
|
|
284
|
+
>
|
|
285
|
+
<Users className="w-5 h-5" />
|
|
286
|
+
<span className="text-[10px] font-medium">Teams</span>
|
|
287
|
+
</button>
|
|
288
|
+
</SheetTrigger>
|
|
289
|
+
<SheetContent side="bottom" className="rounded-t-2xl">
|
|
290
|
+
<SheetHeader className="pb-4">
|
|
291
|
+
<SheetTitle>Switch Team</SheetTitle>
|
|
292
|
+
</SheetHeader>
|
|
293
|
+
|
|
294
|
+
<div className="space-y-1">
|
|
295
|
+
{userTeams.map((membership) => (
|
|
296
|
+
<button
|
|
297
|
+
key={membership.team.id}
|
|
298
|
+
onClick={() => handleTeamSwitch(membership.team.id)}
|
|
299
|
+
className={cn(
|
|
300
|
+
'w-full flex items-center gap-3 p-3 rounded-xl transition-all text-left',
|
|
301
|
+
currentTeam?.id === membership.team.id
|
|
302
|
+
? 'bg-primary/10 text-primary'
|
|
303
|
+
: 'hover:bg-muted'
|
|
304
|
+
)}
|
|
305
|
+
>
|
|
306
|
+
<div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center text-sm font-bold">
|
|
307
|
+
{getTeamInitials(membership.team.name)}
|
|
308
|
+
</div>
|
|
309
|
+
<span className="flex-1 font-medium">{membership.team.name}</span>
|
|
310
|
+
{currentTeam?.id === membership.team.id && (
|
|
311
|
+
<Check className="w-5 h-5" />
|
|
312
|
+
)}
|
|
313
|
+
</button>
|
|
314
|
+
))}
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div className="mt-4 pt-4 border-t">
|
|
318
|
+
<Link
|
|
319
|
+
href="/dashboard/settings/teams"
|
|
320
|
+
onClick={() => setTeamsSheetOpen(false)}
|
|
321
|
+
className="flex items-center justify-between p-3 rounded-xl hover:bg-muted text-muted-foreground"
|
|
322
|
+
>
|
|
323
|
+
<span>Manage Teams</span>
|
|
324
|
+
<ChevronRight className="w-4 h-4" />
|
|
325
|
+
</Link>
|
|
326
|
+
</div>
|
|
327
|
+
</SheetContent>
|
|
328
|
+
</Sheet>
|
|
329
|
+
) : (
|
|
330
|
+
// Settings link when team switching is disabled (single-user/collaborative/single-tenant)
|
|
331
|
+
<Link
|
|
332
|
+
href="/dashboard/settings"
|
|
333
|
+
className={cn(
|
|
334
|
+
'flex flex-col items-center gap-0.5 py-1 px-4 rounded-lg transition-colors',
|
|
335
|
+
pathname.startsWith('/dashboard/settings')
|
|
336
|
+
? 'text-primary'
|
|
337
|
+
: 'text-muted-foreground'
|
|
338
|
+
)}
|
|
339
|
+
>
|
|
340
|
+
<Settings className="w-5 h-5" />
|
|
341
|
+
<span className="text-[10px] font-medium">Settings</span>
|
|
342
|
+
</Link>
|
|
343
|
+
)}
|
|
344
|
+
</div>
|
|
345
|
+
</nav>
|
|
346
|
+
|
|
347
|
+
{/* Create Board Dialog */}
|
|
348
|
+
<Dialog open={createDialogOpen} onOpenChange={setCreateDialogOpen}>
|
|
349
|
+
<DialogContent className="sm:max-w-md">
|
|
350
|
+
<DialogHeader>
|
|
351
|
+
<DialogTitle>Create new board</DialogTitle>
|
|
352
|
+
</DialogHeader>
|
|
353
|
+
<div className="space-y-4 pt-4">
|
|
354
|
+
<div className="space-y-2">
|
|
355
|
+
<Label htmlFor="mobile-board-name">Board name</Label>
|
|
356
|
+
<Input
|
|
357
|
+
id="mobile-board-name"
|
|
358
|
+
placeholder="My awesome board"
|
|
359
|
+
value={newBoardName}
|
|
360
|
+
onChange={(e) => setNewBoardName(e.target.value)}
|
|
361
|
+
onKeyDown={(e) => e.key === 'Enter' && handleCreateBoard()}
|
|
362
|
+
autoFocus
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
365
|
+
<div className="space-y-2">
|
|
366
|
+
<Label>Color</Label>
|
|
367
|
+
<div className="flex gap-2">
|
|
368
|
+
{BOARD_COLORS.map((c) => (
|
|
369
|
+
<button
|
|
370
|
+
key={c.id}
|
|
371
|
+
onClick={() => setNewBoardColor(c.id)}
|
|
372
|
+
className={cn(
|
|
373
|
+
'w-9 h-9 rounded-lg transition-all',
|
|
374
|
+
c.class,
|
|
375
|
+
newBoardColor === c.id
|
|
376
|
+
? 'ring-2 ring-offset-2 ring-primary scale-110'
|
|
377
|
+
: 'hover:scale-105'
|
|
378
|
+
)}
|
|
379
|
+
/>
|
|
380
|
+
))}
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
<div className="flex gap-2 pt-2">
|
|
384
|
+
<Button
|
|
385
|
+
variant="outline"
|
|
386
|
+
className="flex-1"
|
|
387
|
+
onClick={() => setCreateDialogOpen(false)}
|
|
388
|
+
>
|
|
389
|
+
Cancel
|
|
390
|
+
</Button>
|
|
391
|
+
<Button
|
|
392
|
+
className="flex-1"
|
|
393
|
+
onClick={handleCreateBoard}
|
|
394
|
+
disabled={!newBoardName.trim() || isCreating}
|
|
395
|
+
>
|
|
396
|
+
{isCreating ? (
|
|
397
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
398
|
+
) : (
|
|
399
|
+
'Create'
|
|
400
|
+
)}
|
|
401
|
+
</Button>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</DialogContent>
|
|
405
|
+
</Dialog>
|
|
406
|
+
</>
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export default ProductivityMobileNav
|