createsonline 0.1.26__py3-none-any.whl

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 (152) hide show
  1. createsonline/__init__.py +46 -0
  2. createsonline/admin/__init__.py +7 -0
  3. createsonline/admin/content.py +526 -0
  4. createsonline/admin/crud.py +805 -0
  5. createsonline/admin/field_builder.py +559 -0
  6. createsonline/admin/integration.py +482 -0
  7. createsonline/admin/interface.py +2562 -0
  8. createsonline/admin/model_creator.py +513 -0
  9. createsonline/admin/model_manager.py +388 -0
  10. createsonline/admin/modern_dashboard.py +498 -0
  11. createsonline/admin/permissions.py +264 -0
  12. createsonline/admin/user_forms.py +594 -0
  13. createsonline/ai/__init__.py +202 -0
  14. createsonline/ai/fields.py +1226 -0
  15. createsonline/ai/orm.py +325 -0
  16. createsonline/ai/services.py +1244 -0
  17. createsonline/app.py +506 -0
  18. createsonline/auth/__init__.py +8 -0
  19. createsonline/auth/management.py +228 -0
  20. createsonline/auth/models.py +552 -0
  21. createsonline/cli/__init__.py +5 -0
  22. createsonline/cli/commands/__init__.py +122 -0
  23. createsonline/cli/commands/database.py +416 -0
  24. createsonline/cli/commands/info.py +173 -0
  25. createsonline/cli/commands/initdb.py +218 -0
  26. createsonline/cli/commands/project.py +545 -0
  27. createsonline/cli/commands/serve.py +173 -0
  28. createsonline/cli/commands/shell.py +93 -0
  29. createsonline/cli/commands/users.py +148 -0
  30. createsonline/cli/main.py +2041 -0
  31. createsonline/cli/manage.py +274 -0
  32. createsonline/config/__init__.py +9 -0
  33. createsonline/config/app.py +2577 -0
  34. createsonline/config/database.py +179 -0
  35. createsonline/config/docs.py +384 -0
  36. createsonline/config/errors.py +160 -0
  37. createsonline/config/orm.py +43 -0
  38. createsonline/config/request.py +93 -0
  39. createsonline/config/settings.py +176 -0
  40. createsonline/data/__init__.py +23 -0
  41. createsonline/data/dataframe.py +925 -0
  42. createsonline/data/io.py +453 -0
  43. createsonline/data/series.py +557 -0
  44. createsonline/database/__init__.py +60 -0
  45. createsonline/database/abstraction.py +440 -0
  46. createsonline/database/assistant.py +585 -0
  47. createsonline/database/fields.py +442 -0
  48. createsonline/database/migrations.py +132 -0
  49. createsonline/database/models.py +604 -0
  50. createsonline/database.py +438 -0
  51. createsonline/http/__init__.py +28 -0
  52. createsonline/http/client.py +535 -0
  53. createsonline/ml/__init__.py +55 -0
  54. createsonline/ml/classification.py +552 -0
  55. createsonline/ml/clustering.py +680 -0
  56. createsonline/ml/metrics.py +542 -0
  57. createsonline/ml/neural.py +560 -0
  58. createsonline/ml/preprocessing.py +784 -0
  59. createsonline/ml/regression.py +501 -0
  60. createsonline/performance/__init__.py +19 -0
  61. createsonline/performance/cache.py +444 -0
  62. createsonline/performance/compression.py +335 -0
  63. createsonline/performance/core.py +419 -0
  64. createsonline/project_init.py +789 -0
  65. createsonline/routing.py +528 -0
  66. createsonline/security/__init__.py +34 -0
  67. createsonline/security/core.py +811 -0
  68. createsonline/security/encryption.py +349 -0
  69. createsonline/server.py +295 -0
  70. createsonline/static/css/admin.css +263 -0
  71. createsonline/static/css/common.css +358 -0
  72. createsonline/static/css/dashboard.css +89 -0
  73. createsonline/static/favicon.ico +0 -0
  74. createsonline/static/icons/icon-128x128.png +0 -0
  75. createsonline/static/icons/icon-128x128.webp +0 -0
  76. createsonline/static/icons/icon-16x16.png +0 -0
  77. createsonline/static/icons/icon-16x16.webp +0 -0
  78. createsonline/static/icons/icon-180x180.png +0 -0
  79. createsonline/static/icons/icon-180x180.webp +0 -0
  80. createsonline/static/icons/icon-192x192.png +0 -0
  81. createsonline/static/icons/icon-192x192.webp +0 -0
  82. createsonline/static/icons/icon-256x256.png +0 -0
  83. createsonline/static/icons/icon-256x256.webp +0 -0
  84. createsonline/static/icons/icon-32x32.png +0 -0
  85. createsonline/static/icons/icon-32x32.webp +0 -0
  86. createsonline/static/icons/icon-384x384.png +0 -0
  87. createsonline/static/icons/icon-384x384.webp +0 -0
  88. createsonline/static/icons/icon-48x48.png +0 -0
  89. createsonline/static/icons/icon-48x48.webp +0 -0
  90. createsonline/static/icons/icon-512x512.png +0 -0
  91. createsonline/static/icons/icon-512x512.webp +0 -0
  92. createsonline/static/icons/icon-64x64.png +0 -0
  93. createsonline/static/icons/icon-64x64.webp +0 -0
  94. createsonline/static/image/android-chrome-192x192.png +0 -0
  95. createsonline/static/image/android-chrome-512x512.png +0 -0
  96. createsonline/static/image/apple-touch-icon.png +0 -0
  97. createsonline/static/image/favicon-16x16.png +0 -0
  98. createsonline/static/image/favicon-32x32.png +0 -0
  99. createsonline/static/image/favicon.ico +0 -0
  100. createsonline/static/image/favicon.svg +17 -0
  101. createsonline/static/image/icon-128x128.png +0 -0
  102. createsonline/static/image/icon-128x128.webp +0 -0
  103. createsonline/static/image/icon-16x16.png +0 -0
  104. createsonline/static/image/icon-16x16.webp +0 -0
  105. createsonline/static/image/icon-180x180.png +0 -0
  106. createsonline/static/image/icon-180x180.webp +0 -0
  107. createsonline/static/image/icon-192x192.png +0 -0
  108. createsonline/static/image/icon-192x192.webp +0 -0
  109. createsonline/static/image/icon-256x256.png +0 -0
  110. createsonline/static/image/icon-256x256.webp +0 -0
  111. createsonline/static/image/icon-32x32.png +0 -0
  112. createsonline/static/image/icon-32x32.webp +0 -0
  113. createsonline/static/image/icon-384x384.png +0 -0
  114. createsonline/static/image/icon-384x384.webp +0 -0
  115. createsonline/static/image/icon-48x48.png +0 -0
  116. createsonline/static/image/icon-48x48.webp +0 -0
  117. createsonline/static/image/icon-512x512.png +0 -0
  118. createsonline/static/image/icon-512x512.webp +0 -0
  119. createsonline/static/image/icon-64x64.png +0 -0
  120. createsonline/static/image/icon-64x64.webp +0 -0
  121. createsonline/static/image/logo-header-h100.png +0 -0
  122. createsonline/static/image/logo-header-h100.webp +0 -0
  123. createsonline/static/image/logo-header-h200@2x.png +0 -0
  124. createsonline/static/image/logo-header-h200@2x.webp +0 -0
  125. createsonline/static/image/logo.png +0 -0
  126. createsonline/static/js/admin.js +274 -0
  127. createsonline/static/site.webmanifest +35 -0
  128. createsonline/static/templates/admin/base.html +87 -0
  129. createsonline/static/templates/admin/dashboard.html +217 -0
  130. createsonline/static/templates/admin/model_form.html +270 -0
  131. createsonline/static/templates/admin/model_list.html +202 -0
  132. createsonline/static/test_script.js +15 -0
  133. createsonline/static/test_styles.css +59 -0
  134. createsonline/static_files.py +365 -0
  135. createsonline/templates/404.html +100 -0
  136. createsonline/templates/admin_login.html +169 -0
  137. createsonline/templates/base.html +102 -0
  138. createsonline/templates/index.html +151 -0
  139. createsonline/templates.py +205 -0
  140. createsonline/testing.py +322 -0
  141. createsonline/utils.py +448 -0
  142. createsonline/validation/__init__.py +49 -0
  143. createsonline/validation/fields.py +598 -0
  144. createsonline/validation/models.py +504 -0
  145. createsonline/validation/validators.py +561 -0
  146. createsonline/views.py +184 -0
  147. createsonline-0.1.26.dist-info/METADATA +46 -0
  148. createsonline-0.1.26.dist-info/RECORD +152 -0
  149. createsonline-0.1.26.dist-info/WHEEL +5 -0
  150. createsonline-0.1.26.dist-info/entry_points.txt +2 -0
  151. createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
  152. createsonline-0.1.26.dist-info/top_level.txt +1 -0
