@djangocfg/layouts 1.0.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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/package.json +86 -0
  4. package/src/auth/README.md +962 -0
  5. package/src/auth/context/AuthContext.tsx +458 -0
  6. package/src/auth/context/index.ts +2 -0
  7. package/src/auth/context/types.ts +63 -0
  8. package/src/auth/hooks/index.ts +6 -0
  9. package/src/auth/hooks/useAuthForm.ts +329 -0
  10. package/src/auth/hooks/useAuthGuard.ts +23 -0
  11. package/src/auth/hooks/useAuthRedirect.ts +51 -0
  12. package/src/auth/hooks/useAutoAuth.ts +42 -0
  13. package/src/auth/hooks/useLocalStorage.ts +211 -0
  14. package/src/auth/hooks/useSessionStorage.ts +186 -0
  15. package/src/auth/index.ts +10 -0
  16. package/src/auth/middlewares/index.ts +1 -0
  17. package/src/auth/middlewares/proxy.ts +24 -0
  18. package/src/auth/server.ts +6 -0
  19. package/src/auth/utils/errors.ts +34 -0
  20. package/src/auth/utils/index.ts +2 -0
  21. package/src/auth/utils/validation.ts +14 -0
  22. package/src/index.ts +15 -0
  23. package/src/layouts/AppLayout/AppLayout.tsx +123 -0
  24. package/src/layouts/AppLayout/README.md +204 -0
  25. package/src/layouts/AppLayout/SUMMARY.md +240 -0
  26. package/src/layouts/AppLayout/USAGE.md +312 -0
  27. package/src/layouts/AppLayout/components/PageProgress.tsx +104 -0
  28. package/src/layouts/AppLayout/components/Seo.tsx +87 -0
  29. package/src/layouts/AppLayout/components/index.ts +6 -0
  30. package/src/layouts/AppLayout/context/AppContext.tsx +146 -0
  31. package/src/layouts/AppLayout/context/index.ts +5 -0
  32. package/src/layouts/AppLayout/hooks/index.ts +6 -0
  33. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +26 -0
  34. package/src/layouts/AppLayout/hooks/useNavigation.ts +49 -0
  35. package/src/layouts/AppLayout/index.ts +31 -0
  36. package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +51 -0
  37. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +111 -0
  38. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +40 -0
  39. package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +330 -0
  40. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +158 -0
  41. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +13 -0
  42. package/src/layouts/AppLayout/layouts/AuthLayout/types.ts +61 -0
  43. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +92 -0
  44. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +60 -0
  45. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +170 -0
  46. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +164 -0
  47. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +7 -0
  48. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +5 -0
  49. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +44 -0
  50. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +136 -0
  51. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +262 -0
  52. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +289 -0
  53. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +159 -0
  54. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +5 -0
  55. package/src/layouts/AppLayout/layouts/index.ts +7 -0
  56. package/src/layouts/AppLayout/providers/CoreProviders.tsx +47 -0
  57. package/src/layouts/AppLayout/providers/index.ts +5 -0
  58. package/src/layouts/AppLayout/types/config.ts +40 -0
  59. package/src/layouts/AppLayout/types/index.ts +10 -0
  60. package/src/layouts/AppLayout/types/layout.ts +47 -0
  61. package/src/layouts/AppLayout/types/navigation.ts +41 -0
  62. package/src/layouts/AppLayout/types/routes.ts +45 -0
  63. package/src/layouts/AppLayout/utils/index.ts +5 -0
  64. package/src/layouts/AppLayout/utils/routeDetection.ts +31 -0
  65. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +125 -0
  66. package/src/layouts/PaymentsLayout/README.md +133 -0
  67. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +172 -0
  68. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +203 -0
  69. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +100 -0
  70. package/src/layouts/PaymentsLayout/components/index.ts +4 -0
  71. package/src/layouts/PaymentsLayout/events.ts +106 -0
  72. package/src/layouts/PaymentsLayout/index.ts +20 -0
  73. package/src/layouts/PaymentsLayout/types.ts +19 -0
  74. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +109 -0
  75. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +194 -0
  76. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +3 -0
  77. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +19 -0
  78. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +99 -0
  79. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +103 -0
  80. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +138 -0
  81. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +4 -0
  82. package/src/layouts/PaymentsLayout/views/overview/index.tsx +23 -0
  83. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +282 -0
  84. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +2 -0
  85. package/src/layouts/PaymentsLayout/views/payments/index.tsx +18 -0
  86. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +29 -0
  87. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +29 -0
  88. package/src/layouts/ProfileLayout/ProfileLayout.tsx +110 -0
  89. package/src/layouts/ProfileLayout/components/AvatarSection.tsx +146 -0
  90. package/src/layouts/ProfileLayout/components/ProfileForm.tsx +208 -0
  91. package/src/layouts/ProfileLayout/components/index.ts +3 -0
  92. package/src/layouts/ProfileLayout/index.ts +3 -0
  93. package/src/layouts/SupportLayout/README.md +91 -0
  94. package/src/layouts/SupportLayout/SupportLayout.tsx +178 -0
  95. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +154 -0
  96. package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
  97. package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
  98. package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
  99. package/src/layouts/SupportLayout/components/TicketList.tsx +152 -0
  100. package/src/layouts/SupportLayout/components/index.ts +6 -0
  101. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +260 -0
  102. package/src/layouts/SupportLayout/context/index.ts +2 -0
  103. package/src/layouts/SupportLayout/events.ts +31 -0
  104. package/src/layouts/SupportLayout/hooks/index.ts +2 -0
  105. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +118 -0
  106. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +91 -0
  107. package/src/layouts/SupportLayout/index.ts +6 -0
  108. package/src/layouts/SupportLayout/types.ts +23 -0
  109. package/src/layouts/index.ts +9 -0
  110. package/src/snippets/AuthDialog/AuthDialog.tsx +88 -0
  111. package/src/snippets/AuthDialog/events.ts +21 -0
  112. package/src/snippets/AuthDialog/index.ts +3 -0
  113. package/src/snippets/AuthDialog/useAuthDialog.ts +27 -0
  114. package/src/snippets/Breadcrumbs.tsx +80 -0
  115. package/src/snippets/Chat/ChatUIContext.tsx +110 -0
  116. package/src/snippets/Chat/ChatWidget.tsx +476 -0
  117. package/src/snippets/Chat/README.md +122 -0
  118. package/src/snippets/Chat/components/MessageInput.tsx +124 -0
  119. package/src/snippets/Chat/components/MessageList.tsx +168 -0
  120. package/src/snippets/Chat/components/SessionList.tsx +192 -0
  121. package/src/snippets/Chat/components/index.ts +9 -0
  122. package/src/snippets/Chat/hooks/index.ts +6 -0
  123. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +83 -0
  124. package/src/snippets/Chat/index.tsx +44 -0
  125. package/src/snippets/Chat/types.ts +79 -0
  126. package/src/snippets/VideoPlayer/README.md +203 -0
  127. package/src/snippets/VideoPlayer/VideoControls.tsx +133 -0
  128. package/src/snippets/VideoPlayer/VideoPlayer.tsx +114 -0
  129. package/src/snippets/VideoPlayer/index.ts +8 -0
  130. package/src/snippets/VideoPlayer/types.ts +61 -0
  131. package/src/snippets/index.ts +10 -0
  132. package/src/styles/dashboard.css +41 -0
  133. package/src/styles/index.css +20 -0
  134. package/src/styles/sources.css +6 -0
  135. package/src/types/index.ts +1 -0
  136. package/src/types/pageConfig.ts +103 -0
  137. package/src/utils/index.ts +6 -0
  138. package/src/utils/logger.ts +57 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Chat Types
