@turk.net/mui 3.0.7 → 3.0.8

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.
@@ -0,0 +1,509 @@
1
+ # OneHub Page Patterns
2
+
3
+ > **Purpose:** Standardized page patterns for OneHub modules. When creating a new page, match it to the closest pattern below and follow the exact structure, layout, component set, and spacing grid.
4
+ >
5
+ > **All patterns MUST use OneHub theme rules:** typography variants, spacing via `theme.spacing()`, colors via CSS custom properties.
6
+
7
+ ---
8
+
9
+ ## Pattern 1: List Page (Table + Filters)
10
+
11
+ **Use case:** Task lists, customer lists, ticket lists, lead lists, any tabular data with search/filter.
12
+
13
+ ### Layout
14
+
15
+ ```
16
+ TabBasedLayout (app level)
17
+ └── DashboardLayout / WithSidebarLayout (page level)
18
+ └── PageContentContainer
19
+ ├── PageContentHeader (title + actions)
20
+ └── Content Area
21
+ ├── Search & Filter Bar
22
+ ├── Active Filter Tags (Chips)
23
+ ├── Table Header Row
24
+ ├── Table Data Rows
25
+ └── Pagination
26
+ ```
27
+
28
+ ### Component Breakdown
29
+
30
+ | Element | Component | Props |
31
+ |---------|-----------|-------|
32
+ | Page title | `Typography` | `variant="display.xs.bold"` |
33
+ | Search input | `TextField` | `color="neutral" size="medium" placeholder="Ara"` |
34
+ | Search icon | `SearchIcon` from icons | `size: 20px` |
35
+ | Filter button | `Button` | `variant="text" size="medium" color="secondary"` |
36
+ | Active filter tags | `Chip` | `variant="tint" color="primary" size="small" onDelete={...}` |
37
+ | Clear filters button | `Button` | `variant="text" size="medium" color="primary"` |
38
+ | Table header | Custom `Box` | `bg: neutral.background[2].rest`, `height: 40px` |
39
+ | Table header text | `Typography` | `variant="text.xs.medium"` |
40
+ | Table row | Custom `Box` | `bg: neutral.background[1].rest`, `height: 56px` |
41
+ | Table primary text | `Typography` | `variant="text.sm.medium" color="neutral.foreground[1].rest"` |
42
+ | Table secondary text | `Typography` | `variant="text.sm.regular" color="neutral.foreground[4].rest"` |
43
+ | Status indicator | `Badge` or `Chip` | Depends on status type |
44
+ | Pagination | `Pagination` | `size="medium" color="primary"` |
45
+
46
+ ### Spacing Grid
47
+
48
+ ```
49
+ Table cell: paddingLeft/Right: theme.spacing(8) (32px)
50
+ Table header: height: theme.spacing(10) (40px)
51
+ Table row: height: theme.spacing(14) (56px)
52
+ Row gap: gap: theme.spacing(2) (8px)
53
+ Section gap: marginBottom: theme.spacing(4) (16px)
54
+ ```
55
+
56
+ ### Figma Reference
57
+
58
+ - `My task table` (`364:28717`)
59
+ - `Team task table` (`694:41994`)
60
+ - `Customer table` (`2291:40548`)
61
+ - `Ticket List table` (`9148:52401`)
62
+
63
+ ### Example Structure
64
+
65
+ ```tsx
66
+ function TaskListPage() {
67
+ return (
68
+ <PageContentContainer>
69
+ <PageContentHeader>
70
+ <Typography variant="display.xs.bold">My Tasks</Typography>
71
+ <Button variant="contained" color="primary" size="medium">
72
+ New Task
73
+ </Button>
74
+ </PageContentHeader>
75
+
76
+ {/* Search & Filter Bar */}
77
+ <Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
78
+ <TextField
79
+ color="neutral"
80
+ size="medium"
81
+ placeholder="Ara"
82
+ InputProps={{ startAdornment: <SearchIcon /> }}
83
+ sx={{ flex: 1 }}
84
+ />
85
+ <Button variant="text" size="medium" color="secondary" startIcon={<FilterIcon />}>
86
+ Filters
87
+ </Button>
88
+ </Box>
89
+
90
+ {/* Active Filter Tags */}
91
+ <Box sx={{ display: 'flex', gap: 1, mb: 2 }}>
92
+ <Chip variant="tint" color="primary" size="small" label="Today" onDelete={handleRemove} />
93
+ </Box>
94
+
95
+ {/* Table */}
96
+ <Box sx={{ border: 1, borderColor: 'var(--neutral-stroke-3-rest)', borderRadius: 1 }}>
97
+ {/* Header */}
98
+ <Box sx={{
99
+ display: 'flex',
100
+ px: 8,
101
+ height: 40,
102
+ alignItems: 'center',
103
+ bgcolor: 'var(--neutral-background-2-rest)',
104
+ borderBottom: 1,
105
+ borderColor: 'var(--neutral-stroke-3-rest)'
106
+ }}>
107
+ <Typography variant="text.xs.medium" sx={{ minWidth: 140 }}>Date</Typography>
108
+ <Typography variant="text.xs.medium" sx={{ minWidth: 90 }}>Customer</Typography>
109
+ <Typography variant="text.xs.medium" sx={{ flex: 1 }}>Description</Typography>
110
+ <Typography variant="text.xs.medium">Rating</Typography>
111
+ </Box>
112
+
113
+ {/* Rows */}
114
+ {tasks.map(task => (
115
+ <Box key={task.id} sx={{
116
+ display: 'flex',
117
+ px: 8,
118
+ height: 56,
119
+ alignItems: 'center',
120
+ borderBottom: 1,
121
+ borderColor: 'var(--neutral-stroke-3-rest)',
122
+ bgcolor: 'var(--neutral-background-1-rest)'
123
+ }}>
124
+ <Box sx={{ minWidth: 140 }}>
125
+ <Typography variant="text.lg.medium" color="var(--neutral-foreground-2-rest)">06</Typography>
126
+ <Typography variant="text.sm.regular" color="var(--neutral-foreground-4-rest)">WED, THU</Typography>
127
+ </Box>
128
+ <Typography variant="text.sm.regular" color="var(--neutral-foreground-4-rest)" sx={{ minWidth: 90 }}>
129
+ Gizem K.
130
+ </Typography>
131
+ <Typography variant="text.sm.medium" sx={{ flex: 1 }}>{task.description}</Typography>
132
+ <Box sx={{ display: 'flex', gap: 1 }}>
133
+ <FaceHappyIcon />
134
+ <MessageIcon />
135
+ </Box>
136
+ </Box>
137
+ ))}
138
+ </Box>
139
+
140
+ {/* Pagination */}
141
+ <Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
142
+ <Pagination count={totalPages} size="medium" color="primary" />
143
+ </Box>
144
+ </PageContentContainer>
145
+ );
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Pattern 2: Detail Page
152
+
153
+ **Use case:** Task detail, customer detail, ticket detail — viewing a single record with multiple sections.
154
+
155
+ ### Layout
156
+
157
+ ```
158
+ TabBasedLayout
159
+ └── WithSidebarLayout
160
+ └── PageContentContainer
161
+ ├── PageContentHeader (back button + title)
162
+ ├── Tab Navigation (optional)
163
+ └── Content Sections
164
+ ├── Section Card 1
165
+ ├── Section Card 2
166
+ └── ...
167
+ ```
168
+
169
+ ### Component Breakdown
170
+
171
+ | Element | Component | Props |
172
+ |---------|-----------|-------|
173
+ | Back button | `Button` | `variant="text" size="medium" color="primary"` |
174
+ | Page title | `Typography` | `variant="display.xs.bold"` |
175
+ | Section tabs | `Tabs` + `Tab` | Active: `text.sm.semibold`, Inactive: `text.sm.regular` |
176
+ | Section card | `Paper` | `elevation={0}` |
177
+ | Section title | `Typography` | `variant="text.lg.medium"` |
178
+ | Field label | `Typography` | `variant="text.xs.regular" color="neutral.foreground[2].rest"` |
179
+ | Field value | `Typography` | `variant="text.sm.regular" color="neutral.foreground[1].rest"` |
180
+ | Status badge | `Chip` | `variant="tint" size="small"` |
181
+ | Action buttons | `Button` | `variant="contained" color="primary"` |
182
+
183
+ ### Spacing Grid
184
+
185
+ ```
186
+ Section gap: marginBottom: theme.spacing(4) (16px)
187
+ Card padding: padding: theme.spacing(4) (16px)
188
+ Field row gap: gap: theme.spacing(2) (8px)
189
+ Field label-value: marginBottom: theme.spacing(1) (4px)
190
+ ```
191
+
192
+ ### Example Structure
193
+
194
+ ```tsx
195
+ function DetailPage() {
196
+ return (
197
+ <PageContentContainer>
198
+ <PageContentHeader>
199
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
200
+ <Button variant="text" size="medium" color="primary" startIcon={<ArrowLeftIcon />}>
201
+ Back
202
+ </Button>
203
+ <Typography variant="display.xs.bold">Task Detail</Typography>
204
+ </Box>
205
+ <Box sx={{ display: 'flex', gap: 2 }}>
206
+ <Button variant="outlined" size="medium" color="secondary">Transfer</Button>
207
+ <Button variant="contained" size="medium" color="primary">Complete</Button>
208
+ </Box>
209
+ </PageContentHeader>
210
+
211
+ {/* Tabs */}
212
+ <Tabs value={tab} onChange={handleTabChange} sx={{ mb: 4 }}>
213
+ <Tab label="Overview" />
214
+ <Tab label="History" />
215
+ </Tabs>
216
+
217
+ {/* Content */}
218
+ <Paper elevation={0} sx={{ p: 4, mb: 4 }}>
219
+ <Typography variant="text.lg.medium" sx={{ mb: 4 }}>Customer Info</Typography>
220
+ <Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }}>
221
+ <Box>
222
+ <Typography variant="text.xs.regular" color="var(--neutral-foreground-2-rest)">Name</Typography>
223
+ <Typography variant="text.sm.regular">Gizem K.</Typography>
224
+ </Box>
225
+ <Box>
226
+ <Typography variant="text.xs.regular" color="var(--neutral-foreground-2-rest)">Status</Typography>
227
+ <Chip variant="tint" color="success" size="small" label="Active" />
228
+ </Box>
229
+ </Box>
230
+ </Paper>
231
+ </PageContentContainer>
232
+ );
233
+ }
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Pattern 3: Form Page
239
+
240
+ **Use case:** New task, edit task, settings forms, any data entry page.
241
+
242
+ ### Layout
243
+
244
+ ```
245
+ TabBasedLayout
246
+ └── WithSidebarLayout
247
+ └── PageContentContainer
248
+ ├── PageContentHeader (title)
249
+ ├── Form (Formik)
250
+ │ ├── Form Section 1
251
+ │ │ ├── Field Row
252
+ │ │ └── ...
253
+ │ └── Form Section 2
254
+ └── Form Actions (Submit + Cancel)
255
+ ```
256
+
257
+ ### Component Breakdown
258
+
259
+ | Element | Component | Props |
260
+ |---------|-----------|-------|
261
+ | Page title | `Typography` | `variant="display.xs.bold"` |
262
+ | Form wrapper | `form` + `Formik` | Formik `onSubmit` handler |
263
+ | Section title | `Typography` | `variant="text.lg.medium"` |
264
+ | Text input | `TextField` | `color="neutral" size="medium" fullWidth` |
265
+ | Dropdown | `Select` | `color="neutral" size="medium"` |
266
+ | Autocomplete | `Autocomplete` | `size="medium" color="neutral"` |
267
+ | Radio group | `RadioGroup` + `Radio` | `color="primary"` |
268
+ | Checkbox | `Checkbox` + `FormControlLabel` | `color="primary"` |
269
+ | Switch toggle | `Switch` + `FormControlLabel` | `color="primary"` |
270
+ | Date picker | `DatePicker` | `dayjs` adapter |
271
+ | Error message | `FormHelperText` | `error` |
272
+ | Submit button | `Button` | `variant="contained" color="primary" size="medium"` |
273
+ | Cancel button | `Button` | `variant="outlined" color="secondary" size="medium"` |
274
+
275
+ ### Spacing Grid
276
+
277
+ ```
278
+ Section gap: marginBottom: theme.spacing(4) (16px)
279
+ Field row gap: gap: theme.spacing(2) (8px)
280
+ Label margin: marginBottom: theme.spacing(1) (4px)
281
+ Field fullWidth: fullWidth
282
+ Form actions: gap: theme.spacing(2), justifyContent: flex-end
283
+ ```
284
+
285
+ ### Example Structure
286
+
287
+ ```tsx
288
+ function CreateTaskForm() {
289
+ const formik = useFormik({
290
+ initialValues: { title: '', customer: null, priority: 'medium', notify: false },
291
+ validationSchema: yup.object({
292
+ title: yup.string().required('Title is required'),
293
+ customer: yup.object().required('Customer is required'),
294
+ }),
295
+ onSubmit: (values) => createTask(values),
296
+ });
297
+
298
+ return (
299
+ <PageContentContainer>
300
+ <PageContentHeader>
301
+ <Typography variant="display.xs.bold">New Task</Typography>
302
+ </PageContentHeader>
303
+
304
+ <form onSubmit={formik.handleSubmit}>
305
+ <Paper elevation={0} sx={{ p: 4, mb: 4 }}>
306
+ <Typography variant="text.lg.medium" sx={{ mb: 4 }}>Task Details</Typography>
307
+
308
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
309
+ <TextField
310
+ name="title"
311
+ label="Title"
312
+ color="neutral"
313
+ size="medium"
314
+ fullWidth
315
+ value={formik.values.title}
316
+ onChange={formik.handleChange}
317
+ error={formik.touched.title && Boolean(formik.errors.title)}
318
+ helperText={formik.touched.title && formik.errors.title}
319
+ />
320
+
321
+ <Autocomplete
322
+ options={customers}
323
+ size="medium"
324
+ renderInput={(params) => (
325
+ <TextField {...params} label="Customer" color="neutral" />
326
+ )}
327
+ value={formik.values.customer}
328
+ onChange={(_, value) => formik.setFieldValue('customer', value)}
329
+ />
330
+
331
+ <FormControlLabel
332
+ control={<Switch color="primary" checked={formik.values.notify} />}
333
+ label="Notify team"
334
+ />
335
+ </Box>
336
+ </Paper>
337
+
338
+ <Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 2 }}>
339
+ <Button variant="outlined" size="medium" color="secondary">Cancel</Button>
340
+ <Button variant="contained" size="medium" color="primary" type="submit">
341
+ Create
342
+ </Button>
343
+ </Box>
344
+ </form>
345
+ </PageContentContainer>
346
+ );
347
+ }
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Pattern 4: Dashboard
353
+
354
+ **Use case:** Overview screens with metrics, charts, quick actions.
355
+
356
+ ### Layout
357
+
358
+ ```
359
+ TabBasedLayout
360
+ └── WithSidebarLayout
361
+ └── PageContentContainer
362
+ ├── PageContentHeader (title + date filter)
363
+ ├── Metrics Row (Cards)
364
+ ├── Charts/Graphs Section
365
+ └── Quick Actions
366
+ ```
367
+
368
+ ### Component Breakdown
369
+
370
+ | Element | Component | Props |
371
+ |---------|-----------|-------|
372
+ | Page title | `Typography` | `variant="display.xs.bold"` |
373
+ | Date filter | `Chip` or `Select` | Filter chips shown in Figma |
374
+ | Metric card | `Paper` | `elevation={0}` |
375
+ | Metric value | `Typography` | `variant="display.md.semibold"` |
376
+ | Metric label | `Typography` | `variant="text.xs.regular" color="neutral.foreground[2].rest"` |
377
+ | Chart area | Custom component | Uses ApexCharts or similar |
378
+
379
+ ### Spacing Grid
380
+
381
+ ```
382
+ Card gap: gap: theme.spacing(2) (8px)
383
+ Card padding: padding: theme.spacing(4) (16px)
384
+ Section gap: marginBottom: theme.spacing(4) (16px)
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Pattern 5: Dialog/Modal
390
+
391
+ **Use case:** Confirmation dialogs, quick edit forms, transfer/ticket close.
392
+
393
+ ### Component Breakdown
394
+
395
+ | Element | Component | Props |
396
+ |---------|-----------|-------|
397
+ | Dialog | `Dialog` | `maxWidth`, `fullWidth` options |
398
+ | Title | `DialogTitle` | Typography: `text.lg.medium` |
399
+ | Content | `DialogContent` | Typography: `text.md.regular` |
400
+ | Content text | `DialogContentText` | — |
401
+ | Primary action | `Button` | `variant="contained" color="primary"` |
402
+ | Secondary action | `Button` | `variant="outlined" color="secondary"` |
403
+ | Destructive action | `Button` | `variant="contained" color="error"` |
404
+
405
+ ### Example Structure
406
+
407
+ ```tsx
408
+ <Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
409
+ <DialogTitle>Confirm Action</DialogTitle>
410
+ <DialogContent>
411
+ <DialogContentText>
412
+ Are you sure you want to close this ticket?
413
+ </DialogContentText>
414
+ </DialogContent>
415
+ <DialogActions>
416
+ <Button variant="outlined" size="medium" color="secondary" onClick={handleClose}>
417
+ Cancel
418
+ </Button>
419
+ <Button variant="contained" size="medium" color="error" onClick={handleConfirm}>
420
+ Close Ticket
421
+ </Button>
422
+ </DialogActions>
423
+ </Dialog>
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Pattern 6: Notifications
429
+
430
+ **Use case:** Success/error/warning feedback to users.
431
+
432
+ ### Component
433
+
434
+ ```typescript
435
+ // Use notistack's useSnackbar hook (preferred)
436
+ const { enqueueSnackbar } = useSnackbar();
437
+
438
+ enqueueSnackbar('Operation successful', { variant: 'success' });
439
+ enqueueSnackbar('Something went wrong', { variant: 'error' });
440
+
441
+ // OR: MUI Snackbar + Alert
442
+ <Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
443
+ <Alert severity="success" onClose={handleClose}>Message</Alert>
444
+ </Snackbar>
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Pattern 7: Empty State
450
+
451
+ **Use case:** When a list or search returns no results.
452
+
453
+ ### Example
454
+
455
+ ```tsx
456
+ <Box sx={{
457
+ display: 'flex',
458
+ flexDirection: 'column',
459
+ alignItems: 'center',
460
+ justifyContent: 'center',
461
+ py: 16
462
+ }}>
463
+ <EmptyIcon style={{ width: 64, height: 64 }} />
464
+ <Typography variant="text.lg.medium" sx={{ mt: 4 }}>
465
+ No results found
466
+ </Typography>
467
+ <Typography variant="text.sm.regular" color="var(--neutral-foreground-4-rest)" sx={{ mt: 1 }}>
468
+ Try adjusting your search or filter criteria
469
+ </Typography>
470
+ </Box>
471
+ ```
472
+
473
+ ---
474
+
475
+ ## Pattern 8: Loading State
476
+
477
+ **Use case:** While data is being fetched.
478
+
479
+ ### Example
480
+
481
+ ```tsx
482
+ // Skeleton loader (preferred)
483
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
484
+ <Skeleton variant="rectangular" height={40} />
485
+ <Skeleton variant="rectangular" height={56} />
486
+ <Skeleton variant="rectangular" height={56} />
487
+ <Skeleton variant="rectangular" height={56} />
488
+ </Box>
489
+
490
+ // Or: CircularProgress
491
+ <Box sx={{ display: 'flex', justifyContent: 'center', py: 16 }}>
492
+ <CircularProgress />
493
+ </Box>
494
+ ```
495
+
496
+ ---
497
+
498
+ ## Quick Decision Guide
499
+
500
+ | If you need to... | Use Pattern |
501
+ |-------------------|-------------|
502
+ | Show a list of records | Pattern 1: List Page |
503
+ | View single record details | Pattern 2: Detail Page |
504
+ | Collect user input | Pattern 3: Form Page |
505
+ | Show metrics/overview | Pattern 4: Dashboard |
506
+ | Confirm destructive action | Pattern 5: Dialog/Modal |
507
+ | Show feedback message | Pattern 6: Notifications |
508
+ | Handle no-data state | Pattern 7: Empty State |
509
+ | Handle loading state | Pattern 8: Loading State |