@mdguggenbichler/slugbase-core 0.0.31 → 0.0.32

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 (30) hide show
  1. package/frontend/src/components/FilterChips.tsx +5 -3
  2. package/frontend/src/components/StatCard.tsx +82 -5
  3. package/frontend/src/components/bookmarks/BookmarkCard.tsx +317 -210
  4. package/frontend/src/components/bookmarks/BookmarkTableView.tsx +47 -23
  5. package/frontend/src/components/collections/CollectionToolbar.tsx +294 -0
  6. package/frontend/src/components/collections/README.md +44 -0
  7. package/frontend/src/components/collections/index.ts +2 -0
  8. package/frontend/src/components/dashboard/DashboardHeader.tsx +16 -0
  9. package/frontend/src/components/dashboard/MostUsedTagsSection.tsx +49 -0
  10. package/frontend/src/components/dashboard/PinnedSection.tsx +110 -0
  11. package/frontend/src/components/dashboard/QuickAccessSection.tsx +120 -0
  12. package/frontend/src/components/dashboard/README.md +35 -0
  13. package/frontend/src/components/dashboard/StatsCardsRow.tsx +78 -0
  14. package/frontend/src/components/dashboard/index.ts +17 -0
  15. package/frontend/src/locales/de.json +2 -0
  16. package/frontend/src/locales/en.json +1 -0
  17. package/frontend/src/locales/es.json +2 -0
  18. package/frontend/src/locales/fr.json +2 -0
  19. package/frontend/src/locales/it.json +2 -0
  20. package/frontend/src/locales/ja.json +2 -0
  21. package/frontend/src/locales/nl.json +2 -0
  22. package/frontend/src/locales/pl.json +2 -0
  23. package/frontend/src/locales/pt.json +2 -0
  24. package/frontend/src/locales/ru.json +2 -0
  25. package/frontend/src/locales/zh.json +2 -0
  26. package/frontend/src/pages/Bookmarks.tsx +97 -214
  27. package/frontend/src/pages/Dashboard.tsx +99 -216
  28. package/frontend/src/pages/Folders.tsx +181 -251
  29. package/frontend/src/pages/Tags.tsx +87 -145
  30. package/package.json +1 -1
@@ -4,19 +4,17 @@ import { useTranslation } from 'react-i18next';
4
4
  import api from '../api/client';
5
5
  import ConfirmDialog from '../components/ui/ConfirmDialog';
6
6
  import { useConfirmDialog } from '../hooks/useConfirmDialog';
7
- import { Plus, Edit, Trash2, Share2, LayoutGrid, List, Folder, ChevronLeft, ChevronRight } from 'lucide-react';
7
+ import { Plus, Edit, Trash2, Share2, Folder, ChevronLeft, ChevronRight } from 'lucide-react';
8
8
  import FolderModal from '../components/modals/FolderModal';
9
9
  import ShareResourceDialog from '../components/sharing/ShareResourceDialog';
10
10
  import Button from '../components/ui/Button';
11
- import Select from '../components/ui/Select';
12
11
  import Tooltip from '../components/ui/Tooltip';
13
12
  import FolderIcon from '../components/FolderIcon';
14
- import { PageHeader } from '../components/PageHeader';
13
+ import { CollectionToolbar } from '../components/collections';
15
14
  import { EmptyState } from '../components/EmptyState';
16
15
  import { PageLoadingSkeleton } from '../components/ui/PageLoadingSkeleton';
17
16
  import { useAppConfig } from '../contexts/AppConfigContext';
18
- import { ScopeSegmentedControl } from '../components/ScopeSegmentedControl';
19
- import { FilterChips } from '../components/FilterChips';
17
+ import { Card } from '../components/ui/card';
20
18
 
21
19
  interface Folder {
22
20
  id: string;
@@ -49,9 +47,6 @@ export default function Folders() {
49
47
  const saved = localStorage.getItem('folders-view-mode');
50
48
  return (saved === 'list' || saved === 'card') ? saved : 'card';
51
49
  });
52
- const [compactMode, setCompactMode] = useState(() => {
53
- return localStorage.getItem('folders-compact-mode') === 'true';
54
- });
55
50
 
56
51
  const scopeParam = searchParams.get('scope');
57
52
  const scope = (scopeParam === 'mine' || scopeParam === 'shared_with_me' || scopeParam === 'shared_by_me')
@@ -71,10 +66,6 @@ export default function Folders() {
71
66
  localStorage.setItem('folders-view-mode', viewMode);
72
67
  }, [viewMode]);