3
+ * Type definitions for RAG-powered chat widget
4
+ */
5
+
6
+ import type { ChatMessage as BaseChatMessage, ChatSource } from '@djangocfg/api/cfg/contexts';
7
+
8
+ // ─────────────────────────────────────────────────────────────────────────
9
+ // Extended Message Type (for UI with sources)
10
+ // ─────────────────────────────────────────────────────────────────────────
11
+
12
+ export interface ChatMessageWithSources extends BaseChatMessage {
13
+ sources?: ChatSource[];
14
+ }
15
+
16
+ // ─────────────────────────────────────────────────────────────────────────
17
+ // Widget Props
18
+ // ─────────────────────────────────────────────────────────────────────────
19
+
20
+ export interface ChatWidgetProps {
21
+ /** Whether to auto-open on mount */
22
+ autoOpen?: boolean;
23
+ /** Render even when closed (for animations) */
24
+ persistent?: boolean;
25
+ /** Additional CSS classes */
26
+ className?: string;
27
+ /** Callback when toggle state changes */
28
+ onToggle?: (isOpen: boolean) => void;
29
+ /** Callback when message is sent */
30
+ onMessage?: (message: ChatMessageWithSources) => void;
31
+ }
32
+
33
+ // ─────────────────────────────────────────────────────────────────────────
34
+ // UI State
35
+ // ─────────────────────────────────────────────────────────────────────────
36
+
37
+ export interface ChatUIState {
38
+ isOpen: boolean;
39
+ isExpanded: boolean;
40
+ isMinimized: boolean;
41
+ showSources: boolean;
42
+ showTimestamps: boolean;
43
+ }
44
+
45
+ // ─────────────────────────────────────────────────────────────────────────
46
+ // Component Props
47
+ // ─────────────────────────────────────────────────────────────────────────
48
+
49
+ export interface MessageListProps {
50
+ messages: ChatMessageWithSources[];
51
+ isLoading?: boolean;
52
+ showSources?: boolean;
53
+ showTimestamps?: boolean;
54
+ autoScroll?: boolean;
55
+ className?: string;
56
+ }
57
+
58
+ export interface MessageInputProps {
59
+ onSend: (message: string) => Promise<void>;
60
+ isLoading?: boolean;
61
+ disabled?: boolean;
62
+ placeholder?: string;
63
+ className?: string;
64
+ }
65
+
66
+ export interface SessionListProps {
67
+ isOpen: boolean;
68
+ onClose: () => void;
69
+ onSelectSession: (sessionId: string) => void;
70
+ className?: string;
71
+ }
72
+
73
+ // ─────────────────────────────────────────────────────────────────────────
74
+ // Re-export types from API
75
+ // ─────────────────────────────────────────────────────────────────────────
76
+
77
+ export type { ChatSource } from '@djangocfg/api/cfg/contexts';
78
+ export type { ChatMessage as BaseChatMessage } from '@djangocfg/api/cfg/contexts';
79
+
@@ -0,0 +1,203 @@
1
+ # VideoPlayer - Professional Vidstack Implementation
2
+
3
+ A professional, accessible video player built with Vidstack React that supports YouTube, Vimeo, MP4, HLS, and more.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Multi-platform support**: YouTube, Vimeo, MP4, HLS, DASH
8
+ - ✅ **Custom controls**: Professional UI with hover effects
9
+ - ✅ **Accessibility**: Full keyboard navigation and screen reader support
10
+ - ✅ **Responsive**: Works on all screen sizes
11
+ - ✅ **TypeScript**: Full type safety
12
+ - ✅ **Themes**: Default, minimal, and modern themes
13
+ - ✅ **No recommendations**: Clean playback without YouTube distractions
14
+
15
+ ## Installation
16
+
17
+ The VideoPlayer is already included in the UI package with all dependencies:
18
+
19
+ ```bash
20
+ pnpm add @vidstack/react@next media-icons@next
21
+ ```
22
+
23
+ ## Basic Usage
24
+
25
+ ```tsx
26
+ import { VideoPlayer } from '@repo/ui/snippets/VideoPlayer';
27
+
28
+ function MyComponent() {
29
+ return (
30
+ <VideoPlayer
31
+ source={{
32
+ url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
33
+ title: 'Never Gonna Give You Up',
34
+ description: 'Rick Astley - Never Gonna Give You Up (Official Video)'
35
+ }}
36
+ autoplay={false}
37
+ controls={true}
38
+ className="max-w-4xl mx-auto"
39
+ />
40
+ );
41
+ }
42
+ ```
43
+
44
+ ## Advanced Usage
45
+
46
+ ```tsx
47
+ import { VideoPlayer, VideoPlayerRef } from '@repo/ui/snippets/VideoPlayer';
48
+ import { useRef } from 'react';
49
+
50
+ function AdvancedPlayer() {
51
+ const playerRef = useRef<VideoPlayerRef>(null);
52
+
53
+ const handleCustomPlay = () => {
54
+ playerRef.current?.play();
55
+ };
56
+
57
+ return (
58
+ <VideoPlayer
59
+ ref={playerRef}
60
+ source={{
61
+ url: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
62
+ title: 'Big Buck Bunny',
63
+ poster: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg',
64
+ duration: 596
65
+ }}
66
+ theme="modern"
67
+ autoplay={false}
68
+ muted={false}
69
+ playsInline={true}
70
+ showInfo={true}
71
+ onPlay={() => console.log('Video started')}
72
+ onPause={() => console.log('Video paused')}
73
+ onEnded={() => console.log('Video ended')}
74
+ onError={(error) => console.error('Video error:', error)}
75
+ />
76
+ );
77
+ }
78
+ ```
79
+
80
+ ## Supported Video Sources
81
+
82
+ ### YouTube
83
+ ```tsx
84
+ <VideoPlayer
85
+ source={{
86
+ url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
87
+ title: 'YouTube Video'
88
+ }}
89
+ />
90
+ ```
91
+
92
+ ### Vimeo
93
+ ```tsx
94
+ <VideoPlayer
95
+ source={{
96
+ url: 'https://vimeo.com/76979871',
97
+ title: 'Vimeo Video'
98
+ }}
99
+ />
100
+ ```
101
+
102
+ ### Direct MP4
103
+ ```tsx
104
+ <VideoPlayer
105
+ source={{
106
+ url: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
107
+ title: 'Direct MP4',
108
+ poster: 'https://example.com/poster.jpg'
109
+ }}
110
+ />
111
+ ```
112
+
113
+ ### HLS Stream
114
+ ```tsx
115
+ <VideoPlayer
116
+ source={{
117
+ url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
118
+ title: 'HLS Stream'
119
+ }}
120
+ />
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### VideoPlayerProps
126
+
127
+ | Prop | Type | Default | Description |
128
+ |------|------|---------|-------------|
129
+ | `source` | `VideoSource` | - | Video source configuration |
130
+ | `aspectRatio` | `number` | `16/9` | Video aspect ratio |
131
+ | `autoplay` | `boolean` | `false` | Auto-play video |
132
+ | `muted` | `boolean` | `false` | Mute video by default |
133
+ | `playsInline` | `boolean` | `true` | Play inline on mobile |
134
+ | `controls` | `boolean` | `true` | Show custom controls |
135
+ | `showInfo` | `boolean` | `false` | Show video info below player |
136
+ | `theme` | `'default' \| 'minimal' \| 'modern'` | `'default'` | Player theme |
137
+ | `className` | `string` | - | Custom CSS class |
138
+ | `onPlay` | `() => void` | - | Play event callback |
139
+ | `onPause` | `() => void` | - | Pause event callback |
140
+ | `onEnded` | `() => void` | - | End event callback |
141
+ | `onError` | `(error: string) => void` | - | Error event callback |
142
+
143
+ ### VideoSource
144
+
145
+ | Property | Type | Description |
146
+ |----------|------|-------------|
147
+ | `url` | `string` | Video URL (YouTube, Vimeo, MP4, HLS, etc.) |
148
+ | `title` | `string?` | Video title |
149
+ | `description` | `string?` | Video description |
150
+ | `poster` | `string?` | Custom poster/thumbnail URL |
151
+ | `duration` | `number?` | Video duration in seconds |
152
+
153
+ ### VideoPlayerRef Methods
154
+
155
+ | Method | Description |
156
+ |--------|-------------|
157
+ | `play()` | Play the video |
158
+ | `pause()` | Pause the video |
159
+ | `togglePlay()` | Toggle play/pause |
160
+ | `seekTo(time: number)` | Seek to specific time |
161
+ | `setVolume(volume: number)` | Set volume (0-1) |
162
+ | `toggleMute()` | Toggle mute |
163
+ | `enterFullscreen()` | Enter fullscreen |
164
+ | `exitFullscreen()` | Exit fullscreen |
165
+
166
+ ## Themes
167
+
168
+ ### Default Theme
169
+ Clean, professional look with rounded corners and subtle shadows.
170
+
171
+ ### Minimal Theme
172
+ No rounded corners, minimal styling for embedding in tight spaces.
173
+
174
+ ### Modern Theme
175
+ Enhanced shadows and larger border radius for a contemporary look.
176
+
177
+ ## Accessibility
178
+
179
+ The VideoPlayer includes full accessibility support:
180
+
181
+ - ✅ Keyboard navigation (Space, Arrow keys, F for fullscreen)
182
+ - ✅ Screen reader announcements
183
+ - ✅ Focus indicators
184
+ - ✅ ARIA labels and roles
185
+ - ✅ High contrast support
186
+
187
+ ## Performance
188
+
189
+ - ✅ Lazy loading of video content
190
+ - ✅ Efficient re-renders with Vidstack's optimized state management
191
+ - ✅ Minimal bundle size impact
192
+ - ✅ Hardware-accelerated playback when available
193
+
194
+ ## Browser Support
195
+
196
+ Supports all modern browsers through Vidstack's comprehensive compatibility layer:
197
+
198
+ - ✅ Chrome 63+
199
+ - ✅ Firefox 67+
200
+ - ✅ Safari 12+
201
+ - ✅ Edge 79+
202
+ - ✅ iOS Safari 12+
203
+ - ✅ Chrome Android 63+
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Custom Video Controls for Vidstack Player
3
+ */
4
+
5
+ 'use client';
6
+
7
+ import React from 'react';
8
+ import { useMediaStore, useMediaRemote } from '@vidstack/react';
9
+ import type { MediaPlayerElement } from 'vidstack';
10
+ import { Play, Pause, Volume2, VolumeX, Maximize, Minimize } from 'lucide-react';
11
+ import { cn } from '@djangocfg/ui';
12
+
13
+ interface VideoControlsProps {
14
+ player: React.RefObject<MediaPlayerElement | null>;
15
+ className?: string;
16
+ }
17
+
18
+ export function VideoControls({ player, className }: VideoControlsProps) {
19
+ const store = useMediaStore(player);
20
+ const remote = useMediaRemote();
21
+
22
+ const isPaused = store.paused;
23
+ const isMuted = store.muted;
24
+ const isFullscreen = store.fullscreen;
25
+ const currentTime = store.currentTime;
26
+ const duration = store.duration;
27
+ const volume = store.volume;
28
+
29
+ const formatTime = (seconds: number): string => {
30
+ if (!seconds || seconds < 0) return '0:00';
31
+ const minutes = Math.floor(seconds / 60);
32
+ const secs = Math.floor(seconds % 60);
33
+ return `${minutes}:${secs.toString().padStart(2, '0')}`;
34
+ };
35
+
36
+ const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
37
+ if (!duration) return;
38
+ const rect = e.currentTarget.getBoundingClientRect();
39
+ const clickX = e.clientX - rect.left;
40
+ const percentage = clickX / rect.width;
41
+ const newTime = percentage * duration;
42
+ remote.seek(newTime);
43
+ };
44
+
45
+ const handleVolumeChange = (e: React.MouseEvent<HTMLDivElement>) => {
46
+ const rect = e.currentTarget.getBoundingClientRect();
47
+ const clickX = e.clientX - rect.left;
48
+ const percentage = Math.max(0, Math.min(1, clickX / rect.width));
49
+ remote.changeVolume(percentage);
50
+ if (percentage > 0 && isMuted) {
51
+ remote.toggleMuted();
52
+ }
53
+ };
54
+
55
+ const progress = duration > 0 ? (currentTime / duration) * 100 : 0;
56
+
57
+ return (
58
+ <div className={cn(
59
+ "absolute inset-0 flex flex-col justify-end opacity-0 hover:opacity-100 focus-within:opacity-100 transition-opacity duration-300",
60
+ "bg-gradient-to-t from-black/80 via-black/20 to-transparent",
61
+ className
62
+ )}>
63
+ {/* Progress Bar */}
64
+ <div className="px-4 pb-2">
65
+ <div
66
+ className="h-1.5 bg-white/20 rounded-full cursor-pointer hover:h-2 transition-all group"
67
+ onClick={handleProgressClick}
68
+ >
69
+ <div
70
+ className="h-full bg-primary rounded-full transition-all relative group-hover:bg-primary/90"
71
+ style={{ width: `${progress}%` }}
72
+ >
73
+ <div className="absolute right-0 top-1/2 -translate-y-1/2 w-3 h-3 bg-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity" />
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ {/* Control Bar */}
79
+ <div className="flex items-center gap-4 px-4 pb-4">
80
+ {/* Play/Pause */}
81
+ <button
82
+ onClick={() => remote.togglePaused()}
83
+ className="text-white hover:text-primary transition-colors p-1.5 hover:bg-white/10 rounded-full"
84
+ >
85
+ {isPaused ? <Play className="h-6 w-6" /> : <Pause className="h-6 w-6" />}
86
+ </button>
87
+
88
+ {/* Time */}
89
+ <div className="text-white text-sm font-medium">
90
+ {formatTime(currentTime)} / {formatTime(duration)}
91
+ </div>
92
+
93
+ <div className="flex-1" />
94
+
95
+ {/* Volume Control */}
96
+ <div className="flex items-center gap-2 group/volume">
97
+ <button
98
+ onClick={() => remote.toggleMuted()}
99
+ className="text-white hover:text-primary transition-colors p-1.5 hover:bg-white/10 rounded-full"
100
+ >
101
+ {isMuted || volume === 0 ? (
102
+ <VolumeX className="h-5 w-5" />
103
+ ) : (
104
+ <Volume2 className="h-5 w-5" />
105
+ )}
106
+ </button>
107
+
108
+ <div
109
+ className="w-0 group-hover/volume:w-20 transition-all overflow-hidden"
110
+ >
111
+ <div
112
+ className="h-1.5 bg-white/20 rounded-full cursor-pointer hover:h-2 transition-all"
113
+ onClick={handleVolumeChange}
114
+ >
115
+ <div
116
+ className="h-full bg-white rounded-full transition-all"
117
+ style={{ width: `${volume * 100}%` }}
118
+ />
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ {/* Fullscreen */}
124
+ <button
125
+ onClick={() => isFullscreen ? remote.exitFullscreen() : remote.enterFullscreen()}
126
+ className="text-white hover:text-primary transition-colors p-1.5 hover:bg-white/10 rounded-full"
127
+ >
128
+ {isFullscreen ? <Minimize className="h-5 w-5" /> : <Maximize className="h-5 w-5" />}
129
+ </button>
130
+ </div>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Professional VideoPlayer - Vidstack Implementation
3
+ * Supports YouTube, Vimeo, MP4, HLS and more with custom controls
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import React, { forwardRef, useImperativeHandle, useRef } from 'react';
9
+ import { MediaPlayer, MediaOutlet } from '@vidstack/react';
10
+ import { MediaRemoteControl, type MediaPlayerElement } from 'vidstack';
11
+ import { cn } from '@djangocfg/ui';
12
+ import { type VideoPlayerProps, type VideoPlayerRef } from './types';
13
+ import { VideoControls } from './VideoControls';
14
+
15
+ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
16
+ source,
17
+ aspectRatio = 16 / 9,
18
+ autoplay = false,
19
+ muted = false,
20
+ playsInline = true,
21
+ controls = true,
22
+ className,
23
+ showInfo = false,
24
+ theme = 'default',
25
+ onPlay,
26
+ onPause,
27
+ onEnded,
28
+ onError,
29
+ }, ref) => {
30
+ const playerRef = useRef<MediaPlayerElement | null>(null);
31
+
32
+ // Expose player methods via ref
33
+ useImperativeHandle(ref, () => {
34
+ const getRemote = () => {
35
+ if (!playerRef.current) return null;
36
+ const remote = new MediaRemoteControl();
37
+ remote.setTarget(playerRef.current as unknown as EventTarget);
38
+ return remote;
39
+ };
40
+
41
+ return {
42
+ play: () => getRemote()?.play(),
43
+ pause: () => getRemote()?.pause(),
44
+ togglePlay: () => getRemote()?.togglePaused(),
45
+ seekTo: (time: number) => getRemote()?.seek(time),
46
+ setVolume: (volume: number) => getRemote()?.changeVolume(Math.max(0, Math.min(1, volume))),
47
+ toggleMute: () => getRemote()?.toggleMuted(),
48
+ enterFullscreen: () => getRemote()?.enterFullscreen(),
49
+ exitFullscreen: () => getRemote()?.exitFullscreen(),
50
+ };
51
+ }, []);
52
+
53
+ const handlePlay = () => {
54
+ onPlay?.();
55
+ };
56
+
57
+ const handlePause = () => {
58
+ onPause?.();
59
+ };
60
+
61
+ const handleEnded = () => {
62
+ onEnded?.();
63
+ };
64
+
65
+ const handleError = (detail: any) => {
66
+ onError?.(detail?.message || 'Video playback error');
67
+ };
68
+
69
+ return (
70
+ <div className={cn("w-full", className)}>
71
+ {/* Video Player */}
72
+ <div
73
+ className={cn(
74
+ "relative w-full overflow-hidden rounded-lg bg-black",
75
+ theme === 'minimal' && "rounded-none",
76
+ theme === 'modern' && "rounded-xl shadow-2xl"
77
+ )}
78
+ style={{ aspectRatio: aspectRatio }}
79
+ >
80
+ <MediaPlayer
81
+ ref={playerRef}
82
+ title={source.title || 'Video'}
83
+ src={source.url}
84
+ poster={source.poster}
85
+ autoPlay={autoplay}
86
+ muted={muted}
87
+ playsInline={playsInline}
88
+ onPlay={handlePlay}
89
+ onPause={handlePause}
90
+ onEnded={handleEnded}
91
+ onError={handleError}
92
+ className="w-full h-full"
93
+ >
94
+ <MediaOutlet />
95
+
96
+ {/* Custom controls */}
97
+ {controls && <VideoControls player={playerRef} />}
98
+ </MediaPlayer>
99
+ </div>
100
+
101
+ {/* Video Info */}
102
+ {showInfo && source.title && (
103
+ <div className="mt-4 space-y-2">
104
+ <h3 className="text-xl font-semibold text-foreground">{source.title}</h3>
105
+ {source.description && (
106
+ <p className="text-muted-foreground">{source.description}</p>
107
+ )}
108
+ </div>
109
+ )}
110
+ </div>
111
+ );
112
+ });
113
+
114
+ VideoPlayer.displayName = 'VideoPlayer';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Professional VideoPlayer - Vidstack Implementation
3
+ * Export all components and types
4
+ */
5
+
6
+ export { VideoPlayer } from './VideoPlayer';
7
+ export { VideoControls } from './VideoControls';
8
+ export type { VideoSource, VideoPlayerProps, VideoPlayerRef } from './types';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * VideoPlayer Types - Professional Vidstack Implementation
3
+ */
4
+
5
+ export interface VideoSource {
6
+ /** Video URL - supports YouTube, Vimeo, MP4, HLS, etc. */
7
+ url: string;
8
+ /** Video title */
9
+ title?: string;
10
+ /** Video description */
11
+ description?: string;
12
+ /** Custom poster/thumbnail URL */
13
+ poster?: string;
14
+ /** Video duration in seconds */
15
+ duration?: number;
16
+ }
17
+
18
+ export interface VideoPlayerProps {
19
+ /** Video source configuration */
20
+ source: VideoSource;
21
+ /** Aspect ratio (default: 16/9) */
22
+ aspectRatio?: number;
23
+ /** Auto-play video */
24
+ autoplay?: boolean;
25
+ /** Mute video by default */
26
+ muted?: boolean;
27
+ /** Play video inline on mobile */
28
+ playsInline?: boolean;
29
+ /** Show custom controls */
30
+ controls?: boolean;
31
+ /** Custom CSS class */
32
+ className?: string;
33
+ /** Show video info */
34
+ showInfo?: boolean;
35
+ /** Player theme */
36
+ theme?: 'default' | 'minimal' | 'modern';
37
+ /** Event callbacks */
38
+ onPlay?: () => void;
39
+ onPause?: () => void;
40
+ onEnded?: () => void;
41
+ onError?: (error: string) => void;
42
+ }
43
+
44
+ export interface VideoPlayerRef {
45
+ /** Play video */
46
+ play: () => void;
47
+ /** Pause video */
48
+ pause: () => void;
49
+ /** Toggle play/pause */
50
+ togglePlay: () => void;
51
+ /** Seek to time */
52
+ seekTo: (time: number) => void;
53
+ /** Set volume (0-1) */
54
+ setVolume: (volume: number) => void;
55
+ /** Toggle mute */
56
+ toggleMute: () => void;
57
+ /** Enter fullscreen */
58
+ enterFullscreen: () => void;
59
+ /** Exit fullscreen */
60
+ exitFullscreen: () => void;
61
+ }
@@ -0,0 +1,10 @@
1
+ // ============================================================================
2
+ // Snippets - Reusable UI Snippets
3
+ // ============================================================================
4
+
5
+ export { KnowledgeChat, ChatWidget, ChatUIProvider, useChatUI } from './Chat';
6
+ export type { ChatWidgetProps, ChatUIState } from './Chat';
7
+
8
+ export * from './Breadcrumbs';
9
+ export * from './AuthDialog';
10
+ export * from './VideoPlayer';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Dashboard Layout Styles
3
+ * Specific styles for dashboard layout components
4
+ * (Header, Sidebar, etc.)
5
+ */
6
+
7
+ /**
8
+ * Dashboard Header
9
+ * Sticky header with solid background
10
+ */
11
+ .dashboard-header {
12
+ position: sticky;
13
+ top: 0;
14
+ z-index: 50;
15
+ height: 4rem;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: space-between;
19
+ padding: 0.5rem 1rem;
20
+ background-color: hsl(var(--background));
21
+ border-bottom: 1px solid hsl(var(--border));
22
+ }
23
+
24
+ /**
25
+ * Dashboard Sidebar
26
+ * Collapsible sidebar with icon mode
27
+ */
28
+ .dashboard-sidebar {
29
+ /* Sidebar-specific styles can be added here if needed */
30
+ /* Most styling is handled by shadcn/ui Sidebar component */
31
+ }
32
+
33
+ /**
34
+ * Dashboard Main Content
35
+ * Main content area with proper spacing
36
+ */
37
+ .dashboard-main {
38
+ flex: 1;
39
+ padding: 1.5rem;
40
+ overflow-y: auto;
41
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Unrealon Layouts Styles
3
+ * Layout-specific styles for @djangocfg/layouts package
4
+ *
5
+ * This file contains styles specific to layout components:
6
+ * - Dashboard layout
7
+ * - Public layout
8
+ * - Header/Sidebar components
9
+ *
10
+ * Usage: Import this file in your app after importing @djangocfg/ui styles
11
+ */
12
+
13
+ /* Source detection for Tailwind v4 monorepo */
14
+ @import "./sources.css";
15
+
16
+ /* Dashboard layout styles */
17
+ @import "./dashboard.css";
18
+
19
+ /* Public layout styles can be added here */
20
+ /* @import "./public.css"; */
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tailwind v4 Source Detection for Layouts Package
3
+ *
4
+ * @source directive tells Tailwind v4 where to scan for utility classes.
5
+ */
6
+ @source "../components";
@@ -0,0 +1 @@
1
+ export type { PageConfig, PageWithConfig } from "./pageConfig";