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,191 @@
1
+ import React, { ReactNode } from 'react';
2
+
3
+ export interface FDrawerHeader {
4
+ key: string;
5
+ name: string;
6
+ type?: string;
7
+ filterable?: boolean;
8
+ formule?: (item: any) => string;
9
+ search_name?: string;
10
+ }
11
+
12
+ export interface FilterOption {
13
+ value: string;
14
+ label: string;
15
+ }
16
+
17
+ export interface Filter {
18
+ label: string;
19
+ type: string;
20
+ key: string;
21
+ filter_options?: FilterOption[];
22
+ }
23
+
24
+ export interface BtnListItem {
25
+ type: string;
26
+ label?: string;
27
+ to?: string;
28
+ target?: string;
29
+ dropdown?: any[];
30
+ onclick?: () => void;
31
+ icon?: any[];
32
+ icon_type?: string;
33
+ }
34
+
35
+ export interface ActionItem {
36
+ label: string;
37
+ onclick?: (row: any) => void;
38
+ to?: (row: any) => string;
39
+ condition?: (row: any) => boolean;
40
+ permission?: string;
41
+ }
42
+
43
+ export interface ReponseDetail {
44
+ total_pages: number | null;
45
+ previous: string | null;
46
+ next: string | null;
47
+ current_page: number | null;
48
+ paginate?: {
49
+ next: string;
50
+ };
51
+ }
52
+
53
+ export interface ViewAction {
54
+ label: string;
55
+ onclick?: () => void;
56
+ }
57
+
58
+ export interface SummaryRow {
59
+ label: string;
60
+ contents: Array<(param: any) => string | number>;
61
+ }
62
+
63
+ export interface Summary {
64
+ colonnes: string[];
65
+ rows: SummaryRow[];
66
+ }
67
+
68
+ export interface FDrawerProps {
69
+ id?: string;
70
+ selected?: any[];
71
+ maxText?: number;
72
+ type?: string;
73
+ noFilter?: boolean;
74
+ noSearch?: boolean;
75
+ canExport?: boolean;
76
+ table_type?: string;
77
+ toggle?: string;
78
+ headers?: FDrawerHeader[];
79
+ totalHeaders?: FDrawerHeader[];
80
+ totalMainHeaders?: FDrawerHeader[];
81
+ btnsList?: BtnListItem[];
82
+ infinite_scroll?: boolean;
83
+ title?: string | ReactNode;
84
+ title_?: string;
85
+ orderByDefault?: string;
86
+ api?: string;
87
+ additional_params?: string;
88
+ btn_add?: boolean;
89
+ inWorkspace?: boolean;
90
+ wTotalCard?: boolean;
91
+ wMenu?: boolean;
92
+ noTitle?: boolean;
93
+ sideTitle?: ReactNode;
94
+ wReturn?: string;
95
+ noPagination?: boolean;
96
+ summary?: Summary;
97
+ children?: ReactNode;
98
+ arch?: {
99
+ trigger: string;
100
+ elements: string;
101
+ };
102
+ secondElements?: Array<{
103
+ title: string;
104
+ content: (data: any) => string | number;
105
+ custom?: boolean;
106
+ style?: React.CSSProperties;
107
+ }>;
108
+ triggerActions?: ActionItem[];
109
+ actions?: ActionItem[];
110
+ hasAttach?: boolean;
111
+ globalCode?: string;
112
+ reloadAttachment?: () => void;
113
+ hasImportExcel?: boolean;
114
+ api_import?: string;
115
+ hasExportExcel?: boolean;
116
+ hasComHis?: boolean;
117
+ lead?: any;
118
+ update_func?: () => void;
119
+ hasExport?: boolean;
120
+ save?: () => void;
121
+ view?: boolean;
122
+ view_actions?: ViewAction[];
123
+ height?: string;
124
+ }
125
+
126
+ export interface FooterSummaryProps {
127
+ colonnes: string[];
128
+ rows: SummaryRow[];
129
+ param: any[];
130
+ }
131
+
132
+ export interface DropdownActionsMenuProps {
133
+ actions?: ActionItem[];
134
+ row: any;
135
+ setLineSelected: (line: string) => void;
136
+ center: any;
137
+ }
138
+
139
+ export interface TableCollapseSimpleProps {
140
+ dataFilters: any[];
141
+ arch: {
142
+ trigger: string;
143
+ elements: string;
144
+ };
145
+ loading: boolean;
146
+ loading2?: boolean;
147
+ actions?: ActionItem[];
148
+ secondElements?: Array<{
149
+ title: string;
150
+ content: (data: any) => string | number;
151
+ custom?: boolean;
152
+ style?: React.CSSProperties;
153
+ }>;
154
+ triggerActions?: ActionItem[];
155
+ maxText: number;
156
+ data: any[];
157
+ headers: FDrawerHeader[];
158
+ totalHeaders?: FDrawerHeader[];
159
+ totalMainHeaders?: FDrawerHeader[];
160
+ filters: Record<string, any>;
161
+ setFilters: React.Dispatch<React.SetStateAction<Record<string, any>>>;
162
+ setComment: React.Dispatch<React.SetStateAction<any>>;
163
+ setSecondInfoComment: React.Dispatch<React.SetStateAction<string>>;
164
+ setShowModalComment: React.Dispatch<React.SetStateAction<boolean>>;
165
+ showContent: Record<string, boolean>;
166
+ setShowContent: React.Dispatch<React.SetStateAction<Record<string, boolean>>>;
167
+ lineSelected: string;
168
+ setLineSelected: React.Dispatch<React.SetStateAction<string>>;
169
+ isSorting: string | undefined;
170
+ setIsSorting: React.Dispatch<React.SetStateAction<string | undefined>>;
171
+ }
172
+
173
+ export interface FTableProps extends Omit<FDrawerProps, 'type'> {
174
+ setSelected?: (items: any[]) => void;
175
+ }
176
+
177
+ export interface DynamicTableProps {
178
+ side_of_icon?: 'gauche' | 'droite';
179
+ arr?: Array<{
180
+ label: string;
181
+ name: string;
182
+ type: string;
183
+ list?: Array<{
184
+ id: string | number;
185
+ value: string;
186
+ label: string;
187
+ }>;
188
+ }>;
189
+ row?: any[];
190
+ new_row?: any;
191
+ }
@@ -0,0 +1,409 @@
1
+ import React, { useRef, useState, useEffect, ChangeEvent, KeyboardEvent } from 'react';
2
+ import { Link } from 'react-router-dom';
3
+
4
+
5
+
6
+
7
+
8
+ // Types
9
+ export interface InputFieldProps {
10
+ type?: string;
11
+ inline?: boolean;
12
+ label?: string;
13
+ title?: string;
14
+ formError?: string;
15
+ name: string;
16
+ value: string | number;
17
+ onChange: (e: ChangeEvent<HTMLInputElement>) => void;
18
+ validationRules?: Record<string, any>;
19
+ onBlur?: (value: string | number) => void;
20
+ readOnly?: boolean;
21
+ disabled?: boolean;
22
+ required?: boolean;
23
+ placeholder?: string;
24
+ min?: number;
25
+ max?: number;
26
+ minLength?: number;
27
+ maxLength?: number;
28
+ className?: string;
29
+ }
30
+
31
+
32
+
33
+ type InputProps = {
34
+ label?: string;
35
+ name: string;
36
+ type?: string;
37
+ value: string | number;
38
+ placeholder?: string;
39
+ required?: boolean;
40
+ disabled?: boolean;
41
+ error?: string;
42
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
43
+ onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
44
+ };
45
+
46
+ export const InputField: React.FC<InputProps> = ({
47
+ label,
48
+ name,
49
+ type = "text",
50
+ value,
51
+ placeholder,
52
+ required = false,
53
+ disabled = false,
54
+ error,
55
+ onChange,
56
+ onBlur,
57
+ }) => {
58
+ return (
59
+ <div className="flex flex-col gap-1 w-full">
60
+ {label && (
61
+ <label htmlFor={name} className="block text-gray-700 text-sm font-medium mb-2">
62
+
63
+ {label} {required && <span className="text-red-500">*</span>}
64
+ </label>
65
+ )}
66
+
67
+ <input
68
+ id={name}
69
+ name={name}
70
+ type={type}
71
+ value={value}
72
+ placeholder={placeholder}
73
+ required={required}
74
+ disabled={disabled}
75
+ onChange={onChange}
76
+ onBlur={onBlur}
77
+ className={`w-full px-3 py-2 border border-[#D9D9D9] focus:ring-2 focus:ring-[#6A8A82]/20
78
+ ${error ? "border-red-500" : "border-gray-300"}
79
+ ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
80
+ `}
81
+ />
82
+
83
+ {error && <p className="text-xs text-red-500">{error}</p>}
84
+ </div>
85
+ );
86
+ };
87
+
88
+
89
+
90
+ // On reprend InputProps, mais on force `type="text"`
91
+ type TextInputProps = Omit<InputProps, "type">;
92
+
93
+ export const TextInput: React.FC<TextInputProps> = (props) => {
94
+ return <InputField {...props} type="text" />;
95
+ };
96
+
97
+ export const NumberInput:React.FC<TextInputProps> = (props) => {
98
+ return <InputField type="number" {...props} />;
99
+ };
100
+
101
+ export const DateInput: React.FC<TextInputProps> = (props) => {
102
+ return <InputField type="date" {...props} />;
103
+ };
104
+
105
+
106
+
107
+
108
+ type Option = {
109
+ label: string;
110
+ value: string | number;
111
+ };
112
+
113
+ type SelectInputProps = {
114
+ label?: string;
115
+ name: string;
116
+ value: string | number;
117
+ options: Option[];
118
+ defaultValue?: string | number;
119
+ required?: boolean;
120
+ disabled?: boolean;
121
+ error?: string;
122
+ className?: string;
123
+ onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
124
+ onBlur?: (e: React.FocusEvent<HTMLSelectElement>) => void;
125
+ };
126
+
127
+ export const SelectInput: React.FC<SelectInputProps> = ({
128
+ label,
129
+ name,
130
+ value,
131
+ options,
132
+ defaultValue = "",
133
+ required = false,
134
+ disabled = false,
135
+ error,
136
+ className = "",
137
+ onChange,
138
+ onBlur,
139
+ }) => {
140
+ return (
141
+ <div className={`${className} flex flex-col gap-1 w-full`}>
142
+ {label && (
143
+ <label
144
+ htmlFor={name}
145
+ className="block text-gray-700 text-sm font-medium mb-2"
146
+ >
147
+ {label} {required && <span className="text-red-500">*</span>}
148
+ </label>
149
+ )}
150
+
151
+ <select
152
+ id={name}
153
+ name={name}
154
+ value={value ?? ""}
155
+ required={required}
156
+ disabled={disabled}
157
+ onChange={onChange}
158
+ onBlur={onBlur}
159
+ className={`w-full px-4 py-2 border rounded-lg text-sm
160
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
161
+ ${error ? "border-red-500" : "border-gray-300"}
162
+ ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
163
+ `}
164
+ >
165
+ {defaultValue !== undefined && (
166
+ <option value="">{typeof defaultValue === "string" ? defaultValue : "Sélectionnez une option"}</option>
167
+ )}
168
+ {options.map(({ label, value: optionValue }) => (
169
+ <option key={optionValue} value={optionValue}>
170
+ {label}
171
+ </option>
172
+ ))}
173
+ </select>
174
+
175
+ {error && <p className="text-xs text-red-500">{error}</p>}
176
+ </div>
177
+ );
178
+ };
179
+
180
+
181
+
182
+
183
+ type FileInputProps = {
184
+ label?: string;
185
+ name: string;
186
+ file?: string | File; // peut être un chemin string ou un objet File
187
+ required?: boolean;
188
+ disabled?: boolean;
189
+ readOnly?: boolean;
190
+ error?: string;
191
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
192
+ };
193
+
194
+ // ⚡ Adresse de base pour accéder aux fichiers
195
+ const addressIpformMedia = "http://localhost:8000/media/";
196
+
197
+ export const FileInput: React.FC<FileInputProps> = ({
198
+ label,
199
+ name,
200
+ file,
201
+ required = false,
202
+ disabled = false,
203
+ readOnly = false,
204
+ error,
205
+ onChange,
206
+ }) => {
207
+ return (
208
+ <div className="flex flex-col gap-1 w-full">
209
+ {label && (
210
+ <label
211
+ htmlFor={name}
212
+ className="block text-gray-700 text-sm font-medium mb-2"
213
+ >
214
+ {label} {required && <span className="text-red-500">*</span>}
215
+ {file && typeof file === "string" && (
216
+ <div>
217
+ <Link
218
+ to={addressIpformMedia + file}
219
+ target="_blank"
220
+ className="ml-2 text-blue-600 underline text-sm"
221
+ >
222
+ {file.split("/").pop()}
223
+ </Link>
224
+ </div>
225
+ )}
226
+ </label>
227
+ )}
228
+
229
+ <input
230
+ id={name}
231
+ type="file"
232
+ name={name}
233
+ onChange={onChange}
234
+ readOnly={readOnly}
235
+ disabled={disabled}
236
+ required={required}
237
+ className={`w-full px-4 py-2 border rounded-lg text-sm
238
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
239
+ ${error ? "border-red-500" : "border-gray-300"}
240
+ ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
241
+ `}
242
+ />
243
+
244
+ {error && <p className="text-xs text-red-500">{error}</p>}
245
+ </div>
246
+ );
247
+ };
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+ export const ImageInput = (props) => {
359
+ const default_blur = () => { }
360
+
361
+ const fileInputRef = useRef(null);
362
+ const [selectedFilesV1, setSelectedFilesV1] = props.file;
363
+ const [file_url, setFileUrl] = useState(props.file_url ?? null);
364
+
365
+
366
+ const handleFileChange = (e, setfoo) => {
367
+ const newFiles = e.target.files;
368
+ setSelectedFilesV1(newFiles[0])
369
+ };
370
+ return (
371
+ <div className={`visu-div radius-05 col-md flex-col flex-center m-1 ${selectedFilesV1 !== null ? "bg-gray-bold" : "bg-gray-light-very"}`} style={{ height: props.height ?? 'inherit', maxHeight: props.height ?? 'inherit' }}>
372
+ {selectedFilesV1 !== null ? (
373
+ <label className='col flex-col flex-center w-100' role='button' >
374
+ <div className="over-hidden visu-filled">
375
+ <img src={URL.createObjectURL(selectedFilesV1)} alt='photo' className='mb-1' onClick={() => fileInputRef.current.click()} />
376
+ <FaMinusCircle role='button' onClick={() => setSelectedFilesV1(null)} className='icon rem-1' />
377
+ </div>
378
+ </label>
379
+ ) : ((file_url !== null) ? (
380
+ <label className='col flex-col flex-center w-100' role='button' >
381
+ <div className="over-hidden visu-filled flex-center">
382
+ <img src={file_url} alt='photo' className='mb-1' onClick={() => fileInputRef.current.click()} />
383
+ <FaMinusCircle role='button' onClick={() => setFileUrl(null)} className='icon rem-1' />
384
+ </div>
385
+ </label>
386
+ ) : (
387
+ <>
388
+ <label className='col flex-col w-100' role='button' onClick={() => fileInputRef.current.click()}>
389
+ <div className="col text-15rem flex-col flex-center">
390
+ <MdOutlineAddAPhoto className='mb-3 icon rem-25' />
391
+ Drop image here
392
+ </div>
393
+ </label>
394
+
395
+ </>
396
+ ))}
397
+ <input
398
+ type="file"
399
+ name=""
400
+ className='d-none'
401
+ ref={fileInputRef}
402
+ onChange={(e) => handleFileChange(e, setSelectedFilesV1)}
403
+ />
404
+ </div>
405
+ )
406
+ }
407
+
408
+
409
+
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+
3
+ interface ModalProps {
4
+ title: string;
5
+ description?: string;
6
+ width?: string;
7
+ open: boolean;
8
+ onClose: () => void;
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ const Modal: React.FC<ModalProps> = ({ title, description, width, open, onClose, children }) => {
13
+ if (!open) return null;
14
+
15
+ return (
16
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
17
+ <div className={`bg-white rounded-lg py-4 px-6 mx-4 w-[${width ? width : '60%'}]`}>
18
+ <div className="flex justify-between items-start mb-6">
19
+ <div>
20
+ <h3 className="text-xl font-semibold text-tuatara flex items-center space-x-2">
21
+ <span>{title}</span>
22
+ </h3>
23
+ {description && (
24
+ <p className="text-sm text-gray-600 mt-1">{description}</p>
25
+ )}
26
+ </div>
27
+ <button
28
+ onClick={onClose}
29
+ className="text-gray-400 hover:text-gray-600 text-xl"
30
+ aria-label="Close modal"
31
+ >
32
+
33
+ </button>
34
+ </div>
35
+ {children}
36
+ </div>
37
+ </div>
38
+ );
39
+ };
40
+
41
+ export default Modal;
File without changes
File without changes
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { useToast } from '../../hooks/useToast';
3
+
4
+ const ToastDemo: React.FC = () => {
5
+ const { success, error, warning, info } = useToast();
6
+
7
+ const showSuccess = () => {
8
+ success('Opération réussie ! Tout s\'est bien passé.');
9
+ };
10
+
11
+ const showError = () => {
12
+ error('Une erreur est survenue ! Veuillez réessayer.');
13
+ };
14
+
15
+ const showWarning = () => {
16
+ warning('Attention ! Cette action nécessite votre confirmation.');
17
+ };
18
+
19
+ const showInfo = () => {
20
+ info('Information : Nouvelle mise à jour disponible.');
21
+ };
22
+
23
+ const showCustomToast = () => {
24
+ success('Toast personnalisé avec durée de 10 secondes', 10000);
25
+ };
26
+
27
+ return (
28
+ <div className="p-8 max-w-md mx-auto bg-white rounded-lg shadow-lg">
29
+ <h2 className="text-2xl font-bold text-gray-800 mb-6 text-center">
30
+ Démonstration Toast
31
+ </h2>
32
+
33
+ <div className="space-y-4">
34
+ <button
35
+ onClick={showSuccess}
36
+ className="w-full bg-green-500 text-white py-2 px-4 rounded-lg hover:bg-green-600 transition-colors"
37
+ >
38
+ Toast Succès
39
+ </button>
40
+
41
+ <button
42
+ onClick={showError}
43
+ className="w-full bg-red-500 text-white py-2 px-4 rounded-lg hover:bg-red-600 transition-colors"
44
+ >
45
+ Toast Erreur
46
+ </button>
47
+
48
+ <button
49
+ onClick={showWarning}
50
+ className="w-full bg-yellow-500 text-white py-2 px-4 rounded-lg hover:bg-yellow-600 transition-colors"
51
+ >
52
+ Toast Avertissement
53
+ </button>
54
+
55
+ <button
56
+ onClick={showInfo}
57
+ className="w-full bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition-colors"
58
+ >
59
+ Toast Information
60
+ </button>
61
+
62
+ <button
63
+ onClick={showCustomToast}
64
+ className="w-full bg-purple-500 text-white py-2 px-4 rounded-lg hover:bg-purple-600 transition-colors"
65
+ >
66
+ Toast Personnalisé (10s)
67
+ </button>
68
+ </div>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default ToastDemo;