@juv/codego-react-ui 3.1.5 → 3.1.7

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 CHANGED
@@ -201,6 +201,173 @@ input[type="datetime-local"]::-webkit-inner-spin-button {
201
201
  }
202
202
  ```
203
203
 
204
+ ## in LARAVEL "resources\css\app.css"
205
+ ```css
206
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Inter:wght@400;500;600&display=swap');
207
+ @import "tailwindcss";
208
+
209
+ @source "../../resources/js";
210
+ @source "../../node_modules/@juv/codego-react-ui/dist";
211
+
212
+ @theme {
213
+ --font-sans: "Space Grotesk", "Inter", ui-sans-serif, system-ui, sans-serif;
214
+ --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
215
+
216
+ --color-background: var(--background);
217
+ --color-foreground: var(--foreground);
218
+ --color-card: var(--card);
219
+ --color-card-foreground: var(--card-foreground);
220
+ --color-popover: var(--popover);
221
+ --color-popover-foreground: var(--popover-foreground);
222
+ --color-primary: var(--primary);
223
+ --color-primary-hover: var(--primary-hover);
224
+ --color-primary-foreground: var(--primary-foreground);
225
+ --color-secondary: var(--secondary);
226
+ --color-secondary-hover: var(--secondary-hover);
227
+ --color-secondary-foreground: var(--secondary-foreground);
228
+ --color-muted: var(--muted);
229
+ --color-muted-foreground: var(--muted-foreground);
230
+ --color-accent: var(--accent);
231
+ --color-accent-foreground: var(--accent-foreground);
232
+ --color-destructive: var(--destructive);
233
+ --color-destructive-foreground: var(--destructive-foreground);
234
+ --color-border: var(--border);
235
+ --color-input: var(--input);
236
+ --color-ring: var(--ring);
237
+ --color-info: var(--info);
238
+ --color-info-hover: var(--info-hover);
239
+ --color-info-foreground: var(--info-foreground);
240
+ --color-warning: var(--warning);
241
+ --color-warning-hover: var(--warning-hover);
242
+ --color-warning-foreground: var(--warning-foreground);
243
+ --color-danger: var(--danger);
244
+ --color-danger-hover: var(--danger-hover);
245
+ --color-danger-foreground: var(--danger-foreground);
246
+ --color-success: var(--success);
247
+ --color-success-hover: var(--success-hover);
248
+ --color-success-foreground: var(--success-foreground);
249
+ --radius: var(--radius);
250
+ }
251
+
252
+ @layer base {
253
+ :root {
254
+ --background: #ffffff;
255
+ --foreground: #09090b;
256
+ --card: #ffffff;
257
+ --card-foreground: #09090b;
258
+ --popover: #ffffff;
259
+ --popover-foreground: #09090b;
260
+ --primary: #6366f1; /* Indigo 500 */
261
+ --primary-hover: #4f46e5;
262
+ --primary-foreground: #ffffff;
263
+ --secondary: #f4f4f5;
264
+ --secondary-hover: #e4e4e7;
265
+ --secondary-foreground: #18181b;
266
+ --info: #3b82f6; /* Blue 500 */
267
+ --info-hover: #2563eb;
268
+ --info-foreground: #ffffff;
269
+ --warning: #f59e0b; /* Amber 500 */
270
+ --warning-hover: #d97706;
271
+ --warning-foreground: #ffffff;
272
+ --danger: #ef4444; /* Red 500 */
273
+ --danger-hover: #dc2626;
274
+ --danger-foreground: #ffffff;
275
+ --success: #22c55e; /* Green 500 */
276
+ --success-hover: #16a34a;
277
+ --success-foreground: #ffffff;
278
+ --muted: #f4f4f5;
279
+ --muted-foreground: #71717a;
280
+ --accent: #f4f4f5;
281
+ --accent-foreground: #18181b;
282
+ --destructive: #ef4444;
283
+ --destructive-foreground: #ffffff;
284
+ --border: #e4e4e7;
285
+ --input: #e4e4e7;
286
+ --ring: var(--primary);
287
+ --radius: 0.75rem;
288
+ }
289
+
290
+ .dark {
291
+ --background: #030303; /* Deep black */
292
+ --foreground: #ededed;
293
+ --card: #0a0a0a; /* Slightly lighter black */
294
+ --card-foreground: #ededed;
295
+ --popover: #0a0a0a;
296
+ --popover-foreground: #ededed;
297
+ --primary: #8b5cf6; /* Neon Violet */
298
+ --primary-hover: #7c3aed;
299
+ --primary-foreground: #ffffff;
300
+ --secondary: #171717;
301
+ --secondary-hover: #262626;
302
+ --secondary-foreground: #ededed;
303
+ --info: #3b82f6;
304
+ --info-hover: #2563eb;
305
+ --info-foreground: #ffffff;
306
+ --warning: #f59e0b;
307
+ --warning-hover: #d97706;
308
+ --warning-foreground: #ffffff;
309
+ --danger: #ef4444;
310
+ --danger-hover: #dc2626;
311
+ --danger-foreground: #ffffff;
312
+ --success: #22c55e;
313
+ --success-hover: #16a34a;
314
+ --success-foreground: #ffffff;
315
+ --muted: #171717;
316
+ --muted-foreground: #a1a1aa;
317
+ --accent: #1f1f2e; /* Subtle purple tint */
318
+ --accent-foreground: #c4b5fd;
319
+ --destructive: #7f1d1d;
320
+ --destructive-foreground: #ededed;
321
+ --border: #1f1f1f;
322
+ --input: #1f1f1f;
323
+ --ring: var(--primary);
324
+ }
325
+ }
326
+
327
+ @layer base {
328
+ * {
329
+ @apply border-border;
330
+ }
331
+ body {
332
+ @apply bg-background text-foreground;
333
+ font-feature-settings: "rlig" 1, "calt" 1;
334
+ }
335
+ }
336
+
337
+ @layer utilities {
338
+ .glass {
339
+ @apply bg-background/60 backdrop-blur-xl border border-white/10;
340
+ }
341
+ .dark .glass {
342
+ @apply bg-black/40 border-white/5;
343
+ }
344
+ .text-gradient {
345
+ background: linear-gradient(to right, var(--primary), var(--info));
346
+ -webkit-background-clip: text;
347
+ -webkit-text-fill-color: transparent;
348
+ background-clip: text;
349
+ }
350
+ .glow {
351
+ box-shadow: 0 0 20px -5px var(--primary);
352
+ }
353
+ }
354
+ /* Hide calendar icon (Chrome, Edge, Safari) */
355
+ input[type="datetime-local"]::-webkit-calendar-picker-indicator {
356
+ display: none;
357
+ -webkit-appearance: none;
358
+ }
359
+
360
+ /* Optional: remove clear (X) button */
361
+ input[type="datetime-local"]::-webkit-clear-button {
362
+ display: none;
363
+ }
364
+
365
+ /* Optional: remove spin buttons */
366
+ input[type="datetime-local"]::-webkit-inner-spin-button {
367
+ display: none;
368
+ }
369
+
370
+ ```
204
371
  ### 3. Lucide React (icons)
205
372
 
206
373
  ```bash
