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.
- createsonline/__init__.py +46 -0
- createsonline/admin/__init__.py +7 -0
- createsonline/admin/content.py +526 -0
- createsonline/admin/crud.py +805 -0
- createsonline/admin/field_builder.py +559 -0
- createsonline/admin/integration.py +482 -0
- createsonline/admin/interface.py +2562 -0
- createsonline/admin/model_creator.py +513 -0
- createsonline/admin/model_manager.py +388 -0
- createsonline/admin/modern_dashboard.py +498 -0
- createsonline/admin/permissions.py +264 -0
- createsonline/admin/user_forms.py +594 -0
- createsonline/ai/__init__.py +202 -0
- createsonline/ai/fields.py +1226 -0
- createsonline/ai/orm.py +325 -0
- createsonline/ai/services.py +1244 -0
- createsonline/app.py +506 -0
- createsonline/auth/__init__.py +8 -0
- createsonline/auth/management.py +228 -0
- createsonline/auth/models.py +552 -0
- createsonline/cli/__init__.py +5 -0
- createsonline/cli/commands/__init__.py +122 -0
- createsonline/cli/commands/database.py +416 -0
- createsonline/cli/commands/info.py +173 -0
- createsonline/cli/commands/initdb.py +218 -0
- createsonline/cli/commands/project.py +545 -0
- createsonline/cli/commands/serve.py +173 -0
- createsonline/cli/commands/shell.py +93 -0
- createsonline/cli/commands/users.py +148 -0
- createsonline/cli/main.py +2041 -0
- createsonline/cli/manage.py +274 -0
- createsonline/config/__init__.py +9 -0
- createsonline/config/app.py +2577 -0
- createsonline/config/database.py +179 -0
- createsonline/config/docs.py +384 -0
- createsonline/config/errors.py +160 -0
- createsonline/config/orm.py +43 -0
- createsonline/config/request.py +93 -0
- createsonline/config/settings.py +176 -0
- createsonline/data/__init__.py +23 -0
- createsonline/data/dataframe.py +925 -0
- createsonline/data/io.py +453 -0
- createsonline/data/series.py +557 -0
- createsonline/database/__init__.py +60 -0
- createsonline/database/abstraction.py +440 -0
- createsonline/database/assistant.py +585 -0
- createsonline/database/fields.py +442 -0
- createsonline/database/migrations.py +132 -0
- createsonline/database/models.py +604 -0
- createsonline/database.py +438 -0
- createsonline/http/__init__.py +28 -0
- createsonline/http/client.py +535 -0
- createsonline/ml/__init__.py +55 -0
- createsonline/ml/classification.py +552 -0
- createsonline/ml/clustering.py +680 -0
- createsonline/ml/metrics.py +542 -0
- createsonline/ml/neural.py +560 -0
- createsonline/ml/preprocessing.py +784 -0
- createsonline/ml/regression.py +501 -0
- createsonline/performance/__init__.py +19 -0
- createsonline/performance/cache.py +444 -0
- createsonline/performance/compression.py +335 -0
- createsonline/performance/core.py +419 -0
- createsonline/project_init.py +789 -0
- createsonline/routing.py +528 -0
- createsonline/security/__init__.py +34 -0
- createsonline/security/core.py +811 -0
- createsonline/security/encryption.py +349 -0
- createsonline/server.py +295 -0
- createsonline/static/css/admin.css +263 -0
- createsonline/static/css/common.css +358 -0
- createsonline/static/css/dashboard.css +89 -0
- createsonline/static/favicon.ico +0 -0
- createsonline/static/icons/icon-128x128.png +0 -0
- createsonline/static/icons/icon-128x128.webp +0 -0
- createsonline/static/icons/icon-16x16.png +0 -0
- createsonline/static/icons/icon-16x16.webp +0 -0
- createsonline/static/icons/icon-180x180.png +0 -0
- createsonline/static/icons/icon-180x180.webp +0 -0
- createsonline/static/icons/icon-192x192.png +0 -0
- createsonline/static/icons/icon-192x192.webp +0 -0
- createsonline/static/icons/icon-256x256.png +0 -0
- createsonline/static/icons/icon-256x256.webp +0 -0
- createsonline/static/icons/icon-32x32.png +0 -0
- createsonline/static/icons/icon-32x32.webp +0 -0
- createsonline/static/icons/icon-384x384.png +0 -0
- createsonline/static/icons/icon-384x384.webp +0 -0
- createsonline/static/icons/icon-48x48.png +0 -0
- createsonline/static/icons/icon-48x48.webp +0 -0
- createsonline/static/icons/icon-512x512.png +0 -0
- createsonline/static/icons/icon-512x512.webp +0 -0
- createsonline/static/icons/icon-64x64.png +0 -0
- createsonline/static/icons/icon-64x64.webp +0 -0
- createsonline/static/image/android-chrome-192x192.png +0 -0
- createsonline/static/image/android-chrome-512x512.png +0 -0
- createsonline/static/image/apple-touch-icon.png +0 -0
- createsonline/static/image/favicon-16x16.png +0 -0
- createsonline/static/image/favicon-32x32.png +0 -0
- createsonline/static/image/favicon.ico +0 -0
- createsonline/static/image/favicon.svg +17 -0
- createsonline/static/image/icon-128x128.png +0 -0
- createsonline/static/image/icon-128x128.webp +0 -0
- createsonline/static/image/icon-16x16.png +0 -0
- createsonline/static/image/icon-16x16.webp +0 -0
- createsonline/static/image/icon-180x180.png +0 -0
- createsonline/static/image/icon-180x180.webp +0 -0
- createsonline/static/image/icon-192x192.png +0 -0
- createsonline/static/image/icon-192x192.webp +0 -0
- createsonline/static/image/icon-256x256.png +0 -0
- createsonline/static/image/icon-256x256.webp +0 -0
- createsonline/static/image/icon-32x32.png +0 -0
- createsonline/static/image/icon-32x32.webp +0 -0
- createsonline/static/image/icon-384x384.png +0 -0
- createsonline/static/image/icon-384x384.webp +0 -0
- createsonline/static/image/icon-48x48.png +0 -0
- createsonline/static/image/icon-48x48.webp +0 -0
- createsonline/static/image/icon-512x512.png +0 -0
- createsonline/static/image/icon-512x512.webp +0 -0
- createsonline/static/image/icon-64x64.png +0 -0
- createsonline/static/image/icon-64x64.webp +0 -0
- createsonline/static/image/logo-header-h100.png +0 -0
- createsonline/static/image/logo-header-h100.webp +0 -0
- createsonline/static/image/logo-header-h200@2x.png +0 -0
- createsonline/static/image/logo-header-h200@2x.webp +0 -0
- createsonline/static/image/logo.png +0 -0
- createsonline/static/js/admin.js +274 -0
- createsonline/static/site.webmanifest +35 -0
- createsonline/static/templates/admin/base.html +87 -0
- createsonline/static/templates/admin/dashboard.html +217 -0
- createsonline/static/templates/admin/model_form.html +270 -0
- createsonline/static/templates/admin/model_list.html +202 -0
- createsonline/static/test_script.js +15 -0
- createsonline/static/test_styles.css +59 -0
- createsonline/static_files.py +365 -0
- createsonline/templates/404.html +100 -0
- createsonline/templates/admin_login.html +169 -0
- createsonline/templates/base.html +102 -0
- createsonline/templates/index.html +151 -0
- createsonline/templates.py +205 -0
- createsonline/testing.py +322 -0
- createsonline/utils.py +448 -0
- createsonline/validation/__init__.py +49 -0
- createsonline/validation/fields.py +598 -0
- createsonline/validation/models.py +504 -0
- createsonline/validation/validators.py +561 -0
- createsonline/views.py +184 -0
- createsonline-0.1.26.dist-info/METADATA +46 -0
- createsonline-0.1.26.dist-info/RECORD +152 -0
- createsonline-0.1.26.dist-info/WHEEL +5 -0
- createsonline-0.1.26.dist-info/entry_points.txt +2 -0
- createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
- 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 %}
|