@@ -0,0 +1,217 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Dashboard - CREATESONLINE Admin{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="dashboard-header">
7
+ <h1>Admin Dashboard</h1>
8
+ <p class="subtitle">Welcome to CREATESONLINE Admin Panel v0.1.6</p>
9
+ </div>
10
+
11
+ <div class="stats-grid">
12
+ <div class="stat-card">
13
+ <div class="stat-icon">📊</div>
14
+ <div class="stat-content">
15
+ <h3>Total Models</h3>
16
+ <p class="stat-value">{{ total_models|default:0 }}</p>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="stat-card">
21
+ <div class="stat-icon">📝</div>
22
+ <div class="stat-content">
23
+ <h3>Total Records</h3>
24
+ <p class="stat-value">{{ total_records|default:0 }}</p>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="stat-card">
29
+ <div class="stat-icon">👥</div>
30
+ <div class="stat-content">
31
+ <h3>Active Users</h3>
32
+ <p class="stat-value">{{ active_users|default:0 }}</p>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="stat-card">
37
+ <div class="stat-icon">🤖</div>
38
+ <div class="stat-content">
39
+ <h3>AI Status</h3>
40
+ <p class="stat-value">{{ ai_status|default:"Active" }}</p>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="dashboard-grid">
46
+ <div class="card">
47
+ <div class="card-header">
48
+ <h2>Recent Activity</h2>
49
+ </div>
50
+ <div class="card-body">
51
+ <div class="activity-list">
52
+ {% if recent_activity %}
53
+ {% for activity in recent_activity %}
54
+ <div class="activity-item">
55
+ <span class="activity-time">{{ activity.time }}</span>
56
+ <span class="activity-desc">{{ activity.description }}</span>
57
+ </div>
58
+ {% endfor %}
59
+ {% else %}
60
+ <p class="empty-state">No recent activity</p>
61
+ {% endif %}
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <div class="card">
67
+ <div class="card-header">
68
+ <h2>Registered Models</h2>
69
+ </div>
70
+ <div class="card-body">
71
+ <div class="model-list">
72
+ {% if models %}
73
+ {% for model in models %}
74
+ <div class="model-item">
75
+ <a href="/admin/{{ model.name }}">
76
+ <strong>{{ model.verbose_name }}</strong>
77
+ <span class="badge badge-info">{{ model.count }} records</span>
78
+ </a>
79
+ </div>
80
+ {% endfor %}
81
+ {% else %}
82
+ <p class="empty-state">No models registered yet</p>
83
+ {% endif %}
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="card">
89
+ <div class="card-header">
90
+ <h2>System Information</h2>
91
+ </div>
92
+ <div class="card-body">
93
+ <table class="info-table">
94
+ <tr>
95
+ <td><strong>Framework</strong></td>
96
+ <td>CREATESONLINE</td>
97
+ </tr>
98
+ <tr>
99
+ <td><strong>Version</strong></td>
100
+ <td>{{ version|default:"0.1.6" }}</td>
101
+ </tr>
102
+ <tr>
103
+ <td><strong>Admin Version</strong></td>
104
+ <td>{{ admin_version|default:"0.1.6" }}</td>
105
+ </tr>
106
+ <tr>
107
+ <td><strong>AI Enabled</strong></td>
108
+ <td>
109
+ {% if ai_enabled %}
110
+ <span class="badge badge-success">Yes</span>
111
+ {% else %}
112
+ <span class="badge badge-warning">No</span>
113
+ {% endif %}
114
+ </td>
115
+ </tr>
116
+ </table>
117
+ </div>
118
+ </div>
119
+
120
+ <div class="card">
121
+ <div class="card-header">
122
+ <h2>Quick Actions</h2>
123
+ </div>
124
+ <div class="card-body">
125
+ <div class="quick-actions">
126
+ <button class="btn btn-primary" onclick="window.location.href='/admin/create'">
127
+ Add New Record
128
+ </button>
129
+ <button class="btn btn-secondary" onclick="window.location.href='/admin/settings'">
130
+ Settings
131
+ </button>
132
+ <button class="btn btn-info" onclick="window.location.href='/admin/docs'">
133
+ Documentation
134
+ </button>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ {% endblock %}
140
+
141
+ {% block extra_css %}
142
+ <style>
143
+ .dashboard-header {
144
+ margin-bottom: 2rem;
145
+ }
146
+
147
+ .dashboard-header .subtitle {
148
+ color: var(--text-secondary);
149
+ margin-top: 0.5rem;
150
+ }
151
+
152
+ .activity-list, .model-list {
153
+ display: flex;
154
+ flex-direction: column;
155
+ gap: 0.75rem;
156
+ }
157
+
158
+ .activity-item {
159
+ display: flex;
160
+ gap: 1rem;
161
+ padding: 0.5rem;
162
+ border-left: 3px solid var(--primary-color);
163
+ background: var(--background-light);
164
+ }
165
+
166
+ .activity-time {
167
+ color: var(--text-secondary);
168
+ font-size: 0.875rem;
169
+ white-space: nowrap;
170
+ }
171
+
172
+ .model-item a {
173
+ display: flex;
174
+ justify-content: space-between;
175
+ align-items: center;
176
+ padding: 0.75rem;
177
+ background: var(--background-light);
178
+ border-radius: 4px;
179
+ text-decoration: none;
180
+ color: inherit;
181
+ transition: all 0.2s;
182
+ }
183
+
184
+ .model-item a:hover {
185
+ background: var(--primary-color);
186
+ color: white;
187
+ }
188
+
189
+ .info-table {
190
+ width: 100%;
191
+ }
192
+
193
+ .info-table tr {
194
+ border-bottom: 1px solid var(--border-color);
195
+ }
196
+
197
+ .info-table td {
198
+ padding: 0.75rem 0;
199
+ }
200
+
201
+ .quick-actions {
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 0.75rem;
205
+ }
206
+
207
+ .quick-actions .btn {
208
+ width: 100%;
209
+ }
210
+
211
+ .empty-state {
212
+ color: var(--text-secondary);
213
+ text-align: center;
214
+ padding: 2rem;
215
+ }
216
+ </style>
217
+ {% endblock %}
@@ -0,0 +1,270 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ action|title }} {{ model_name }} - CREATESONLINE Admin{% endblock %}
4
+
5
+ {% block breadcrumbs %}
6
+ <div class="breadcrumbs">
7
+ <a href="/admin/">Home</a>
8
+ <span class="separator">/</span>
9
+ <a href="/admin/{{ model_name }}">{{ model_name }}</a>
10
+ <span class="separator">/</span>
11
+ <span>{{ action|title }}</span>
12
+ </div>
13
+ {% endblock %}
14
+
15
+ {% block content %}
16
+ <div class="form-header">
17
+ <h1>{{ action|title }} {{ model_verbose_name|default:model_name }}</h1>
18
+ </div>
19
+
20
+ <form id="adminForm" class="admin-form" method="POST" action="/admin/{{ model_name }}/{% if object_id %}{{ object_id }}/edit{% else %}create{% endif %}">
21
+ <div class="form-container">
22
+ {% for field in fields %}
23
+ <div class="form-group">
24
+ <label for="{{ field.name }}" class="form-label">
25
+ {{ field.label }}
26
+ {% if field.required %}
27
+ <span class="required">*</span>
28
+ {% endif %}
29
+ </label>
30
+
31
+ {% if field.type == 'text' %}
32
+ <input
33
+ type="text"
34
+ id="{{ field.name }}"
35
+ name="{{ field.name }}"
36
+ class="form-control"
37
+ value="{{ field.value|default:'' }}"
38
+ {% if field.required %}required{% endif %}
39
+ {% if field.readonly %}readonly{% endif %}
40
+ >
41
+
42
+ {% elif field.type == 'textarea' %}
43
+ <textarea
44
+ id="{{ field.name }}"
45
+ name="{{ field.name }}"
46
+ class="form-control"
47
+ rows="5"
48
+ {% if field.required %}required{% endif %}
49
+ {% if field.readonly %}readonly{% endif %}
50
+ >{{ field.value|default:'' }}</textarea>
51
+
52
+ {% elif field.type == 'number' %}
53
+ <input
54
+ type="number"
55
+ id="{{ field.name }}"
56
+ name="{{ field.name }}"
57
+ class="form-control"
58
+ value="{{ field.value|default:'' }}"
59
+ {% if field.required %}required{% endif %}
60
+ {% if field.readonly %}readonly{% endif %}
61
+ >
62
+
63
+ {% elif field.type == 'email' %}
64
+ <input
65
+ type="email"
66
+ id="{{ field.name }}"
67
+ name="{{ field.name }}"
68
+ class="form-control"
69
+ value="{{ field.value|default:'' }}"
70
+ {% if field.required %}required{% endif %}
71
+ {% if field.readonly %}readonly{% endif %}
72
+ >
73
+
74
+ {% elif field.type == 'password' %}
75
+ <input
76
+ type="password"
77
+ id="{{ field.name }}"
78
+ name="{{ field.name }}"
79
+ class="form-control"
80
+ {% if field.required %}required{% endif %}
81
+ >
82
+
83
+ {% elif field.type == 'date' %}
84
+ <input
85
+ type="date"
86
+ id="{{ field.name }}"
87
+ name="{{ field.name }}"
88
+ class="form-control"
89
+ value="{{ field.value|default:'' }}"
90
+ {% if field.required %}required{% endif %}
91
+ {% if field.readonly %}readonly{% endif %}
92
+ >
93
+
94
+ {% elif field.type == 'datetime' %}
95
+ <input
96
+ type="datetime-local"
97
+ id="{{ field.name }}"
98
+ name="{{ field.name }}"
99
+ class="form-control"
100
+ value="{{ field.value|default:'' }}"
101
+ {% if field.required %}required{% endif %}
102
+ {% if field.readonly %}readonly{% endif %}
103
+ >
104
+
105
+ {% elif field.type == 'checkbox' %}
106
+ <div class="checkbox-wrapper">
107
+ <input
108
+ type="checkbox"
109
+ id="{{ field.name }}"
110
+ name="{{ field.name }}"
111
+ {% if field.value %}checked{% endif %}
112
+ {% if field.readonly %}disabled{% endif %}
113
+ >
114
+ <label for="{{ field.name }}">{{ field.help_text|default:'' }}</label>
115
+ </div>
116
+
117
+ {% elif field.type == 'select' %}
118
+ <select
119
+ id="{{ field.name }}"
120
+ name="{{ field.name }}"
121
+ class="form-control"
122
+ {% if field.required %}required{% endif %}
123
+ {% if field.readonly %}disabled{% endif %}
124
+ >
125
+ <option value="">-- Select --</option>
126
+ {% for choice in field.choices %}
127
+ <option value="{{ choice.value }}" {% if choice.value == field.value %}selected{% endif %}>
128
+ {{ choice.label }}
129
+ </option>
130
+ {% endfor %}
131
+ </select>
132
+
133
+ {% elif field.type == 'file' %}
134
+ <input
135
+ type="file"
136
+ id="{{ field.name }}"
137
+ name="{{ field.name }}"
138
+ class="form-control"
139
+ {% if field.required %}required{% endif %}
140
+ >
141
+ {% if field.value %}
142
+ <small>Current file: <a href="{{ field.value }}" target="_blank">View</a></small>
143
+ {% endif %}
144
+
145
+ {% else %}
146
+ <input
147
+ type="text"
148
+ id="{{ field.name }}"
149
+ name="{{ field.name }}"
150
+ class="form-control"
151
+ value="{{ field.value|default:'' }}"
152
+ {% if field.required %}required{% endif %}
153
+ {% if field.readonly %}readonly{% endif %}
154
+ >
155
+ {% endif %}
156
+
157
+ {% if field.help_text and field.type != 'checkbox' %}
158
+ <small class="form-help">{{ field.help_text }}</small>
159
+ {% endif %}
160
+
161
+ {% if field.errors %}
162
+ <div class="form-error">{{ field.errors }}</div>
163
+ {% endif %}
164
+ </div>
165
+ {% endfor %}
166
+ </div>
167
+
168
+ <div class="form-actions">
169
+ <button type="submit" class="btn btn-primary">Save</button>
170
+ <button type="button" class="btn btn-secondary" onclick="autoSave()">Save & Continue</button>
171
+ <button type="button" class="btn btn-secondary" onclick="window.history.back()">Cancel</button>
172
+ {% if object_id %}
173
+ <button type="button" class="btn btn-danger" onclick="deleteObject()">Delete</button>
174
+ {% endif %}
175
+ </div>
176
+ </form>
177
+ {% endblock %}
178
+
179
+ {% block extra_js %}
180
+ <script>
181
+ const modelName = "{{ model_name }}";
182
+ const objectId = "{{ object_id }}";
183
+
184
+ document.getElementById('adminForm').addEventListener('submit', async function(e) {
185
+ e.preventDefault();
186
+
187
+ const formData = new FormData(this);
188
+ const data = Object.fromEntries(formData.entries());
189
+
190
+ try {
191
+ const url = objectId ?
192
+ `/admin/${modelName}/${objectId}` :
193
+ `/admin/${modelName}/create`;
194
+
195
+ const response = await fetch(url, {
196
+ method: objectId ? 'PUT' : 'POST',
197
+ headers: {
198
+ 'Content-Type': 'application/json'
199
+ },
200
+ body: JSON.stringify(data)
201
+ });
202
+
203
+ if (response.ok) {
204
+ window.location.href = `/admin/${modelName}`;
205
+ } else {
206
+ const error = await response.json();
207
+ alert('Error: ' + (error.message || 'Failed to save'));
208
+ }
209
+ } catch (error) {
210
+ console.error('Error:', error);
211
+ alert('An error occurred while saving');
212
+ }
213
+ });
214
+
215
+ async function autoSave() {
216
+ const formData = new FormData(document.getElementById('adminForm'));
217
+ const data = Object.fromEntries(formData.entries());
218
+
219
+ try {
220
+ const url = objectId ?
221
+ `/admin/${modelName}/${objectId}` :
222
+ `/admin/${modelName}/create`;
223
+
224
+ const response = await fetch(url, {
225
+ method: objectId ? 'PUT' : 'POST',
226
+ headers: {
227
+ 'Content-Type': 'application/json'
228
+ },
229
+ body: JSON.stringify(data)
230
+ });
231
+
232
+ if (response.ok) {
233
+ const result = await response.json();
234
+ if (!objectId && result.id) {
235
+ window.location.href = `/admin/${modelName}/${result.id}/edit`;
236
+ } else {
237
+ alert('Saved successfully');
238
+ }
239
+ } else {
240
+ alert('Failed to save');
241
+ }
242
+ } catch (error) {
243
+ console.error('Error:', error);
244
+ alert('An error occurred');
245
+ }
246
+ }
247
+
248
+ async function deleteObject() {
249
+ if (!confirm('Are you sure you want to delete this record?')) return;
250
+
251
+ try {
252
+ const response = await fetch(`/admin/${modelName}/${objectId}`, {
253
+ method: 'DELETE',
254
+ headers: {
255
+ 'Content-Type': 'application/json'
256
+ }
257
+ });
258
+
259
+ if (response.ok) {
260
+ window.location.href = `/admin/${modelName}`;
261
+ } else {
262
+ alert('Failed to delete');
263
+ }
264
+ } catch (error) {
265
+ console.error('Error:', error);
266
+ alert('An error occurred');
267
+ }
268
+ }
269
+ </script>
270
+ {% endblock %}
@@ -0,0 +1,202 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ model_name }} - CREATESONLINE Admin{% endblock %}
4
+
5
+ {% block breadcrumbs %}
6
+ <div class="breadcrumbs">
7
+ <a href="/admin/">Home</a>
8
+ <span class="separator">/</span>
9
+ <span>{{ model_name }}</span>
10
+ </div>
11
+ {% endblock %}
12
+
13
+ {% block content %}
14
+ <div class="model-header">
15
+ <h1>{{ model_verbose_name|default:model_name }}</h1>
16
+ <div class="model-actions">
17
+ <button class="btn btn-primary" onclick="window.location.href='/admin/{{ model_name }}/create'">
18
+ Add {{ model_verbose_name|default:model_name }}
19
+ </button>
20
+ <button class="btn btn-secondary" onclick="exportData()">
21
+ Export
22
+ </button>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="filters-section">
27
+ <div class="search-box">
28
+ <input type="text" id="searchInput" placeholder="Search..." class="form-control">
29
+ </div>
30
+ <div class="filter-actions">
31
+ <button class="btn btn-sm" onclick="applyFilters()">Apply Filters</button>
32
+ <button class="btn btn-sm btn-secondary" onclick="clearFilters()">Clear</button>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="table-container">
37
+ <table class="admin-table" id="dataTable">
38
+ <thead>
39
+ <tr>
40
+ <th><input type="checkbox" id="selectAll"></th>
41
+ {% for field in fields %}
42
+ <th data-sortable="{{ field.sortable }}">
43
+ {{ field.label }}
44
+ {% if field.sortable %}
45
+ <span class="sort-icon">⇅</span>
46
+ {% endif %}
47
+ </th>
48
+ {% endfor %}
49
+ <th>Actions</th>
50
+ </tr>
51
+ </thead>
52
+ <tbody>
53
+ {% if objects %}
54
+ {% for obj in objects %}
55
+ <tr data-id="{{ obj.id }}">
56
+ <td><input type="checkbox" class="row-select" value="{{ obj.id }}"></td>
57
+ {% for field in fields %}
58
+ <td>{{ obj[field.name]|default:"—" }}</td>
59
+ {% endfor %}
60
+ <td class="actions-cell">
61
+ <button class="btn btn-sm btn-info" onclick="viewRecord({{ obj.id }})">View</button>
62
+ <button class="btn btn-sm btn-primary" onclick="editRecord({{ obj.id }})">Edit</button>
63
+ <button class="btn btn-sm btn-danger" onclick="deleteRecord({{ obj.id }})">Delete</button>
64
+ </td>
65
+ </tr>
66
+ {% endfor %}
67
+ {% else %}
68
+ <tr>
69
+ <td colspan="{{ fields|length + 2 }}" class="empty-state">
70
+ No records found. <a href="/admin/{{ model_name }}/create">Create one now</a>
71
+ </td>
72
+ </tr>
73
+ {% endif %}
74
+ </tbody>
75
+ </table>
76
+ </div>
77
+
78
+ {% if pagination %}
79
+ <div class="pagination">
80
+ {% if pagination.has_previous %}
81
+ <button class="btn btn-sm" onclick="goToPage({{ pagination.previous_page }})">Previous</button>
82
+ {% endif %}
83
+
84
+ <span class="page-info">
85
+ Page {{ pagination.current_page }} of {{ pagination.total_pages }}
86
+ </span>
87
+
88
+ {% if pagination.has_next %}
89
+ <button class="btn btn-sm" onclick="goToPage({{ pagination.next_page }})">Next</button>
90
+ {% endif %}
91
+ </div>
92
+ {% endif %}
93
+
94
+ <div class="bulk-actions">
95
+ <select id="bulkAction" class="form-control">
96
+ <option value="">Bulk Actions</option>
97
+ <option value="delete">Delete Selected</option>
98
+ <option value="export">Export Selected</option>
99
+ </select>
100
+ <button class="btn btn-primary" onclick="applyBulkAction()">Apply</button>
101
+ </div>
102
+ {% endblock %}
103
+
104
+ {% block extra_js %}
105
+ <script>
106
+ const modelName = "{{ model_name }}";
107
+
108
+ function viewRecord(id) {
109
+ window.location.href = `/admin/${modelName}/${id}`;
110
+ }
111
+
112
+ function editRecord(id) {
113
+ window.location.href = `/admin/${modelName}/${id}/edit`;
114
+ }
115
+
116
+ async function deleteRecord(id) {
117
+ if (!confirm('Are you sure you want to delete this record?')) return;
118
+
119
+ try {
120
+ const response = await fetch(`/admin/${modelName}/${id}`, {
121
+ method: 'DELETE',
122
+ headers: {
123
+ 'Content-Type': 'application/json'
124
+ }
125
+ });
126
+
127
+ if (response.ok) {
128
+ window.location.reload();
129
+ } else {
130
+ alert('Failed to delete record');
131
+ }
132
+ } catch (error) {
133
+ console.error('Error:', error);
134
+ alert('An error occurred');
135
+ }
136
+ }
137
+
138
+ function exportData() {
139
+ window.location.href = `/admin/${modelName}/export`;
140
+ }
141
+
142
+ function applyFilters() {
143
+ const searchTerm = document.getElementById('searchInput').value;
144
+ const url = new URL(window.location);
145
+ url.searchParams.set('search', searchTerm);
146
+ window.location.href = url.toString();
147
+ }
148
+
149
+ function clearFilters() {
150
+ window.location.href = `/admin/${modelName}`;
151
+ }
152
+
153
+ function goToPage(page) {
154
+ const url = new URL(window.location);
155
+ url.searchParams.set('page', page);
156
+ window.location.href = url.toString();
157
+ }
158
+
159
+ function applyBulkAction() {
160
+ const action = document.getElementById('bulkAction').value;
161
+ if (!action) {
162
+ alert('Please select an action');
163
+ return;
164
+ }
165
+
166
+ const selected = Array.from(document.querySelectorAll('.row-select:checked'))
167
+ .map(cb => cb.value);
168
+
169
+ if (selected.length === 0) {
170
+ alert('Please select at least one record');
171
+ return;
172
+ }
173
+
174
+ if (action === 'delete') {
175
+ if (!confirm(`Are you sure you want to delete ${selected.length} record(s)?`)) return;
176
+
177
+ fetch(`/admin/${modelName}/bulk-delete`, {
178
+ method: 'POST',
179
+ headers: {
180
+ 'Content-Type': 'application/json'
181
+ },
182
+ body: JSON.stringify({ ids: selected })
183
+ })
184
+ .then(response => {
185
+ if (response.ok) {
186
+ window.location.reload();
187
+ } else {
188
+ alert('Failed to delete records');
189
+ }
190
+ });
191
+ } else if (action === 'export') {
192
+ window.location.href = `/admin/${modelName}/export?ids=${selected.join(',')}`;
193
+ }
194
+ }
195
+
196
+ // Select all checkbox
197
+ document.getElementById('selectAll').addEventListener('change', function() {
198
+ const checkboxes = document.querySelectorAll('.row-select');
199
+ checkboxes.forEach(cb => cb.checked = this.checked);
200
+ });
201
+ </script>
202
+ {% endblock %}