@@ -265,6 +432,7 @@ import { Button, Card, Modal } from "@juv/codego-react-ui"
265
432
  | Avatar Stack | `AvatarStack` |
266
433
  | Badge | `Badge` |
267
434
  | Breadcrumb | `Breadcrumb` |
435
+ | Bulletin Board | `BulletinBoard`, `BulletinPreview`, `useServerBulletin` |
268
436
  | Button | `Button` |
269
437
  | Calendar | `Calendar` |
270
438
  | Card | `Card` |
@@ -320,6 +488,105 @@ import { Button, Card, Modal } from "@juv/codego-react-ui"
320
488
 
321
489
  ---
322
490
 
491
+ ## BulletinBoard Props
492
+
493
+ | Prop | Type | Default | Description |
494
+ |---|---|---|---|
495
+ | `items` | `BulletinItem[]` | — | ✓ Array of bulletin post items. |
496
+ | `layout` | `"grid" \| "list" \| "masonry"` | `"grid"` | Board layout mode. |
497
+ | `columns` | `1 \| 2 \| 3 \| 4` | `3` | Number of grid columns (responsive). |
498
+ | `variant` | `"card" \| "minimal" \| "bordered"` | `"card"` | Visual style of each post card. |
499
+ | `searchable` | `boolean` | `false` | Show a search input above the board. |
500
+ | `filterable` | `boolean` | `false` | Show category filter chips above the board. |
501
+ | `categories` | `string[]` | | Explicit category list. Auto-derived from items if omitted. |
502
+ | `title` | `ReactNode` | `"Bulletin Board"` | Board header title. |
503
+ | `headerAction` | `ReactNode` | | Trailing element in the board header (e.g. a New Post button). |
504
+ | `showHeader` | `boolean` | `true` | Show or hide the board header bar. |
505
+ | `emptyMessage` | `ReactNode` | `"No posts found."` | Content shown when the filtered list is empty. |
506
+ | `loading` | `boolean` | `false` | Show skeleton cards instead of real content. |
507
+ | `loadingCount` | `number` | `6` | Number of skeleton cards to render while loading. |
508
+ | `preview` | `boolean` | `false` | Open a `BulletinPreview` modal when a card is clicked. |
509
+ | `onEdit` | `(item: BulletinItem) => void` | | Called when the Edit button is clicked inside the preview. |
510
+ | `onDelete` | `(item: BulletinItem) => void` | | Called after a successful delete (or when no `deleteBaseUrl` is set). |
511
+ | `deleteBaseUrl` | `string` | | Base URL for built-in `DELETE {baseUrl}/{id}/delete` request. |
512
+ | `deleteIdKey` | `string` | `"id"` | Item key used as the id segment in the delete URL. |
513
+ | `serverPagination` | `BulletinServerPaginationProp \| null` | | Pass the `serverPagination` from `useServerBulletin` to enable server-driven pagination. |
514
+ | `onItemClick` | `(item: BulletinItem) => void` | | Fired when a card is clicked (ignored when `preview=true`). |
515
+ | `className` | `string` | | Additional CSS classes on the outer wrapper. |
516
+
517
+ ### BulletinPreview Props
518
+
519
+ | Prop | Type | Required | Description |
520
+ |---|---|---|---|
521
+ | `item` | `BulletinItem` | ✓ | The bulletin item to display. |
522
+ | `onClose` | `() => void` | ✓ | Called when the modal is dismissed. |
523
+ | `onEdit` | `(item: BulletinItem) => void` | | Show an Edit button; called when clicked. |
524
+ | `onDelete` | `(item: BulletinItem) => void` | | Show a Delete button; called when clicked. |
525
+
526
+ ### useServerBulletin Options
527
+
528
+ | Option | Type | Required | Description |
529
+ |---|---|---|---|
530
+ | `url` | `string` | ✓ | API endpoint. `?page=N` appended automatically. |
531
+ | `params` | `Record<string, string \| number>` | | Extra query params merged on every request. |
532
+ | `encrypt` | `boolean` | | Expect a Laravel-encrypted response payload. |
533
+ | `key` | `string` | | Laravel `APP_KEY` for decryption. |
534
+ | `decryptPayloadLog` | `boolean` | | Log the decrypted payload to the console. |
535
+ | `transform` | `(row: any) => BulletinItem` | | Map a raw API row to a `BulletinItem`. |
536
+
537
+ ### useServerBulletin Return
538
+
539
+ | Field | Type | Description |
540
+ |---|---|---|
541
+ | `items` | `BulletinItem[]` | Fetched (and optionally transformed) items. |
542
+ | `loading` | `boolean` | `true` while the request is in-flight. |
543
+ | `error` | `string \| null` | Error message if the request failed. |
544
+ | `pagination` | `ServerPagination \| null` | Raw pagination metadata. |
545
+ | `serverPagination` | `BulletinServerPaginationProp \| null` | Pass directly as `<BulletinBoard serverPagination={...} />`. |
546
+ | `goToPage` | `(page: number) => void` | Navigate to a specific page. |
547
+ | `reload` | `() => void` | Re-fetch the current page (e.g. after a delete). |
548
+
549
+ ### useServerBulletin Example
550
+
551
+ ```tsx
552
+ import { BulletinBoard, useServerBulletin } from "@juv/codego-react-ui"
553
+
554
+ function AnnouncementsPage() {
555
+ const { items, loading, serverPagination, reload } = useServerBulletin({
556
+ url: "/api/bulletins",
557
+ params: { per_page: 9 },
558
+ transform: (row) => ({
559
+ id: row.id,
560
+ title: row.subject,
561
+ body: row.content,
562
+ author: row.posted_by,
563
+ date: row.created_at,
564
+ category: row.department,
565
+ priority: row.level,
566
+ pinned: row.is_pinned,
567
+ tags: row.tags ?? [],
568
+ }),
569
+ })
570
+
571
+ return (
572
+ <BulletinBoard
573
+ items={items}
574
+ loading={loading}
575
+ serverPagination={serverPagination}
576
+ columns={3}
577
+ searchable
578
+ filterable
579
+ preview
580
+ onEdit={(item) => openEditModal(item)}
581
+ deleteBaseUrl="/api/bulletins"
582
+ onDelete={() => reload()}
583
+ />
584
+ )
585
+ }
586
+ ```
587
+
588
+ ---
589
+
323
590
  ## LeafletMap Props
324
591
 
325
592
  | Prop | Type | Default | Description |