popsite-ui 1.0.0

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 (87) hide show
  1. package/App.jsx +95 -0
  2. package/README.md +92 -0
  3. package/components/layout/PortalHeader.jsx +18 -0
  4. package/components/layout/SystemSidebar.jsx +33 -0
  5. package/components/modules/AnalyticsDashboardModule.jsx +17 -0
  6. package/components/modules/ChatMessagingModule.jsx +17 -0
  7. package/components/modules/EcommerceStoreModule.jsx +17 -0
  8. package/components/modules/EventTicketBookingModule.jsx +17 -0
  9. package/components/modules/FlightBookingModule.jsx +17 -0
  10. package/components/modules/FoodOrderingModule.jsx +17 -0
  11. package/components/modules/HospitalAppointmentModule.jsx +17 -0
  12. package/components/modules/HotelBookingModule.jsx +17 -0
  13. package/components/modules/InvoiceBillingModule.jsx +17 -0
  14. package/components/modules/LibraryManagementModule.jsx +17 -0
  15. package/components/modules/ModuleContentDeck.jsx +44 -0
  16. package/components/modules/MovieBookingModule.jsx +17 -0
  17. package/components/modules/QuizExamModule.jsx +17 -0
  18. package/components/modules/StudentRegistrationModule.jsx +17 -0
  19. package/components/modules/SystemModuleRenderer.jsx +19 -0
  20. package/components/modules/SystemModuleTemplate.jsx +62 -0
  21. package/components/modules/SystemVisualWidget.jsx +123 -0
  22. package/components/modules/moduleContentMap.js +238 -0
  23. package/components/modules/moduleEnhancementsMap.js +439 -0
  24. package/components/modules/systemModuleMap.js +31 -0
  25. package/components/system/DynamicSystemForm.jsx +154 -0
  26. package/components/system/SystemHero.jsx +21 -0
  27. package/components/system/SystemSummaryCard.jsx +53 -0
  28. package/data/systems/analyticsDashboard.js +48 -0
  29. package/data/systems/chatMessaging.js +43 -0
  30. package/data/systems/ecommerceStore.js +50 -0
  31. package/data/systems/eventTicketBooking.js +50 -0
  32. package/data/systems/flightBooking.js +38 -0
  33. package/data/systems/foodOrdering.js +48 -0
  34. package/data/systems/hospitalAppointment.js +50 -0
  35. package/data/systems/hotelBooking.js +38 -0
  36. package/data/systems/index.js +31 -0
  37. package/data/systems/invoiceBilling.js +50 -0
  38. package/data/systems/libraryManagement.js +43 -0
  39. package/data/systems/movieBooking.js +48 -0
  40. package/data/systems/quizExam.js +38 -0
  41. package/data/systems/studentRegistration.js +43 -0
  42. package/dist/popsite-ui.es.js +4368 -0
  43. package/dist/popsite-ui.umd.js +60 -0
  44. package/dist/style.css +1 -0
  45. package/index.html +13 -0
  46. package/library/index.js +20 -0
  47. package/main.jsx +15 -0
  48. package/package.json +40 -0
  49. package/src/App.jsx +12 -0
  50. package/src/components/modules/AnalyticsDashboardModule.jsx +224 -0
  51. package/src/components/modules/ChatMessagingModule.jsx +294 -0
  52. package/src/components/modules/EcommerceStoreModule.jsx +405 -0
  53. package/src/components/modules/EventTicketBookingModule.jsx +253 -0
  54. package/src/components/modules/FlightBookingModule.jsx +399 -0
  55. package/src/components/modules/FoodOrderingModule.jsx +316 -0
  56. package/src/components/modules/HospitalAppointmentModule.jsx +267 -0
  57. package/src/components/modules/HotelBookingModule.jsx +317 -0
  58. package/src/components/modules/InvoiceBillingModule.jsx +302 -0
  59. package/src/components/modules/LandingPageModule.jsx +185 -0
  60. package/src/components/modules/LibraryManagementModule.jsx +189 -0
  61. package/src/components/modules/MovieBookingModule.jsx +337 -0
  62. package/src/components/modules/QuizExamModule.jsx +255 -0
  63. package/src/components/modules/StudentRegistrationModule.jsx +292 -0
  64. package/src/components/system/SystemHero.jsx +44 -0
  65. package/src/components/system/SystemSummaryCard.jsx +29 -0
  66. package/src/components/system/Toast.jsx +69 -0
  67. package/src/data/systems/analyticsDashboard.js +32 -0
  68. package/src/data/systems/chatMessaging.js +59 -0
  69. package/src/data/systems/ecommerceStore.js +84 -0
  70. package/src/data/systems/eventBooking.js +33 -0
  71. package/src/data/systems/flightBooking.js +59 -0
  72. package/src/data/systems/foodOrdering.js +48 -0
  73. package/src/data/systems/hospitalAppointment.js +48 -0
  74. package/src/data/systems/hotelBooking.js +59 -0
  75. package/src/data/systems/invoiceBilling.js +19 -0
  76. package/src/data/systems/landingPage.js +29 -0
  77. package/src/data/systems/libraryManagement.js +17 -0
  78. package/src/data/systems/movieBooking.js +49 -0
  79. package/src/data/systems/quizExam.js +31 -0
  80. package/src/data/systems/studentRegistration.js +9 -0
  81. package/src/index.js +22 -0
  82. package/src/main.jsx +10 -0
  83. package/src/styles.css +296 -0
  84. package/styles.css +820 -0
  85. package/utils/systemEngine.js +128 -0
  86. package/vite.config.js +8 -0
  87. package/vite.lib.config.js +27 -0
