ptechcore_ui 0.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 (57) hide show
  1. package/eslint.config.js +28 -0
  2. package/index.html +78 -0
  3. package/package.json +42 -0
  4. package/postcss.config.js +6 -0
  5. package/src/App.tsx +156 -0
  6. package/src/assets/imgs/login_illustration.png +0 -0
  7. package/src/components/common/Buttons.tsx +39 -0
  8. package/src/components/common/Cards.tsx +18 -0
  9. package/src/components/common/FDrawer.tsx +2448 -0
  10. package/src/components/common/FDrawer.types.ts +191 -0
  11. package/src/components/common/Inputs.tsx +409 -0
  12. package/src/components/common/Modals.tsx +41 -0
  13. package/src/components/common/Navigations.tsx +0 -0
  14. package/src/components/common/Toast.tsx +0 -0
  15. package/src/components/demo/ToastDemo.tsx +73 -0
  16. package/src/components/layout/Header.tsx +202 -0
  17. package/src/components/layout/ModernDoubleSidebarLayout.tsx +727 -0
  18. package/src/components/layout/PrivateLayout.tsx +52 -0
  19. package/src/components/layout/Sidebar.tsx +182 -0
  20. package/src/components/ui/Toast.tsx +93 -0
  21. package/src/contexts/SessionContext.tsx +77 -0
  22. package/src/contexts/ThemeContext.tsx +58 -0
  23. package/src/contexts/ToastContext.tsx +94 -0
  24. package/src/index.css +3 -0
  25. package/src/main.tsx +10 -0
  26. package/src/models/Organization.ts +47 -0
  27. package/src/models/Plan.ts +42 -0
  28. package/src/models/User.ts +23 -0
  29. package/src/pages/Analytics.tsx +101 -0
  30. package/src/pages/CreateOrganization.tsx +215 -0
  31. package/src/pages/Dashboard.tsx +15 -0
  32. package/src/pages/Home.tsx +12 -0
  33. package/src/pages/Profile.tsx +313 -0
  34. package/src/pages/Settings.tsx +382 -0
  35. package/src/pages/Team.tsx +180 -0
  36. package/src/pages/auth/Login.tsx +140 -0
  37. package/src/pages/auth/Register.tsx +302 -0
  38. package/src/pages/organizations/DetailEntity.tsx +1002 -0
  39. package/src/pages/organizations/DetailOrganizations.tsx +1629 -0
  40. package/src/pages/organizations/ListOrganizations.tsx +270 -0
  41. package/src/pages/pricings/CartPlan.tsx +486 -0
  42. package/src/pages/pricings/ListPricing.tsx +321 -0
  43. package/src/pages/users/CreateUser.tsx +450 -0
  44. package/src/pages/users/ListUsers.tsx +0 -0
  45. package/src/services/AuthServices.ts +94 -0
  46. package/src/services/OrganizationServices.ts +61 -0
  47. package/src/services/PlanSubscriptionServices.tsx +137 -0
  48. package/src/services/UserServices.ts +36 -0
  49. package/src/services/api.ts +64 -0
  50. package/src/styles/theme.ts +383 -0
  51. package/src/utils/utils.ts +48 -0
  52. package/src/vite-env.d.ts +1 -0
  53. package/tailwind.config.js +158 -0
  54. package/tsconfig.app.json +24 -0
  55. package/tsconfig.json +7 -0
  56. package/tsconfig.node.json +22 -0
  57. package/vite.config.ts +10 -0