73
68
 
74
- useEffect(() => {
75
- localStorage.setItem('folders-compact-mode', compactMode.toString());
76
- }, [compactMode]);
77
-
78
69
  useEffect(() => {
79
70
  loadData();
80
71
  }, [sortBy, scope, page, pageSize]);
@@ -196,104 +187,54 @@ export default function Folders() {
196
187
 
197
188
  return (
198
189
  <div className="space-y-6 pb-24">
199
- {/* Sticky controls bar: header + toolbar - stays visible when scrolling */}
200
- <div className="sticky top-0 z-40 space-y-4 pb-4 -mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8 pt-0 -mt-8 bg-background shadow-sm">
201
- <PageHeader
202
- className="pt-4"
203
- title={`${t('folders.title')} (${totalFolders})`}
204
- subtitle={
205
- hasActiveFilters || totalFolders > pageSize
206
- ? t('bookmarks.showingXOfY', { x: sortedFolders.length, y: totalFolders })
207
- : undefined
208
- }
209
- actions={
210
- <div className="flex flex-wrap items-center gap-2">
211
- <ScopeSegmentedControl
212
- value={scope}
213
- onChange={(s) => updateParams({ scope: s === 'all' ? undefined : s })}
214
- options={[
215
- { value: 'all', label: t('bookmarks.scopeAll') },
216
- { value: 'mine', label: t('bookmarks.scopeMine') },
217
- { value: 'shared_with_me', label: t('common.scopeSharedWithMe') },
218
- { value: 'shared_by_me', label: t('common.scopeSharedByMe') },
219
- ]}
220
- ariaLabel={t('bookmarks.scopeAll')}
221
- />
222
- <Button onClick={handleCreate} icon={Plus}>
223
- {t('folders.create')}
224
- </Button>
225
- </div>
226
- }
227
- />
228
-
229
- <FilterChips
230
- chips={filterChips}
231
- onRemove={handleRemoveFilter}
232
- onClearAll={handleResetFilters}
233
- clearAllLabel={t('bookmarks.clearAllFilters')}
234
- clearAllAriaLabel={t('bookmarks.clearAllFilters')}
235
- />
236
-
237
- {/* Toolbar: Sort, Page size, View Modes */}
238
- <div className="flex flex-wrap items-center gap-3 bg-card rounded-lg border p-4 shadow-sm">
239
- {/* Sort */}
240
- <div className="flex items-center gap-2">
241
- <Select
242
- value={sortBy}
243
- onChange={(value) => updateParams({ sort: value as SortOption })}
244
- options={sortOptions}
245
- className="min-w-[160px]"
246
- />
247
- </div>
248
- <div className="flex items-center gap-2">
249
- <Select
250
- value={String(pageSize)}
251
- onChange={(value) => updateParams({ limit: value })}
252
- options={PAGE_SIZE_OPTIONS.map((n) => ({ value: String(n), label: String(n) }))}
253
- className="min-w-[80px]"
254
- />
255
- <span className="text-sm text-muted-foreground whitespace-nowrap">{t('bookmarks.perPage')}</span>
256
- </div>
257
- {/* View Mode Toggle */}
258
- <div className="flex items-center gap-2 border-l border-gray-200 dark:border-gray-700 pl-3 ml-auto">
259
- <div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-700 rounded-lg p-1">
260
- <button
261
- onClick={() => setViewMode('card')}
262
- className={`p-1.5 rounded transition-colors ${
263
- viewMode === 'card'
264
- ? 'bg-card text-primary shadow-sm'
265
- : 'text-muted-foreground hover:text-foreground'
266
- }`}
267
- title={t('folders.viewCard')}
268
- >
269
- <LayoutGrid className="h-4 w-4" />
270
- </button>
271
- <button
272
- onClick={() => setViewMode('list')}
273
- className={`p-1.5 rounded transition-colors ${
274
- viewMode === 'list'
275
- ? 'bg-card text-primary shadow-sm'
276
- : 'text-muted-foreground hover:text-foreground'
277
- }`}
278
- title={t('folders.viewList')}
279
- >
280
- <List className="h-4 w-4" />
281
- </button>
282
- </div>
283
- <button
284
- onClick={() => setCompactMode(!compactMode)}
285
- className={`px-3 py-1.5 text-sm rounded-lg transition-colors ${
286
- compactMode
287
- ? 'bg-primary/20 text-primary'
288
- : 'bg-muted text-muted-foreground hover:bg-accent'
289
- }`}
290
- title={t('folders.compactMode')}
291
- >
292
- {t('folders.compactMode')}
293
- </button>
294
- </div>
295
- </div>
296
- </div>
190
+ <CollectionToolbar
191
+ title={t('folders.title')}
192
+ count={totalFolders}
193
+ subtitle={
194
+ hasActiveFilters || totalFolders > pageSize
195
+ ? t('bookmarks.showingXOfY', { x: sortedFolders.length, y: totalFolders })
196
+ : undefined
197
+ }
198
+ tabs={{
199
+ value: scope,
200
+ onChange: (s) => updateParams({ scope: s === 'all' ? undefined : s }),
201
+ options: [
202
+ { value: 'all', label: t('bookmarks.scopeAll') },
203
+ { value: 'mine', label: t('bookmarks.scopeMine') },
204
+ { value: 'shared_with_me', label: t('common.scopeSharedWithMe') },
205
+ { value: 'shared_by_me', label: t('common.scopeSharedByMe') },
206
+ ],
207
+ ariaLabel: t('bookmarks.scopeAll'),
208
+ }}
209
+ createButton={{ label: t('folders.create'), onClick: handleCreate }}
210
+ filterChips={{
211
+ chips: filterChips,
212
+ onRemove: handleRemoveFilter,
213
+ onClearAll: handleResetFilters,
214
+ clearAllLabel: t('bookmarks.clearAllFilters'),
215
+ clearAllAriaLabel: t('bookmarks.clearAllFilters'),
216
+ }}
217
+ sort={{
218
+ value: sortBy,
219
+ onChange: (value) => updateParams({ sort: value as SortOption }),
220
+ options: sortOptions,
221
+ className: 'min-w-[160px]',
222
+ }}
223
+ perPage={{
224
+ value: pageSize,
225
+ onChange: (value) => {
226
+ updateParams({ limit: String(value) });
227
+ },
228
+ options: [...PAGE_SIZE_OPTIONS],
229
+ label: t('bookmarks.perPage'),
230
+ }}
231
+ viewMode={{
232
+ value: viewMode,
233
+ onChange: setViewMode,
234
+ cardLabel: t('folders.viewCard'),
235
+ listLabel: t('folders.viewList'),
236
+ }}
237
+ />
297
238
 
298
239
  {/* Folders Display */}
299
240
  {sortedFolders.length === 0 ? (
@@ -321,109 +262,102 @@ export default function Folders() {
321
262
  />
322
263
  )
323
264
  ) : viewMode === 'card' ? (
324
- <div className={`grid grid-cols-1 gap-3 items-stretch ${
325
- compactMode
326
- ? 'sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8'
327
- : 'sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6'
328
- }`}>
329
- {sortedFolders.map((folder) => (
330
- <div
331
- key={folder.id}
332
- className={`group bg-card rounded-lg border border-border hover:border-primary/70 hover:bg-muted/50 hover:shadow-md transition-all duration-200 flex flex-col h-full min-h-0 ${compactMode ? 'p-2.5 min-h-[160px]' : 'p-2.5 min-h-[140px]'}`}
333
- >
334
- <Link
335
- to={`${prefix}/bookmarks?folder_id=${folder.id}`}
336
- className="flex-1 flex flex-col min-w-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded"
265
+ <div className="grid gap-3 items-stretch [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
266
+ {sortedFolders.map((folder) => {
267
+ const isShared = (folder.shared_teams && folder.shared_teams.length > 0) || (folder.shared_users && folder.shared_users.length > 0);
268
+ return (
269
+ <Card
270
+ key={folder.id}
271
+ className="group relative flex flex-col h-[101px] cursor-pointer rounded-lg border bg-card/95 dark:bg-card/90 transition-[border-color,box-shadow] duration-150 border-border/80 hover:border-primary/80 hover:shadow-[0_2px_6px_rgba(0,0,0,0.06)] dark:border-border/70 dark:hover:border-primary/80 dark:hover:shadow-[0_2px_6px_rgba(0,0,0,0.25)] px-3 pt-0 pb-1.5 focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2"
337
272
  >
338
- <div className="space-y-3 flex-1 flex flex-col">
339
- <div className="flex items-start gap-3">
340
- <div className={`flex-shrink-0 ${compactMode ? 'w-9 h-9' : 'w-10 h-10'} rounded-xl bg-primary/20 flex items-center justify-center border border-primary/30`}>
341
- <FolderIcon iconName={folder.icon} size={compactMode ? 18 : 20} className="text-primary" />
342
- </div>
343
- <div className="flex-1 min-w-0 pt-0.5">
344
- <h3 className={`${compactMode ? 'text-xs' : 'text-sm'} font-medium text-foreground truncate mb-1`}>
345
- {folder.name}
346
- </h3>
347
- {folder.shared_teams && folder.shared_teams.length > 0 && (
348
- <Tooltip
349
- content={
350
- <div className="space-y-1">
351
- <div className="font-semibold mb-1">{t('folders.sharedWith')}</div>
352
- {folder.shared_teams.map((team) => (
353
- <div key={team.id} className="text-xs">• {team.name}</div>
354
- ))}
355
- {folder.shared_users && folder.shared_users.length > 0 && (
356
- folder.shared_users.map((user) => (
357
- <div key={user.id} className="text-xs">• {user.name || user.email}</div>
358
- ))
359
- )}
360
- </div>
361
- }
362
- >
363
- <span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 rounded-md border border-green-200 dark:border-green-800/50 cursor-help">
364
- <Share2 className="h-3 w-3" />
365
- {t('folders.shared')}
366
- </span>
367
- </Tooltip>
368
- )}
369
- </div>
273
+ <Link
274
+ to={`${prefix}/bookmarks?folder_id=${folder.id}`}
275
+ className="absolute inset-0 rounded-lg z-0 focus:outline-none"
276
+ aria-label={folder.name}
277
+ />
278
+ <header className="flex-shrink-0 flex items-center gap-1.5 min-w-0 pt-3 relative z-10">
279
+ <div className="flex-shrink-0 w-7 h-7 rounded-md bg-background/90 dark:bg-muted/20 flex items-center justify-center border border-border/50 overflow-hidden">
280
+ <FolderIcon iconName={folder.icon} size={16} className="text-primary" />
370
281
  </div>
371
- </div>
372
- </Link>
373
- {folder.folder_type === 'own' && (
374
- <div className={`flex gap-1.5 pt-2.5 mt-auto shrink-0 border-t border-border ${compactMode ? 'pt-2' : ''}`}>
375
- <Button
376
- variant="ghost"
377
- size="sm"
378
- icon={Share2}
379
- iconClassName="h-3.5 w-3.5 stroke-[1.5]"
380
- onClick={() => { setSharingFolder(folder); setShareDialogOpen(true); }}
381
- title={t('sharing.shareFolder')}
382
- className="h-8 w-8 p-0 flex-shrink-0"
383
- />
384
- <Button
385
- variant="ghost"
386
- size="sm"
387
- icon={Edit}
388
- iconClassName="h-3.5 w-3.5 stroke-[1.5]"
389
- onClick={() => handleEdit(folder)}
390
- className="flex-1 h-8 min-w-0 text-xs"
391
- >
392
- {t('common.edit')}
393
- </Button>
394
- <Button
395
- variant="ghost"
396
- size="sm"
397
- icon={Trash2}
398
- iconClassName="h-3.5 w-3.5 stroke-[1.5]"
399
- onClick={() => handleDelete(folder.id)}
400
- title={t('common.delete')}
401
- className="h-8 w-8 p-0 flex-shrink-0 text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
402
- />
403
- </div>
404
- )}
405
- </div>
406
- ))}
282
+ <div className="flex-1 min-w-0 min-h-0 overflow-hidden">
283
+ <h3 className="text-sm font-semibold text-foreground line-clamp-2 break-words leading-snug tracking-tight min-h-0">
284
+ {folder.name}
285
+ </h3>
286
+ </div>
287
+ </header>
288
+ {isShared && (
289
+ <div className="flex-shrink-0 mt-1.5 relative z-10">
290
+ <Tooltip
291
+ content={
292
+ <div className="space-y-1">
293
+ <div className="font-semibold mb-1">{t('folders.sharedWith')}</div>
294
+ {folder.shared_teams?.map((team) => (
295
+ <div key={team.id} className="text-xs">• {team.name}</div>
296
+ ))}
297
+ {folder.shared_users?.map((user) => (
298
+ <div key={user.id} className="text-xs">• {user.name || user.email}</div>
299
+ ))}
300
+ </div>
301
+ }
302
+ >
303
+ <span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] font-medium bg-emerald-500/10 dark:bg-emerald-500/10 text-muted-foreground rounded cursor-help">
304
+ <Share2 className="h-2.5 w-2.5" />
305
+ {t('folders.shared')}
306
+ </span>
307
+ </Tooltip>
308
+ </div>
309
+ )}
310
+ <div className="flex-1 min-h-0" aria-hidden />
311
+ {folder.folder_type === 'own' && (
312
+ <footer className="flex-shrink-0 flex items-center justify-end gap-0.5 h-6 min-h-[24px] pt-2.5 relative z-10 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150 w-[76px] ml-auto">
313
+ <Button
314
+ variant="ghost"
315
+ size="sm"
316
+ icon={Share2}
317
+ iconClassName="h-3.5 w-3.5 stroke-[1.5]"
318
+ onClick={(e) => { e.preventDefault(); e.stopPropagation(); setSharingFolder(folder); setShareDialogOpen(true); }}
319
+ className="h-6 w-6 p-0 min-w-6 text-muted-foreground hover:text-foreground"
320
+ title={t('sharing.shareFolder')}
321
+ />
322
+ <Button
323
+ variant="ghost"
324
+ size="sm"
325
+ icon={Edit}
326
+ iconClassName="h-3.5 w-3.5 stroke-[1.5]"
327
+ onClick={(e) => { e.preventDefault(); e.stopPropagation(); handleEdit(folder); }}
328
+ className="h-6 w-6 p-0 min-w-6 text-muted-foreground hover:text-foreground"
329
+ title={t('common.edit')}
330
+ />
331
+ <Button
332
+ variant="ghost"
333
+ size="sm"
334
+ icon={Trash2}
335
+ iconClassName="h-3.5 w-3.5 stroke-[1.5]"
336
+ onClick={(e) => { e.preventDefault(); e.stopPropagation(); handleDelete(folder.id); }}
337
+ className="h-6 w-6 p-0 min-w-6 text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
338
+ title={t('common.delete')}
339
+ />
340
+ </footer>
341
+ )}
342
+ </Card>
343
+ );
344
+ })}
407
345
  </div>
408
346
  ) : (
409
347
  <div className="overflow-x-auto bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
410
348
  <table className="w-full">
411
349
  <thead className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-700">
412
350
  <tr>
413
- <th className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'} text-left ${compactMode ? 'text-[10px]' : 'text-xs'} font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide`}>
351
+ <th className="px-4 py-3 text-left text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">
414
352
  {t('folders.name')}
415
353
  </th>
416
- {!compactMode && (
417
- <th className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'} text-left ${compactMode ? 'text-[10px]' : 'text-xs'} font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide`}>
418
- {t('bookmarks.title')}
419
- </th>
420
- )}
421
- {!compactMode && (
422
- <th className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'} text-left ${compactMode ? 'text-[10px]' : 'text-xs'} font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide`}>
423
- {t('folders.shared')}
424
- </th>
425
- )}
426
- <th className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'} text-right ${compactMode ? 'text-[10px]' : 'text-xs'} font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide`}>
354
+ <th className="px-4 py-3 text-left text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">
355
+ {t('bookmarks.title')}
356
+ </th>
357
+ <th className="px-4 py-3 text-left text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">
358
+ {t('folders.shared')}
359
+ </th>
360
+ <th className="px-4 py-3 text-right text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">
427
361
  {t('common.actions')}
428
362
  </th>
429
363
  </tr>
@@ -432,75 +366,71 @@ export default function Folders() {
432
366
  {sortedFolders.map((folder) => (
433
367
  <tr
434
368
  key={folder.id}
435
- className={`hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors ${compactMode ? 'h-10' : ''}`}
369
+ className="hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
436
370
  >
437
- <td className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'}`}>
371
+ <td className="px-4 py-3">
438
372
  <Link
439
373
  to={`${prefix}/bookmarks?folder_id=${folder.id}`}
440
- className={`flex items-center ${compactMode ? 'gap-2' : 'gap-3'} hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded`}
374
+ className="flex items-center gap-3 hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded"
441
375
  >
442
- <div className={`flex-shrink-0 ${compactMode ? 'w-6 h-6' : 'w-8 h-8'} rounded-lg bg-primary/20 flex items-center justify-center border border-primary/30`}>
443
- <FolderIcon iconName={folder.icon} size={compactMode ? 12 : 16} className="text-primary" />
376
+ <div className="flex-shrink-0 w-8 h-8 rounded-lg bg-primary/20 flex items-center justify-center border border-primary/30">
377
+ <FolderIcon iconName={folder.icon} size={16} className="text-primary" />
444
378
  </div>
445
- <div className={`font-medium text-gray-900 dark:text-white ${compactMode ? 'text-xs' : 'text-[15px]'}`}>
379
+ <div className="font-medium text-gray-900 dark:text-white text-[15px]">
446
380
  {folder.name}
447
381
  </div>
448
382
  </Link>
449
383
  </td>
450
- {!compactMode && (
451
- <td className="px-4 py-3 text-xs text-muted-foreground">—</td>
452
- )}
453
- {!compactMode && (
454
- <td className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'}`}>
455
- {folder.shared_teams && folder.shared_teams.length > 0 ? (
456
- <Tooltip
457
- content={
458
- <div className="space-y-1">
459
- <div className="font-semibold mb-1">{t('folders.sharedWith')}</div>
460
- {folder.shared_teams.map((team) => (
461
- <div key={team.id} className="text-xs">
462
- {team.name}
463
- </div>
464
- ))}
465
- {folder.shared_users && folder.shared_users.length > 0 && (
466
- <>
467
- {folder.shared_users.map((user) => (
468
- <div key={user.id} className="text-xs">
469
- • {user.name || user.email}
470
- </div>
471
- ))}
472
- </>
473
- )}
474
- </div>
475
- }
476
- >
477
- <span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 rounded-md cursor-help">
478
- <Share2 className="h-3 w-3" />
479
- {t('folders.shared')}
480
- </span>
481
- </Tooltip>
482
- ) : (
483
- <span className="text-xs text-gray-500 dark:text-gray-400">-</span>
484
- )}
485
- </td>
486
- )}
487
- <td className={`${compactMode ? 'px-2 py-1.5' : 'px-4 py-3'}`}>
384
+ <td className="px-4 py-3 text-xs text-muted-foreground">—</td>
385
+ <td className="px-4 py-3">
386
+ {folder.shared_teams && folder.shared_teams.length > 0 ? (
387
+ <Tooltip
388
+ content={
389
+ <div className="space-y-1">
390
+ <div className="font-semibold mb-1">{t('folders.sharedWith')}</div>
391
+ {folder.shared_teams.map((team) => (
392
+ <div key={team.id} className="text-xs">
393
+ {team.name}
394
+ </div>
395
+ ))}
396
+ {folder.shared_users && folder.shared_users.length > 0 && (
397
+ <>
398
+ {folder.shared_users.map((user) => (
399
+ <div key={user.id} className="text-xs">
400
+ • {user.name || user.email}
401
+ </div>
402
+ ))}
403
+ </>
404
+ )}
405
+ </div>
406
+ }
407
+ >
408
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 rounded-md cursor-help">
409
+ <Share2 className="h-3 w-3" />
410
+ {t('folders.shared')}
411
+ </span>
412
+ </Tooltip>
413
+ ) : (
414
+ <span className="text-xs text-gray-500 dark:text-gray-400">-</span>
415
+ )}
416
+ </td>
417
+ <td className="px-4 py-3">
488
418
  {folder.folder_type === 'own' && (
489
- <div className={`flex items-center justify-end ${compactMode ? 'gap-1' : 'gap-2'}`}>
419
+ <div className="flex items-center justify-end gap-2">
490
420
  <Button
491
421
  variant="ghost"
492
422
  size="sm"
493
423
  icon={Share2}
494
424
  onClick={() => { setSharingFolder(folder); setShareDialogOpen(true); }}
495
425
  title={t('sharing.shareFolder')}
496
- className={compactMode ? 'px-1 h-6' : 'px-2'}
426
+ className="px-2"
497
427
  />
498
428
  <Button
499
429
  variant="ghost"
500
430
  size="sm"
501
431
  icon={Edit}
502
432
  onClick={() => handleEdit(folder)}
503
- className={compactMode ? 'px-1 h-6' : 'px-2'}
433
+ className="px-2"
504
434
  />
505
435
  <Button
506
436
  variant="ghost"
@@ -508,7 +438,7 @@ export default function Folders() {
508
438
  icon={Trash2}
509
439
  onClick={() => handleDelete(folder.id)}
510
440
  title={t('common.delete')}
511
- className={`text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 ${compactMode ? 'px-1 h-6' : 'px-2'}`}
441
+ className="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 px-2"
512
442
  />
513
443
  </div>
514
444
  )}