@@ -0,0 +1,292 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { studentRegistrationMockData } from '../../data/systems/studentRegistration';
3
+ import { useToast } from '../system/Toast';
4
+
5
+ export function StudentRegistrationModule() {
6
+ const [currentView, setCurrentView] = useState('roster'); // roster, wizard, edit
7
+ const [students, setStudents] = useState(studentRegistrationMockData.enrolledStudents);
8
+ const [searchQuery, setSearchQuery] = useState('');
9
+
10
+ // Wizard State
11
+ const [wizardStep, setWizardStep] = useState(1);
12
+ const [formData, setFormData] = useState({
13
+ firstName: '', lastName: '', email: '', phone: '', department: '', year: ''
14
+ });
15
+
16
+ // Edit State
17
+ const [editingStudent, setEditingStudent] = useState(null);
18
+
19
+ const { showToast, ToastContainer } = useToast();
20
+
21
+ useEffect(() => {
22
+ const saved = localStorage.getItem('popsite_students');
23
+ if (saved) {
24
+ try { setStudents(JSON.parse(saved)); } catch(e){}
25
+ }
26
+ }, []);
27
+
28
+ useEffect(() => {
29
+ localStorage.setItem('popsite_students', JSON.stringify(students));
30
+ }, [students]);
31
+
32
+ const validatePhone = (phone) => /^[0-9\-\+\s\(\)]{7,15}$/.test(phone);
33
+ const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
34
+
35
+ const handleNextStep = () => {
36
+ if (wizardStep === 1) {
37
+ if (!formData.firstName || !formData.lastName || !formData.email || !formData.phone) {
38
+ showToast('All fields required', 'error'); return;
39
+ }
40
+ if (!validateEmail(formData.email)) {
41
+ showToast('Invalid email format', 'error'); return;
42
+ }
43
+ if (!validatePhone(formData.phone)) {
44
+ showToast('Invalid phone format', 'error'); return;
45
+ }
46
+ } else if (wizardStep === 2) {
47
+ if (!formData.department || !formData.year) {
48
+ showToast('Please select department and year', 'error'); return;
49
+ }
50
+ }
51
+ setWizardStep(prev => prev + 1);
52
+ };
53
+
54
+ const submitRegistration = () => {
55
+ const newStudent = {
56
+ ...formData,
57
+ id: `STU-2026-${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
58
+ gpa: 0.0
59
+ };
60
+ setStudents([...students, newStudent]);
61
+ showToast(`${formData.firstName} registered successfully!`, 'success');
62
+ setCurrentView('roster');
63
+ };
64
+
65
+ const handleEditSave = () => {
66
+ setStudents(prev => prev.map(s => s.id === editingStudent.id ? editingStudent : s));
67
+ showToast('Profile updated!', 'success');
68
+ setCurrentView('roster');
69
+ };
70
+
71
+ const exportCSV = () => {
72
+ if (students.length === 0) return;
73
+ const keys = Object.keys(students[0]);
74
+ const csvContent = keys.join(',') + '\n' +
75
+ students.map(s => keys.map(k => `"${s[k]}"`).join(',')).join('\n');
76
+
77
+ const blob = new Blob([csvContent], { type: 'text/csv' });
78
+ const url = URL.createObjectURL(blob);
79
+ const a = document.createElement('a');
80
+ a.href = url;
81
+ a.download = `student_roster_${Date.now()}.csv`;
82
+ a.click();
83
+ showToast('Roster Exported to CSV', 'success');
84
+ };
85
+
86
+ const startWizard = () => {
87
+ setFormData({ firstName: '', lastName: '', email: '', phone: '', department: '', year: '' });
88
+ setWizardStep(1);
89
+ setCurrentView('wizard');
90
+ };
91
+
92
+ // Views
93
+ const renderRoster = () => {
94
+ const filtered = students.filter(s =>
95
+ `${s.firstName} ${s.lastName}`.toLowerCase().includes(searchQuery.toLowerCase()) ||
96
+ s.email.toLowerCase().includes(searchQuery.toLowerCase())
97
+ );
98
+
99
+ return (
100
+ <div className="pk-container animate-fade-in">
101
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '2rem', flexWrap: 'wrap', gap: '1rem' }}>
102
+ <div>
103
+ <h1 className="pk-heading-xl" style={{ marginBottom: '0.5rem' }}>{studentRegistrationMockData.schoolName}</h1>
104
+ <p className="pk-text-body">Admissions & Roster Management</p>
105
+ </div>
106
+ <div style={{ display: 'flex', gap: '1rem' }}>
107
+ <button className="pk-btn pk-btn-outline" onClick={exportCSV}>⬇️ Export CSV</button>
108
+ <button className="pk-btn pk-btn-primary" onClick={startWizard}>+ New Admission</button>
109
+ </div>
110
+ </div>
111
+
112
+ <div style={{ marginBottom: '2rem' }}>
113
+ <input type="text" className="pk-input" style={{ width: '100%', maxWidth: '400px' }} placeholder="Search students by name or email..." value={searchQuery} onChange={e => setSearchQuery(e.target.value)} />
114
+ </div>
115
+
116
+ <div className="pk-grid" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))' }}>
117
+ {filtered.map(st => (
118
+ <div key={st.id} className="pk-card pk-card-interactive" style={{ padding: '1.5rem' }}>
119
+ <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '1rem' }}>
120
+ <span className="pk-badge">{st.id}</span>
121
+ <span style={{ fontWeight: 'bold', color: st.gpa >= 3.5 ? 'var(--pk-success)' : 'var(--pk-text-main)' }}>GPA: {st.gpa.toFixed(1)}</span>
122
+ </div>
123
+ <h3 className="pk-heading-md" style={{ marginBottom: '0.25rem' }}>{st.firstName} {st.lastName}</h3>
124
+ <p className="pk-text-sm" style={{ color: 'var(--pk-primary)', marginBottom: '1rem' }}>{st.department} • {st.year}</p>
125
+
126
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', marginBottom: '1.5rem' }}>
127
+ <span className="pk-text-sm">📧 {st.email}</span>
128
+ <span className="pk-text-sm">📞 {st.phone}</span>
129
+ </div>
130
+
131
+ <button
132
+ className="pk-btn pk-btn-outline pk-w-full"
133
+ onClick={() => { setEditingStudent(st); setCurrentView('edit'); }}
134
+ >
135
+ Edit Profile
136
+ </button>
137
+ </div>
138
+ ))}
139
+ </div>
140
+ </div>
141
+ );
142
+ };
143
+
144
+ const renderWizard = () => (
145
+ <div className="pk-container animate-fade-in" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '80vh' }}>
146
+ <div className="pk-card" style={{ width: '100%', maxWidth: '600px' }}>
147
+ <div className="pk-card-header" style={{ padding: '2rem', borderBottom: '1px solid var(--pk-border)' }}>
148
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
149
+ <h2 className="pk-heading-lg">Admission Wizard</h2>
150
+ <button className="pk-btn pk-btn-outline" style={{ border: 'none' }} onClick={() => setCurrentView('roster')}>✕ Cancel</button>
151
+ </div>
152
+
153
+ {/* Visual Progress Bar */}
154
+ <div style={{ display: 'flex', justifyContent: 'space-between', position: 'relative' }}>
155
+ <div style={{ position: 'absolute', top: '50%', left: '0', width: '100%', height: '2px', background: 'var(--pk-border)', zIndex: 0, transform: 'translateY(-50%)' }}></div>
156
+ <div style={{ position: 'absolute', top: '50%', left: '0', width: `${((wizardStep - 1) / 2) * 100}%`, height: '2px', background: 'var(--pk-primary)', zIndex: 1, transform: 'translateY(-50%)', transition: 'width 0.3s' }}></div>
157
+
158
+ {['Personal', 'Academic', 'Review'].map((step, idx) => (
159
+ <div key={idx} style={{ position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', alignItems: 'center', width: '40px' }}>
160
+ <div style={{ width: '24px', height: '24px', borderRadius: '50%', background: wizardStep > idx ? 'var(--pk-primary)' : 'var(--pk-bg-main)', border: `2px solid ${wizardStep >= idx + 1 ? 'var(--pk-primary)' : 'var(--pk-border)'}`, color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '10px' }}>
161
+ {wizardStep > idx ? '✓' : idx + 1}
162
+ </div>
163
+ <span style={{ position: 'absolute', bottom: '-20px', fontSize: '0.75rem', color: wizardStep >= idx + 1 ? 'var(--pk-text-main)' : 'var(--pk-text-muted)' }}>{step}</span>
164
+ </div>
165
+ ))}
166
+ </div>
167
+ </div>
168
+
169
+ <div className="pk-card-body" style={{ padding: '3rem 2rem' }}>
170
+ {wizardStep === 1 && (
171
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
172
+ <div style={{ display: 'flex', gap: '1rem' }}>
173
+ <div className="pk-input-group" style={{ flex: 1 }}>
174
+ <label className="pk-label">First Name *</label>
175
+ <input type="text" className="pk-input" value={formData.firstName} onChange={e => setFormData({...formData, firstName: e.target.value})} />
176
+ </div>
177
+ <div className="pk-input-group" style={{ flex: 1 }}>
178
+ <label className="pk-label">Last Name *</label>
179
+ <input type="text" className="pk-input" value={formData.lastName} onChange={e => setFormData({...formData, lastName: e.target.value})} />
180
+ </div>
181
+ </div>
182
+ <div className="pk-input-group">
183
+ <label className="pk-label">Email Address *</label>
184
+ <input type="email" className="pk-input" value={formData.email} onChange={e => setFormData({...formData, email: e.target.value})} placeholder="student@example.com" />
185
+ </div>
186
+ <div className="pk-input-group">
187
+ <label className="pk-label">Phone Number *</label>
188
+ <input type="tel" className="pk-input" value={formData.phone} onChange={e => setFormData({...formData, phone: e.target.value})} placeholder="555-0100" />
189
+ </div>
190
+ </div>
191
+ )}
192
+
193
+ {wizardStep === 2 && (
194
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
195
+ <div className="pk-input-group">
196
+ <label className="pk-label">Target Department *</label>
197
+ <select className="pk-input" value={formData.department} onChange={e => setFormData({...formData, department: e.target.value})}>
198
+ <option value="">-- Select Department --</option>
199
+ {studentRegistrationMockData.departments.map(d => <option key={d} value={d}>{d}</option>)}
200
+ </select>
201
+ </div>
202
+ <div className="pk-input-group">
203
+ <label className="pk-label">Entry Year *</label>
204
+ <select className="pk-input" value={formData.year} onChange={e => setFormData({...formData, year: e.target.value})}>
205
+ <option value="">-- Select Level --</option>
206
+ <option value="Freshman">Freshman</option>
207
+ <option value="Transfer">Transfer</option>
208
+ <option value="Graduate">Graduate</option>
209
+ </select>
210
+ </div>
211
+ </div>
212
+ )}
213
+
214
+ {wizardStep === 3 && (
215
+ <div>
216
+ <h3 className="pk-heading-md" style={{ marginBottom: '1.5rem' }}>Review Application</h3>
217
+ <div style={{ background: 'var(--pk-bg-main)', padding: '1.5rem', borderRadius: 'var(--pk-radius-md)' }}>
218
+ <table style={{ width: '100%', textAlign: 'left' }}>
219
+ <tbody>
220
+ <tr><td style={{ padding: '0.5rem', color: 'var(--pk-text-muted)' }}>Name</td><td style={{ fontWeight: 600 }}>{formData.firstName} {formData.lastName}</td></tr>
221
+ <tr><td style={{ padding: '0.5rem', color: 'var(--pk-text-muted)' }}>Email</td><td style={{ fontWeight: 600 }}>{formData.email}</td></tr>
222
+ <tr><td style={{ padding: '0.5rem', color: 'var(--pk-text-muted)' }}>Phone</td><td style={{ fontWeight: 600 }}>{formData.phone}</td></tr>
223
+ <tr><td style={{ padding: '0.5rem', color: 'var(--pk-text-muted)' }}>Dept</td><td style={{ fontWeight: 600 }}>{formData.department}</td></tr>
224
+ <tr><td style={{ padding: '0.5rem', color: 'var(--pk-text-muted)' }}>Year</td><td style={{ fontWeight: 600 }}>{formData.year}</td></tr>
225
+ </tbody>
226
+ </table>
227
+ </div>
228
+ </div>
229
+ )}
230
+ </div>
231
+
232
+ <div className="pk-card-footer" style={{ display: 'flex', justifyContent: 'space-between', padding: '1.5rem 2rem' }}>
233
+ <button
234
+ className="pk-btn pk-btn-outline"
235
+ style={{ visibility: wizardStep === 1 ? 'hidden' : 'visible' }}
236
+ onClick={() => setWizardStep(prev => prev - 1)}
237
+ >
238
+ ← Previous
239
+ </button>
240
+ {wizardStep < 3 ? (
241
+ <button className="pk-btn pk-btn-primary" onClick={handleNextStep}>Next Step →</button>
242
+ ) : (
243
+ <button className="pk-btn pk-btn-primary" style={{ background: 'var(--pk-success)' }} onClick={submitRegistration}>Confirm Admission ✅</button>
244
+ )}
245
+ </div>
246
+ </div>
247
+ </div>
248
+ );
249
+
250
+ const renderEdit = () => {
251
+ if (!editingStudent) return null;
252
+ return (
253
+ <div className="pk-container animate-fade-in" style={{ display: 'flex', justifyContent: 'center' }}>
254
+ <div className="pk-card" style={{ width: '100%', maxWidth: '600px', padding: '3rem' }}>
255
+ <h2 className="pk-heading-lg" style={{ marginBottom: '2rem' }}>Edit Profile: {editingStudent.id}</h2>
256
+
257
+ <div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
258
+ <div className="pk-input-group" style={{ flex: 1 }}>
259
+ <label className="pk-label">First Name</label>
260
+ <input type="text" className="pk-input" value={editingStudent.firstName} onChange={e => setEditingStudent({...editingStudent, firstName: e.target.value})} />
261
+ </div>
262
+ <div className="pk-input-group" style={{ flex: 1 }}>
263
+ <label className="pk-label">Last Name</label>
264
+ <input type="text" className="pk-input" value={editingStudent.lastName} onChange={e => setEditingStudent({...editingStudent, lastName: e.target.value})} />
265
+ </div>
266
+ </div>
267
+
268
+ <div className="pk-input-group" style={{ marginBottom: '1rem' }}>
269
+ <label className="pk-label">Department</label>
270
+ <select className="pk-input" value={editingStudent.department} onChange={e => setEditingStudent({...editingStudent, department: e.target.value})}>
271
+ {studentRegistrationMockData.departments.map(d => <option key={d} value={d}>{d}</option>)}
272
+ </select>
273
+ </div>
274
+
275
+ <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '1rem', marginTop: '3rem' }}>
276
+ <button className="pk-btn pk-btn-outline" onClick={() => setCurrentView('roster')}>Cancel</button>
277
+ <button className="pk-btn pk-btn-primary" onClick={handleEditSave}>Save Profile</button>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ );
282
+ };
283
+
284
+ return (
285
+ <div style={{ position: 'relative', minHeight: '100vh', backgroundColor: 'var(--pk-bg-main)' }}>
286
+ {currentView === 'roster' && renderRoster()}
287
+ {currentView === 'wizard' && renderWizard()}
288
+ {currentView === 'edit' && renderEdit()}
289
+ <ToastContainer />
290
+ </div>
291
+ );
292
+ }
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+
3
+ export function SystemHero({ title, subtitle, bgImage, children }) {
4
+ return (
5
+ <div
6
+ className="pk-card pk-glass"
7
+ style={{
8
+ position: 'relative',
9
+ margin: '2rem 0',
10
+ padding: '4rem 2rem',
11
+ textAlign: 'center',
12
+ overflow: 'hidden',
13
+ background: bgImage
14
+ ? `linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.6)), url(${bgImage}) center/cover`
15
+ : 'var(--pk-bg-glass)',
16
+ color: bgImage ? 'white' : 'inherit'
17
+ }}
18
+ >
19
+ <div style={{ position: 'relative', zIndex: 10 }}>
20
+ <h1 className={bgImage ? 'pk-heading-xl' : 'pk-heading-xl pk-gradient-text'} style={{ marginBottom: '1rem' }}>
21
+ {title}
22
+ </h1>
23
+ <p className="pk-text-body" style={{ color: bgImage ? 'rgba(255,255,255,0.9)' : 'var(--pk-text-muted)', fontSize: '1.25rem', maxWidth: '600px', margin: '0 auto 2rem' }}>
24
+ {subtitle}
25
+ </p>
26
+ <div>
27
+ {children}
28
+ </div>
29
+ </div>
30
+ {!bgImage && (
31
+ <div style={{
32
+ position: 'absolute',
33
+ top: '-50%', left: '-10%',
34
+ width: '500px', height: '500px',
35
+ background: 'radial-gradient(circle, var(--pk-primary-light) 0%, transparent 70%)',
36
+ opacity: 0.5,
37
+ zIndex: 0,
38
+ borderRadius: '50%',
39
+ pointerEvents: 'none'
40
+ }} />
41
+ )}
42
+ </div>
43
+ );
44
+ }
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+
3
+ export function SystemSummaryCard({ title, value, icon, trend, trendLabel }) {
4
+ const isPositive = trend && trend > 0;
5
+ const isNegative = trend && trend < 0;
6
+
7
+ return (
8
+ <div className="pk-card pk-card-interactive" style={{ padding: '1.5rem', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
9
+ <div className="pk-flex pk-justify-between pk-items-center">
10
+ <h3 className="pk-label" style={{ color: 'var(--pk-text-muted)' }}>{title}</h3>
11
+ <span style={{ fontSize: '1.5rem', opacity: 0.8 }}>{icon}</span>
12
+ </div>
13
+ <div>
14
+ <div className="pk-heading-md" style={{ marginBottom: '0.5rem' }}>{value}</div>
15
+ {trend !== undefined && (
16
+ <div className="pk-flex pk-items-center" style={{ gap: '0.5rem', fontSize: '0.875rem' }}>
17
+ <span style={{
18
+ fontWeight: 600,
19
+ color: isPositive ? 'var(--pk-success)' : isNegative ? 'var(--pk-danger)' : 'var(--pk-text-muted)'
20
+ }}>
21
+ {isPositive ? '↑' : isNegative ? '↓' : '-'} {Math.abs(trend)}%
22
+ </span>
23
+ <span style={{ color: 'var(--pk-text-muted)' }}>{trendLabel || 'vs last month'}</span>
24
+ </div>
25
+ )}
26
+ </div>
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,69 @@
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ /**
4
+ * Toast System Component
5
+ * Lightweight toast system for success/error messages.
6
+ */
7
+ export function Toast({ message, type = 'success', onClose, duration = 3000 }) {
8
+ const [isVisible, setIsVisible] = useState(true);
9
+
10
+ useEffect(() => {
11
+ const timer = setTimeout(() => {
12
+ setIsVisible(false);
13
+ setTimeout(() => {
14
+ if (onClose) onClose();
15
+ }, 300); // Wait for transition to finish
16
+ }, duration);
17
+
18
+ return () => clearTimeout(timer);
19
+ }, [duration, onClose]);
20
+
21
+ if (!isVisible && !onClose) return null;
22
+
23
+ return (
24
+ <div className={`pk-toast ${type} ${isVisible ? '' : 'pk-fade-out'}`} style={{ opacity: isVisible ? 1 : 0, transition: 'opacity 0.3s ease-out' }}>
25
+ <div style={{ fontSize: '1.25rem' }}>
26
+ {type === 'success' ? '✅' : type === 'error' ? '❌' : 'ℹ️'}
27
+ </div>
28
+ <div style={{ flex: 1 }}>
29
+ <p className="pk-label" style={{ margin: 0, textTransform: 'capitalize' }}>{type}</p>
30
+ <p className="pk-text-sm" style={{ margin: 0 }}>{message}</p>
31
+ </div>
32
+ <button
33
+ onClick={() => { setIsVisible(false); if(onClose) setTimeout(onClose, 300); }}
34
+ style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '1.25rem', color: 'var(--pk-text-muted)' }}
35
+ >
36
+ ×
37
+ </button>
38
+ </div>
39
+ );
40
+ }
41
+
42
+ // Simple Toast Manager Hook for modules
43
+ export function useToast() {
44
+ const [toasts, setToasts] = useState([]);
45
+
46
+ const showToast = (message, type = 'success') => {
47
+ const id = Date.now();
48
+ setToasts(prev => [...prev, { id, message, type }]);
49
+ };
50
+
51
+ const removeToast = (id) => {
52
+ setToasts(prev => prev.filter(t => t.id !== id));
53
+ };
54
+
55
+ const ToastContainer = () => (
56
+ <div className="pk-toast-container">
57
+ {toasts.map(toast => (
58
+ <Toast
59
+ key={toast.id}
60
+ message={toast.message}
61
+ type={toast.type}
62
+ onClose={() => removeToast(toast.id)}
63
+ />
64
+ ))}
65
+ </div>
66
+ );
67
+
68
+ return { showToast, ToastContainer };
69
+ }
@@ -0,0 +1,32 @@
1
+ export const analyticsDashboardMockData = {
2
+ organizationName: "Global Tech Solutions",
3
+ kpis: {
4
+ revenue: { value: "$124,500", trend: 12.5 },
5
+ users: { value: "14,245", trend: 8.2 },
6
+ conversionRate: { value: "4.8%", trend: -1.2 },
7
+ bounceRate: { value: "32.1%", trend: -5.4 }
8
+ },
9
+ financialReports: [
10
+ { id: "RPT-001", period: "Q1 2026", revenue: 124500, expenses: 84000, net: 40500, status: "Finalized" },
11
+ { id: "RPT-002", period: "Q4 2025", revenue: 118000, expenses: 82000, net: 36000, status: "Finalized" },
12
+ { id: "RPT-003", period: "Q3 2025", revenue: 105000, expenses: 76000, net: 29000, status: "Finalized" },
13
+ { id: "RPT-004", period: "Q2 2025", revenue: 98000, expenses: 71000, net: 27000, status: "Finalized" }
14
+ ],
15
+ systemLogs: [
16
+ { id: "log1", timestamp: "2026-04-08 10:15:00", level: "INFO", message: "User array successfully synchronized.", service: "AuthService" },
17
+ { id: "log2", timestamp: "2026-04-08 10:12:45", level: "WARN", message: "API rate limit approaching for endpoint /v2/data.", service: "APIGateway" },
18
+ { id: "log3", timestamp: "2026-04-08 10:05:12", level: "ERROR", message: "Database connection timeout after 3000ms.", service: "DBService" },
19
+ { id: "log4", timestamp: "2026-04-08 09:45:00", level: "INFO", message: "Daily backup completed successfully.", service: "BackupAgent" },
20
+ { id: "log5", timestamp: "2026-04-08 09:30:22", level: "INFO", message: "New deployment v4.5.1 rolled out.", service: "CI/CD" },
21
+ { id: "log6", timestamp: "2026-04-08 09:15:00", level: "WARN", message: "High memory trace detected in worker node 4.", service: "ComputeEngine" }
22
+ ],
23
+ chartData: [
24
+ { label: "Mon", value: 30 },
25
+ { label: "Tue", value: 45 },
26
+ { label: "Wed", value: 25 },
27
+ { label: "Thu", value: 60 },
28
+ { label: "Fri", value: 85 },
29
+ { label: "Sat", value: 40 },
30
+ { label: "Sun", value: 50 }
31
+ ]
32
+ };
@@ -0,0 +1,59 @@
1
+ export const chatMessagingMockData = {
2
+ currentUser: {
3
+ id: "me",
4
+ name: "Alex Designer",
5
+ avatar: "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?auto=format&fit=crop&w=150&q=80",
6
+ status: "online"
7
+ },
8
+ conversations: [
9
+ {
10
+ id: "c1",
11
+ contact: {
12
+ id: "u1",
13
+ name: "Sarah Engineering",
14
+ avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=150&q=80",
15
+ status: "online"
16
+ },
17
+ messages: [
18
+ { id: "m1", senderId: "u1", text: "Hey! Did you get a chance to look at the new PopSite UI designs?", timestamp: "10:05 AM" },
19
+ { id: "m2", senderId: "me", text: "Yes! They look absolutely stunning. The glassmorphism is on point.", timestamp: "10:12 AM" },
20
+ { id: "m3", senderId: "u1", text: "Awesome! We should deploy the new modules by tomorrow.", timestamp: "10:14 AM" }
21
+ ],
22
+ unread: 0
23
+ },
24
+ {
25
+ id: "c2",
26
+ contact: {
27
+ id: "u2",
28
+ name: "PopSite AI Assistant",
29
+ avatar: "https://images.unsplash.com/photo-1531746020798-e6953c6e8e04?auto=format&fit=crop&w=150&q=80",
30
+ status: "online"
31
+ },
32
+ messages: [
33
+ { id: "m1", senderId: "u2", text: "Hello! I'm your virtual UI assistant. How can I help you today?", timestamp: "09:00 AM" }
34
+ ],
35
+ unread: 1
36
+ },
37
+ {
38
+ id: "c3",
39
+ contact: {
40
+ id: "u3",
41
+ name: "Marketing Team",
42
+ avatar: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?auto=format&fit=crop&w=150&q=80",
43
+ status: "offline"
44
+ },
45
+ messages: [
46
+ { id: "m1", senderId: "u3", text: "The new campaign numbers are looking good.", timestamp: "Yesterday" }
47
+ ],
48
+ unread: 0
49
+ }
50
+ ],
51
+ autoReplies: [
52
+ "That sounds great!",
53
+ "I completely agree. Let's make it happen.",
54
+ "Could you clarify that a bit more?",
55
+ "Fascinating! The possibilities are endless.",
56
+ "I've updated the system parameters accordingly.",
57
+ "Let me think about that for a second..."
58
+ ]
59
+ };
@@ -0,0 +1,84 @@
1
+ export const ecommerceMockData = {
2
+ storeName: "PopSite Originals",
3
+ categories: ["All", "Apparel", "Accessories", "Footwear", "Tech"],
4
+ products: [
5
+ {
6
+ id: "p1",
7
+ name: "Prismatic Hoodie",
8
+ price: 89.99,
9
+ category: "Apparel",
10
+ image: "https://images.unsplash.com/photo-1556821840-3a63f95609a7?auto=format&fit=crop&w=800&q=80",
11
+ description: "Premium cotton hoodie featuring a subtle iridescent logo. Perfect for chilly evenings and casual flexes.",
12
+ sizes: ["S", "M", "L", "XL"],
13
+ colors: ["Black", "Heather Grey", "Lavender"],
14
+ inStock: true,
15
+ rating: 4.8,
16
+ reviews: 124
17
+ },
18
+ {
19
+ id: "p2",
20
+ name: "Aerodynamic Runners",
21
+ price: 145.00,
22
+ category: "Footwear",
23
+ image: "https://images.unsplash.com/photo-1542291026-7eec264c27ff?auto=format&fit=crop&w=800&q=80",
24
+ description: "Ultralight sneakers designed for maximum comfort. Carbon-infused sole brings explosive returns.",
25
+ sizes: ["8", "9", "10", "11", "12"],
26
+ colors: ["Neon Red", "Obsidian"],
27
+ inStock: true,
28
+ rating: 4.9,
29
+ reviews: 89
30
+ },
31
+ {
32
+ id: "p3",
33
+ name: "Minimalist Chronograph",
34
+ price: 210.00,
35
+ category: "Accessories",
36
+ image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=800&q=80",
37
+ description: "Matte black stainless steel with a sleek sapphire crystal face. Water resistant to 50 meters.",
38
+ sizes: ["One Size"],
39
+ colors: ["Matte Black"],
40
+ inStock: false,
41
+ rating: 4.7,
42
+ reviews: 56
43
+ },
44
+ {
45
+ id: "p4",
46
+ name: "Noise-Cancelling Pods Pro",
47
+ price: 199.99,
48
+ category: "Tech",
49
+ image: "https://images.unsplash.com/photo-1606220588913-b3aecb2ac201?auto=format&fit=crop&w=800&q=80",
50
+ description: "Immersive audio experience with active noise cancellation and 24-hour battery life.",
51
+ sizes: [],
52
+ colors: ["White", "Space Gray"],
53
+ inStock: true,
54
+ rating: 4.5,
55
+ reviews: 210
56
+ },
57
+ {
58
+ id: "p5",
59
+ name: "Urban Backpack",
60
+ price: 75.00,
61
+ category: "Accessories",
62
+ image: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62?auto=format&fit=crop&w=800&q=80",
63
+ description: "Water-resistant commuter backpack with padded laptop sleeve and hidden anti-theft pockets.",
64
+ sizes: ["One Size"],
65
+ colors: ["Charcoal", "Olive Drab"],
66
+ inStock: true,
67
+ rating: 4.6,
68
+ reviews: 178
69
+ },
70
+ {
71
+ id: "p6",
72
+ name: "Graphic Tee - The Wave",
73
+ price: 35.00,
74
+ category: "Apparel",
75
+ image: "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=800&q=80",
76
+ description: "Heavyweight boxy organic cotton tee with a retro wave graphic.",
77
+ sizes: ["S", "M", "L"],
78
+ colors: ["White", "Sand"],
79
+ inStock: true,
80
+ rating: 4.3,
81
+ reviews: 45
82
+ }
83
+ ]
84
+ };
@@ -0,0 +1,33 @@
1
+ export const eventBookingMockData = {
2
+ platformName: "PopSite Tickets",
3
+ upcomingEvents: [
4
+ {
5
+ id: "ev1",
6
+ title: "Global Tech Summit 2026",
7
+ date: new Date(Date.now() + 86400000 * 5).toISOString(), // 5 days from now
8
+ location: "San Francisco Convention Center",
9
+ image: "https://images.unsplash.com/photo-1540575467063-178a50c2df87?auto=format&fit=crop&w=800&q=80",
10
+ description: "The premier gathering of global tech leaders discussing AI, web architecture, and edge computing.",
11
+ speakers: ["Dr. Evelyn Stone (AI Lead, Acme)", "Marcus Chen (Founder, WebCorp)"],
12
+ agenda: ["09:00 - Keynote", "11:30 - The Future of Vanilla CSS", "15:00 - Networking Mixer"],
13
+ tiers: [
14
+ { id: "t1", name: "General Admission", price: 299, capacity: 50 },
15
+ { id: "t2", name: "VIP Backstage", price: 899, capacity: 0 } // Sold out mock
16
+ ]
17
+ },
18
+ {
19
+ id: "ev2",
20
+ title: "Electronic Music Paradise",
21
+ date: new Date(Date.now() + 86400000 * 12).toISOString(), // 12 days from now
22
+ location: "Miami Beach Outdoor Arena",
23
+ image: "https://images.unsplash.com/photo-1470229722913-7c090be5f524?auto=format&fit=crop&w=800&q=80",
24
+ description: "A 3-day outdoor electronic music festival featuring top global DJs and immersive light shows.",
25
+ speakers: ["DJ Nova", "The Synthetics"],
26
+ agenda: ["Day 1 - Opening Ceremony", "Day 2 - Sunset Stage", "Day 3 - Fireworks Finale"],
27
+ tiers: [
28
+ { id: "t3", name: "3-Day Pass", price: 349, capacity: 1500 },
29
+ { id: "t4", name: "VIP + Artist Meet", price: 1200, capacity: 15 }
30
+ ]
31
+ }
32
+ ]
33
+ };