@@ -0,0 +1,52 @@
1
+ import React, { useState } from 'react';
2
+ import Header from './Header';
3
+ import Sidebar from './Sidebar';
4
+
5
+ interface PrivateLayoutProps {
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ const RewiseLayout: React.FC<PrivateLayoutProps> = ({ children }) => {
10
+ const [sidebarOpen, setSidebarOpen] = useState(false);
11
+
12
+ return (
13
+ <div className="min-h-screen bg-gray-100">
14
+ {/* Header */}
15
+ <Header
16
+ onMenuClick={() => setSidebarOpen(!sidebarOpen)}
17
+ sidebarOpen={sidebarOpen}
18
+ />
19
+
20
+ <div className="flex">
21
+ {/* Sidebar */}
22
+ <Sidebar
23
+ isOpen={sidebarOpen}
24
+ onClose={() => setSidebarOpen(false)}
25
+ />
26
+
27
+ {/* Main Content */}
28
+ <main
29
+ className={`
30
+ flex-1 transition-all duration-300 ease-in-out
31
+ ${sidebarOpen ? 'lg:ml-64' : 'lg:ml-16'}
32
+ pt-16
33
+ `}
34
+ >
35
+ <div className="p-6">
36
+ {children}
37
+ </div>
38
+ </main>
39
+ </div>
40
+
41
+ {/* Overlay for mobile */}
42
+ {sidebarOpen && (
43
+ <div
44
+ className="fixed inset-0 z-20 bg-black bg-opacity-50 lg:hidden"
45
+ onClick={() => setSidebarOpen(false)}
46
+ />
47
+ )}
48
+ </div>
49
+ );
50
+ };
51
+
52
+ export default RewiseLayout;
@@ -0,0 +1,182 @@
1
+ import React from 'react';
2
+ import { useLocation, useNavigate } from 'react-router-dom';
3
+ import {
4
+ Home,
5
+ BarChart3,
6
+ Users,
7
+ Calendar,
8
+ FileText,
9
+ Settings,
10
+ HelpCircle,
11
+ ChevronLeft,
12
+ ChevronRight,
13
+ Building2
14
+ } from 'lucide-react';
15
+
16
+ interface SidebarProps {
17
+ isOpen: boolean;
18
+ onClose: () => void;
19
+ }
20
+
21
+ interface MenuItem {
22
+ id: string;
23
+ label: string;
24
+ icon: React.ComponentType<any>;
25
+ path: string;
26
+ badge?: number;
27
+ }
28
+
29
+ const Sidebar: React.FC<SidebarProps> = ({ isOpen, onClose }) => {
30
+ const location = useLocation();
31
+ const navigate = useNavigate();
32
+
33
+ const menuItems: MenuItem[] = [
34
+ {
35
+ id: 'dashboard',
36
+ label: 'Tableau de bord',
37
+ icon: Home,
38
+ path: '/dashboard'
39
+ },
40
+ {
41
+ id: 'analytics',
42
+ label: 'Analyses',
43
+ icon: BarChart3,
44
+ path: '/analytics'
45
+ },
46
+ {
47
+ id: 'team',
48
+ label: 'Équipe',
49
+ icon: Users,
50
+ path: '/team',
51
+ badge: 3
52
+ },
53
+ {
54
+ id: 'organizations',
55
+ label: 'Organisations',
56
+ icon: Building2,
57
+ path: '/organizations'
58
+ },
59
+ {
60
+ id: 'calendar',
61
+ label: 'Calendrier',
62
+ icon: Calendar,
63
+ path: '/calendar'
64
+ },
65
+ {
66
+ id: 'reports',
67
+ label: 'Rapports',
68
+ icon: FileText,
69
+ path: '/reports'
70
+ }
71
+ ];
72
+
73
+ const bottomMenuItems: MenuItem[] = [
74
+ {
75
+ id: 'settings',
76
+ label: 'Paramètres',
77
+ icon: Settings,
78
+ path: '/settings'
79
+ },
80
+ {
81
+ id: 'help',
82
+ label: 'Aide',
83
+ icon: HelpCircle,
84
+ path: '/help'
85
+ }
86
+ ];
87
+
88
+ const handleMenuClick = (path: string) => {
89
+ navigate(path);
90
+ // Close sidebar on mobile after navigation
91
+ if (window.innerWidth < 1024) {
92
+ onClose();
93
+ }
94
+ };
95
+
96
+ const isActiveRoute = (path: string) => {
97
+ return location.pathname === path || location.pathname.startsWith(path + '/');
98
+ };
99
+
100
+ const MenuItemComponent = ({ item }: { item: MenuItem }) => {
101
+ const Icon = item.icon;
102
+ const isActive = isActiveRoute(item.path);
103
+
104
+ return (
105
+ <button
106
+ onClick={() => handleMenuClick(item.path)}
107
+ className={`
108
+ w-full flex items-center space-x-3 px-3 py-3 rounded-lg transition-all duration-200
109
+ ${isActive
110
+ ? 'bg-[#8290A9] text-white shadow-md'
111
+ : 'text-gray-600 hover:bg-gray-100 hover:text-gray-800'
112
+ }
113
+ ${!isOpen ? 'justify-center' : ''}
114
+ `}
115
+ title={!isOpen ? item.label : undefined}
116
+ >
117
+ <Icon className={`w-5 h-5 ${isActive ? 'text-white' : ''}`} />
118
+ {isOpen && (
119
+ <>
120
+ <span className="font-medium">{item.label}</span>
121
+ {item.badge && (
122
+ <span className="ml-auto bg-red-500 text-white text-xs rounded-full px-2 py-1 min-w-[20px] h-5 flex items-center justify-center">
123
+ {item.badge}
124
+ </span>
125
+ )}
126
+ </>
127
+ )}
128
+ </button>
129
+ );
130
+ };
131
+
132
+ return (
133
+ <>
134
+ {/* Sidebar */}
135
+ <aside
136
+ className={`
137
+ fixed left-0 top-16 h-[calc(100vh-4rem)] bg-white border-r border-gray-200 z-30
138
+ transition-all duration-300 ease-in-out
139
+ ${isOpen ? 'w-64' : 'w-16'}
140
+ ${isOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
141
+ `}
142
+ >
143
+ <div className="flex flex-col h-full">
144
+ {/* Main Navigation */}
145
+ <nav className="flex-1 px-3 py-4">
146
+ <div className="space-y-2">
147
+ {menuItems.map((item) => (
148
+ <MenuItemComponent key={item.id} item={item} />
149
+ ))}
150
+ </div>
151
+ </nav>
152
+
153
+ {/* Bottom Navigation */}
154
+ <nav className="px-3 py-4 border-t border-gray-200">
155
+ <div className="space-y-2">
156
+ {bottomMenuItems.map((item) => (
157
+ <MenuItemComponent key={item.id} item={item} />
158
+ ))}
159
+ </div>
160
+ </nav>
161
+
162
+ {/* Collapse/Expand Button (Desktop only) */}
163
+ <div className="hidden lg:block px-3 py-2 border-t border-gray-200">
164
+ <button
165
+ onClick={onClose}
166
+ className="w-full flex items-center justify-center py-2 text-gray-500 hover:text-gray-700 transition-colors"
167
+ title={isOpen ? 'Réduire la sidebar' : 'Étendre la sidebar'}
168
+ >
169
+ {isOpen ? (
170
+ <ChevronLeft className="w-4 h-4" />
171
+ ) : (
172
+ <ChevronRight className="w-4 h-4" />
173
+ )}
174
+ </button>
175
+ </div>
176
+ </div>
177
+ </aside>
178
+ </>
179
+ );
180
+ };
181
+
182
+ export default Sidebar;
@@ -0,0 +1,93 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Toast as ToastType, useToast } from '../../contexts/ToastContext';
3
+ import { CheckCircle, XCircle, AlertTriangle, Info, X } from 'lucide-react';
4
+
5
+ interface ToastItemProps {
6
+ toast: ToastType;
7
+ }
8
+
9
+ const ToastItem: React.FC<ToastItemProps> = ({ toast }) => {
10
+ const { removeToast } = useToast();
11
+ const [isVisible, setIsVisible] = useState(false);
12
+ const [isLeaving, setIsLeaving] = useState(false);
13
+
14
+ useEffect(() => {
15
+ // Trigger entrance animation
16
+ const timer = setTimeout(() => setIsVisible(true), 10);
17
+ return () => clearTimeout(timer);
18
+ }, []);
19
+
20
+ const handleClose = () => {
21
+ setIsLeaving(true);
22
+ setTimeout(() => {
23
+ removeToast(toast.id);
24
+ }, 300);
25
+ };
26
+
27
+ const getIcon = () => {
28
+ switch (toast.type) {
29
+ case 'success':
30
+ return <CheckCircle className="w-5 h-5 text-green-600" />;
31
+ case 'error':
32
+ return <XCircle className="w-5 h-5 text-red-600" />;
33
+ case 'warning':
34
+ return <AlertTriangle className="w-5 h-5 text-yellow-600" />;
35
+ case 'info':
36
+ return <Info className="w-5 h-5 text-blue-600" />;
37
+ }
38
+ };
39
+
40
+ const getBackgroundColor = () => {
41
+ switch (toast.type) {
42
+ case 'success':
43
+ return 'bg-green-50 border-green-200';
44
+ case 'error':
45
+ return 'bg-red-50 border-red-200';
46
+ case 'warning':
47
+ return 'bg-yellow-50 border-yellow-200';
48
+ case 'info':
49
+ return 'bg-blue-50 border-blue-200';
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div
55
+ className={`
56
+ transform transition-all duration-300 ease-in-out
57
+ ${isVisible && !isLeaving ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'}
58
+ ${getBackgroundColor()}
59
+ max-w-sm w-full border rounded-lg p-4 shadow-lg
60
+ flex items-start space-x-3
61
+ `}
62
+ >
63
+ <div className="flex-shrink-0">
64
+ {getIcon()}
65
+ </div>
66
+ <div className="flex-1 min-w-0">
67
+ <p className="text-sm text-gray-900 font-medium">
68
+ {toast.message}
69
+ </p>
70
+ </div>
71
+ <button
72
+ onClick={handleClose}
73
+ className="flex-shrink-0 ml-4 text-gray-400 hover:text-gray-600 transition-colors"
74
+ >
75
+ <X className="w-4 h-4" />
76
+ </button>
77
+ </div>
78
+ );
79
+ };
80
+
81
+ const ToastContainer: React.FC = () => {
82
+ const { toasts } = useToast();
83
+
84
+ return (
85
+ <div className="fixed top-4 right-4 z-50 space-y-3">
86
+ {toasts.map((toast) => (
87
+ <ToastItem key={toast.id} toast={toast} />
88
+ ))}
89
+ </div>
90
+ );
91
+ };
92
+
93
+ export default ToastContainer;
@@ -0,0 +1,77 @@
1
+ import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
2
+ import { AuthServices } from '../services/AuthServices';
3
+ import { User } from '../models/User';
4
+
5
+
6
+
7
+
8
+ interface SessionContextType {
9
+ isAuthenticated: boolean;
10
+ token: string | null;
11
+ loggedUser: User | null;
12
+ login: (token: string) => void;
13
+ logout: () => void;
14
+ }
15
+
16
+ const SessionContext = createContext<SessionContextType | undefined>(undefined);
17
+
18
+ export const useSession = () => {
19
+ const context = useContext(SessionContext);
20
+ if (!context) {
21
+ throw new Error('useSession must be used within a SessionProvider');
22
+ }
23
+ return context;
24
+ };
25
+
26
+ export const SessionProvider = ({ children }: { children: ReactNode }) => {
27
+ const [token, setToken] = useState<string | null>(localStorage.getItem('token'));
28
+ const [loggedUser, setLoggedUser] = useState<User | null>(null);
29
+
30
+ useEffect(() => {
31
+ const storedToken = localStorage.getItem('token');
32
+ if (storedToken) {
33
+ setToken(storedToken);
34
+ }
35
+ }, []);
36
+
37
+ const login = (newToken: string) => {
38
+ localStorage.setItem('token', newToken);
39
+ setToken(newToken);
40
+ };
41
+
42
+ const logout = () => {
43
+ localStorage.removeItem('token');
44
+ setToken(null);
45
+ };
46
+
47
+ useEffect(() => {
48
+ if (token) {
49
+ AuthServices.getUserInformations(token)
50
+ .then((res) => {
51
+ const result = res as { success: boolean; message?: string; data?: any };
52
+ if (result.success === true) {
53
+ setLoggedUser(result.data.user);
54
+ } else {
55
+ setLoggedUser(null);
56
+ }
57
+ })
58
+ .catch(() => setLoggedUser(null));
59
+ } else {
60
+ setLoggedUser(null);
61
+ }
62
+ }, [token]);
63
+
64
+
65
+
66
+ return (
67
+ <SessionContext.Provider value={{
68
+ isAuthenticated: !!token,
69
+ loggedUser,
70
+ token,
71
+ login,
72
+ logout,
73
+ }}>
74
+ {children}
75
+ </SessionContext.Provider>
76
+ );
77
+ };
@@ -0,0 +1,58 @@
1
+ import React, { createContext, useContext, useState, useEffect } from 'react';
2
+ import { themes, defaultTheme, getThemeCSSVariables, Theme, ThemeType, ThemeContextValue } from '../styles/theme';
3
+
4
+ const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
5
+
6
+ export const useTheme = () => {
7
+ const context = useContext(ThemeContext);
8
+ if (!context) {
9
+ throw new Error('useTheme must be used within a ThemeProvider');
10
+ }
11
+ return context;
12
+ };
13
+
14
+ interface ThemeProviderProps {
15
+ children: React.ReactNode;
16
+ }
17
+
18
+ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
19
+ // Récupérer le thème sauvegardé ou utiliser le thème par défaut
20
+ const [themeType, setThemeType] = useState<ThemeType>(() => {
21
+ const saved = localStorage.getItem('wisebook-theme') as ThemeType;
22
+ return saved && saved in themes ? saved : 'minimalist';
23
+ });
24
+
25
+ const [theme, setThemeState] = useState<Theme>(themes[themeType] || defaultTheme);
26
+
27
+ // Appliquer les variables CSS du thème
28
+ useEffect(() => {
29
+ const root = document.documentElement;
30
+ const cssVars = getThemeCSSVariables(theme);
31
+
32
+ Object.entries(cssVars).forEach(([key, value]) => {
33
+ root.style.setProperty(key, value);
34
+ });
35
+
36
+ // Appliquer les styles globaux
37
+ root.style.backgroundColor = theme.colors.background;
38
+ root.style.color = theme.colors.text.primary;
39
+
40
+ // Sauvegarder le choix
41
+ localStorage.setItem('wisebook-theme', themeType);
42
+ }, [theme, themeType]);
43
+
44
+ const setTheme = (type: ThemeType) => {
45
+ if (type in themes) {
46
+ setThemeType(type);
47
+ setThemeState(themes[type]);
48
+ }
49
+ };
50
+
51
+ return (
52
+ <ThemeContext.Provider value={{ theme, themeType, setTheme }}>
53
+ {children}
54
+ </ThemeContext.Provider>
55
+ );
56
+ };
57
+
58
+ export default ThemeProvider;
@@ -0,0 +1,94 @@
1
+ import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
2
+
3
+ export interface Toast {
4
+ id: string;
5
+ message: string;
6
+ type: 'success' | 'error' | 'warning' | 'info';
7
+ duration?: number;
8
+ }
9
+
10
+ interface ToastContextType {
11
+ toasts: Toast[];
12
+ addToast: (toast: Omit<Toast, 'id'>) => void;
13
+ removeToast: (id: string) => void;
14
+ success: (message: string, duration?: number) => void;
15
+ error: (message: string, duration?: number) => void;
16
+ warning: (message: string, duration?: number) => void;
17
+ info: (message: string, duration?: number) => void;
18
+ }
19
+
20
+ const ToastContext = createContext<ToastContextType | undefined>(undefined);
21
+
22
+ export const useToast = (): ToastContextType => {
23
+ const context = useContext(ToastContext);
24
+ if (!context) {
25
+ throw new Error('useToast must be used within a ToastProvider');
26
+ }
27
+ return context;
28
+ };
29
+
30
+ interface ToastProviderProps {
31
+ children: ReactNode;
32
+ }
33
+
34
+ export const ToastProvider: React.FC<ToastProviderProps> = ({ children }) => {
35
+ const [toasts, setToasts] = useState<Toast[]>([]);
36
+
37
+ const generateId = (): string => {
38
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
39
+ };
40
+
41
+ const addToast = useCallback((toast: Omit<Toast, 'id'>) => {
42
+ const id = generateId();
43
+ const newToast: Toast = {
44
+ id,
45
+ duration: 5000,
46
+ ...toast,
47
+ };
48
+
49
+ setToasts(prev => [...prev, newToast]);
50
+
51
+ // Auto remove toast after duration
52
+ if (newToast.duration && newToast.duration > 0) {
53
+ setTimeout(() => {
54
+ removeToast(id);
55
+ }, newToast.duration);
56
+ }
57
+ }, []);
58
+
59
+ const removeToast = useCallback((id: string) => {
60
+ setToasts(prev => prev.filter(toast => toast.id !== id));
61
+ }, []);
62
+
63
+ const success = useCallback((message: string, duration?: number) => {
64
+ addToast({ message, type: 'success', duration });
65
+ }, [addToast]);
66
+
67
+ const error = useCallback((message: string, duration?: number) => {
68
+ addToast({ message, type: 'error', duration });
69
+ }, [addToast]);
70
+
71
+ const warning = useCallback((message: string, duration?: number) => {
72
+ addToast({ message, type: 'warning', duration });
73
+ }, [addToast]);
74
+
75
+ const info = useCallback((message: string, duration?: number) => {
76
+ addToast({ message, type: 'info', duration });
77
+ }, [addToast]);
78
+
79
+ const value: ToastContextType = {
80
+ toasts,
81
+ addToast,
82
+ removeToast,
83
+ success,
84
+ error,
85
+ warning,
86
+ info,
87
+ };
88
+
89
+ return (
90
+ <ToastContext.Provider value={value}>
91
+ {children}
92
+ </ToastContext.Provider>
93
+ );
94
+ };
package/src/index.css ADDED
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
package/src/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App.tsx';
4
+ import './index.css';
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1,47 @@
1
+ import { Subscription } from "./Plan";
2
+
3
+ export interface Organization {
4
+ id: number;
5
+ legal_name: string;
6
+ trading_name?: string;
7
+ phone?: string;
8
+ email?: string;
9
+ address?: string;
10
+ owner: number;
11
+ active_subscription?: Subscription | null;
12
+ modules?: any[];
13
+ created_at: string;
14
+ updated_at: string;
15
+ is_deleted: boolean;
16
+ }
17
+
18
+ export interface Entity {
19
+ id: number;
20
+ organization: number;
21
+ legal_name: string;
22
+ trading_name?: string;
23
+ phone?: string;
24
+ email?: string;
25
+ logo?: string;
26
+ legal_representative_name?: string;
27
+ legal_representative_phone?: string;
28
+ legal_representative_email?: string;
29
+ country?: string;
30
+ city?: string;
31
+ address?: string;
32
+ rib?: string;
33
+ iban?: string;
34
+ bank_name?: string;
35
+ bank_address?: string;
36
+ tax_account?: string;
37
+ rccm?: string;
38
+ currency?: string;
39
+ centers_taxes?: string;
40
+ point_of_sale?: string;
41
+ establishment?: string;
42
+ fne_url?: string;
43
+ fne_auth_key?: string;
44
+ regime_taxes?: string;
45
+ balance_sticker_fne?: number;
46
+
47
+ }
@@ -0,0 +1,42 @@
1
+ export interface Module {
2
+ id: number;
3
+ code: string;
4
+ name: string;
5
+ description?: string;
6
+ price: number;
7
+ is_active: boolean;
8
+ price_monthly: number;
9
+ price_yearly: number;
10
+ created_at: string;
11
+ updated_at: string;
12
+ is_deleted: boolean;
13
+ }
14
+
15
+ export interface Plan {
16
+ id: number;
17
+ name: string;
18
+ description?: string;
19
+ price_monthly: number;
20
+ price_yearly: number;
21
+ max_users: number;
22
+ max_entities: number;
23
+ modules_included: Module[] | number[];
24
+ is_private: boolean;
25
+ private_for?: number | null;
26
+ created_at: string;
27
+ updated_at: string;
28
+ is_deleted: boolean;
29
+ }
30
+
31
+
32
+ export interface Subscription {
33
+ id: number;
34
+ organization: number;
35
+ plan: Plan;
36
+ start_date: string;
37
+ end_date: string;
38
+ is_active: boolean;
39
+ created_at: string;
40
+ updated_at: string;
41
+ extra_modules?: Module[] | number[];
42
+ }
@@ -0,0 +1,23 @@
1
+
2
+
3
+ export interface User {
4
+ id: number;
5
+ last_login: string | null;
6
+ is_superuser: boolean;
7
+ username: string;
8
+ first_name: string;
9
+ last_name: string;
10
+ email: string;
11
+ is_staff: boolean;
12
+ is_active: boolean;
13
+ date_joined: string;
14
+ is_deleted: boolean;
15
+ deleted_at: string | null;
16
+ phonenumber: string | null;
17
+ groups: any[];
18
+ user_permissions: any[];
19
+ centers_access?: Array<{
20
+ id: number;
21
+ permissions?: number[];
22
+ }>;
23
+ }