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,559 @@
|
|
|
1
|
+
# createsonline/admin/field_builder.py
|
|
2
|
+
"""
|
|
3
|
+
Field Builder - Smart field type selector for adding fields to models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
FIELD_TEMPLATES = {
|
|
7
|
+
# Text Fields
|
|
8
|
+
"short_text": {
|
|
9
|
+
"label": "Short Text",
|
|
10
|
+
"description": "For names, titles, usernames (max 255 chars)",
|
|
11
|
+
"sql_type": "String",
|
|
12
|
+
"default_length": 150,
|
|
13
|
+
"examples": "username, title, name, slug, code",
|
|
14
|
+
"icon": "📝"
|
|
15
|
+
},
|
|
16
|
+
"long_text": {
|
|
17
|
+
"label": "Long Text",
|
|
18
|
+
"description": "For descriptions, bios, comments (unlimited)",
|
|
19
|
+
"sql_type": "Text",
|
|
20
|
+
"default_length": None,
|
|
21
|
+
"examples": "bio, description, content, notes",
|
|
22
|
+
"icon": "📄"
|
|
23
|
+
},
|
|
24
|
+
"email": {
|
|
25
|
+
"label": "Email",
|
|
26
|
+
"description": "Email address with validation",
|
|
27
|
+
"sql_type": "String",
|
|
28
|
+
"default_length": 254,
|
|
29
|
+
"examples": "email, contact_email, support_email",
|
|
30
|
+
"icon": "📧"
|
|
31
|
+
},
|
|
32
|
+
"url": {
|
|
33
|
+
"label": "URL",
|
|
34
|
+
"description": "Website or link URL",
|
|
35
|
+
"sql_type": "String",
|
|
36
|
+
"default_length": 500,
|
|
37
|
+
"examples": "website, profile_url, avatar_url",
|
|
38
|
+
"icon": "🔗"
|
|
39
|
+
},
|
|
40
|
+
"phone": {
|
|
41
|
+
"label": "Phone Number",
|
|
42
|
+
"description": "Phone number with country code",
|
|
43
|
+
"sql_type": "String",
|
|
44
|
+
"default_length": 20,
|
|
45
|
+
"examples": "phone, mobile, contact_number",
|
|
46
|
+
"icon": "📱"
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
# Number Fields
|
|
50
|
+
"integer": {
|
|
51
|
+
"label": "Integer",
|
|
52
|
+
"description": "Whole numbers (IDs, counts, ages)",
|
|
53
|
+
"sql_type": "Integer",
|
|
54
|
+
"default_length": None,
|
|
55
|
+
"examples": "age, count, quantity, order_number",
|
|
56
|
+
"icon": "🔢"
|
|
57
|
+
},
|
|
58
|
+
"decimal": {
|
|
59
|
+
"label": "Decimal",
|
|
60
|
+
"description": "Numbers with decimals (prices, ratings)",
|
|
61
|
+
"sql_type": "Numeric",
|
|
62
|
+
"default_length": "(10, 2)",
|
|
63
|
+
"examples": "price, rating, salary, discount",
|
|
64
|
+
"icon": "💰"
|
|
65
|
+
},
|
|
66
|
+
"float": {
|
|
67
|
+
"label": "Float",
|
|
68
|
+
"description": "Floating point numbers",
|
|
69
|
+
"sql_type": "Float",
|
|
70
|
+
"default_length": None,
|
|
71
|
+
"examples": "percentage, score, weight",
|
|
72
|
+
"icon": "📊"
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
# Boolean & Choice Fields
|
|
76
|
+
"boolean": {
|
|
77
|
+
"label": "Boolean (Yes/No)",
|
|
78
|
+
"description": "True/False checkbox",
|
|
79
|
+
"sql_type": "Boolean",
|
|
80
|
+
"default_length": None,
|
|
81
|
+
"examples": "is_active, is_verified, is_featured",
|
|
82
|
+
"icon": "✓"
|
|
83
|
+
},
|
|
84
|
+
"choice": {
|
|
85
|
+
"label": "Choice (Enum)",
|
|
86
|
+
"description": "Dropdown selection from predefined options",
|
|
87
|
+
"sql_type": "Enum",
|
|
88
|
+
"default_length": None,
|
|
89
|
+
"examples": "status, role, priority, category",
|
|
90
|
+
"icon": "📋"
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
# Date & Time Fields
|
|
94
|
+
"date": {
|
|
95
|
+
"label": "Date",
|
|
96
|
+
"description": "Calendar date (YYYY-MM-DD)",
|
|
97
|
+
"sql_type": "Date",
|
|
98
|
+
"default_length": None,
|
|
99
|
+
"examples": "birth_date, start_date, deadline",
|
|
100
|
+
"icon": "📅"
|
|
101
|
+
},
|
|
102
|
+
"datetime": {
|
|
103
|
+
"label": "Date & Time",
|
|
104
|
+
"description": "Full date with time",
|
|
105
|
+
"sql_type": "DateTime",
|
|
106
|
+
"default_length": None,
|
|
107
|
+
"examples": "created_at, updated_at, published_at",
|
|
108
|
+
"icon": "🕐"
|
|
109
|
+
},
|
|
110
|
+
"time": {
|
|
111
|
+
"label": "Time",
|
|
112
|
+
"description": "Time of day (HH:MM:SS)",
|
|
113
|
+
"sql_type": "Time",
|
|
114
|
+
"default_length": None,
|
|
115
|
+
"examples": "opening_time, closing_time, meeting_time",
|
|
116
|
+
"icon": "⏰"
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
# File & Media Fields
|
|
120
|
+
"image": {
|
|
121
|
+
"label": "Image",
|
|
122
|
+
"description": "Image file path/URL",
|
|
123
|
+
"sql_type": "String",
|
|
124
|
+
"default_length": 500,
|
|
125
|
+
"examples": "avatar, profile_picture, thumbnail, logo",
|
|
126
|
+
"icon": "🖼️"
|
|
127
|
+
},
|
|
128
|
+
"file": {
|
|
129
|
+
"label": "File",
|
|
130
|
+
"description": "Document or file path/URL",
|
|
131
|
+
"sql_type": "String",
|
|
132
|
+
"default_length": 500,
|
|
133
|
+
"examples": "resume, attachment, document, certificate",
|
|
134
|
+
"icon": "📎"
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
# JSON & Special Fields
|
|
138
|
+
"json": {
|
|
139
|
+
"label": "JSON",
|
|
140
|
+
"description": "Structured data in JSON format",
|
|
141
|
+
"sql_type": "JSON",
|
|
142
|
+
"default_length": None,
|
|
143
|
+
"examples": "settings, metadata, preferences, config",
|
|
144
|
+
"icon": "🗃️"
|
|
145
|
+
},
|
|
146
|
+
"uuid": {
|
|
147
|
+
"label": "UUID",
|
|
148
|
+
"description": "Universally unique identifier",
|
|
149
|
+
"sql_type": "String",
|
|
150
|
+
"default_length": 36,
|
|
151
|
+
"examples": "external_id, api_key, token",
|
|
152
|
+
"icon": "🔑"
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
# Common Use Cases
|
|
156
|
+
"slug": {
|
|
157
|
+
"label": "Slug",
|
|
158
|
+
"description": "URL-friendly identifier (lowercase, hyphens)",
|
|
159
|
+
"sql_type": "String",
|
|
160
|
+
"default_length": 200,
|
|
161
|
+
"examples": "slug, url_slug, permalink",
|
|
162
|
+
"icon": "🔗"
|
|
163
|
+
},
|
|
164
|
+
"color": {
|
|
165
|
+
"label": "Color",
|
|
166
|
+
"description": "Hex color code (#RRGGBB)",
|
|
167
|
+
"sql_type": "String",
|
|
168
|
+
"default_length": 7,
|
|
169
|
+
"examples": "color, theme_color, background_color",
|
|
170
|
+
"icon": "🎨"
|
|
171
|
+
},
|
|
172
|
+
"ip_address": {
|
|
173
|
+
"label": "IP Address",
|
|
174
|
+
"description": "IPv4 or IPv6 address",
|
|
175
|
+
"sql_type": "String",
|
|
176
|
+
"default_length": 45,
|
|
177
|
+
"examples": "ip_address, last_login_ip, created_from_ip",
|
|
178
|
+
"icon": "🌐"
|
|
179
|
+
},
|
|
180
|
+
"password": {
|
|
181
|
+
"label": "Password Hash",
|
|
182
|
+
"description": "Hashed password (DO NOT store plain text!)",
|
|
183
|
+
"sql_type": "String",
|
|
184
|
+
"default_length": 128,
|
|
185
|
+
"examples": "password_hash, api_secret_hash",
|
|
186
|
+
"icon": "🔒"
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def render_add_field_form(model_name: str) -> str:
|
|
192
|
+
"""Render the smart add field form"""
|
|
193
|
+
|
|
194
|
+
# Group field types by category
|
|
195
|
+
categories = {
|
|
196
|
+
"Text": ["short_text", "long_text", "email", "url", "phone", "slug"],
|
|
197
|
+
"Numbers": ["integer", "decimal", "float"],
|
|
198
|
+
"Date & Time": ["date", "datetime", "time"],
|
|
199
|
+
"Boolean & Choice": ["boolean", "choice"],
|
|
200
|
+
"Files & Media": ["image", "file"],
|
|
201
|
+
"Advanced": ["json", "uuid", "color", "ip_address", "password"],
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Build field type selector HTML
|
|
205
|
+
field_types_html = ""
|
|
206
|
+
for category, field_keys in categories.items():
|
|
207
|
+
field_types_html += f'<optgroup label="{category}">'
|
|
208
|
+
for key in field_keys:
|
|
209
|
+
template = FIELD_TEMPLATES[key]
|
|
210
|
+
field_types_html += f'''
|
|
211
|
+
<option value="{key}"
|
|
212
|
+
data-sql-type="{template['sql_type']}"
|
|
213
|
+
data-length="{template['default_length'] or ''}"
|
|
214
|
+
data-examples="{template['examples']}"
|
|
215
|
+
data-description="{template['description']}">
|
|
216
|
+
{template['icon']} {template['label']}
|
|
217
|
+
</option>
|
|
218
|
+
'''
|
|
219
|
+
field_types_html += '</optgroup>'
|
|
220
|
+
|
|
221
|
+
html = f"""<!DOCTYPE html>
|
|
222
|
+
<html lang="en">
|
|
223
|
+
<head>
|
|
224
|
+
<meta charset="UTF-8">
|
|
225
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
226
|
+
<title>Add Field to {model_name} - CREATESONLINE Admin</title>
|
|
227
|
+
<style>
|
|
228
|
+
* {{
|
|
229
|
+
margin: 0;
|
|
230
|
+
padding: 0;
|
|
231
|
+
box-sizing: border-box;
|
|
232
|
+
}}
|
|
233
|
+
|
|
234
|
+
body {{
|
|
235
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
236
|
+
background: #ffffff;
|
|
237
|
+
color: #000000;
|
|
238
|
+
}}
|
|
239
|
+
|
|
240
|
+
.container {{
|
|
241
|
+
max-width: 1000px;
|
|
242
|
+
margin: 0 auto;
|
|
243
|
+
padding: 40px 20px;
|
|
244
|
+
}}
|
|
245
|
+
|
|
246
|
+
.header {{
|
|
247
|
+
margin-bottom: 40px;
|
|
248
|
+
}}
|
|
249
|
+
|
|
250
|
+
.logo {{
|
|
251
|
+
margin-bottom: 20px;
|
|
252
|
+
}}
|
|
253
|
+
|
|
254
|
+
.logo img {{
|
|
255
|
+
height: 40px;
|
|
256
|
+
}}
|
|
257
|
+
|
|
258
|
+
h1 {{
|
|
259
|
+
font-size: 32px;
|
|
260
|
+
font-weight: 700;
|
|
261
|
+
color: #000;
|
|
262
|
+
margin-bottom: 10px;
|
|
263
|
+
}}
|
|
264
|
+
|
|
265
|
+
.breadcrumb {{
|
|
266
|
+
color: #666;
|
|
267
|
+
font-size: 14px;
|
|
268
|
+
}}
|
|
269
|
+
|
|
270
|
+
.breadcrumb a {{
|
|
271
|
+
color: #000;
|
|
272
|
+
text-decoration: none;
|
|
273
|
+
}}
|
|
274
|
+
|
|
275
|
+
.breadcrumb a:hover {{
|
|
276
|
+
text-decoration: underline;
|
|
277
|
+
}}
|
|
278
|
+
|
|
279
|
+
.form-card {{
|
|
280
|
+
background: #fff;
|
|
281
|
+
border: 2px solid #e0e0e0;
|
|
282
|
+
border-radius: 12px;
|
|
283
|
+
padding: 40px;
|
|
284
|
+
margin-bottom: 30px;
|
|
285
|
+
}}
|
|
286
|
+
|
|
287
|
+
.form-group {{
|
|
288
|
+
margin-bottom: 25px;
|
|
289
|
+
}}
|
|
290
|
+
|
|
291
|
+
label {{
|
|
292
|
+
display: block;
|
|
293
|
+
font-weight: 600;
|
|
294
|
+
margin-bottom: 8px;
|
|
295
|
+
color: #000;
|
|
296
|
+
}}
|
|
297
|
+
|
|
298
|
+
.required {{
|
|
299
|
+
color: #ff0000;
|
|
300
|
+
}}
|
|
301
|
+
|
|
302
|
+
input[type="text"],
|
|
303
|
+
select,
|
|
304
|
+
textarea {{
|
|
305
|
+
width: 100%;
|
|
306
|
+
padding: 12px;
|
|
307
|
+
border: 2px solid #e0e0e0;
|
|
308
|
+
border-radius: 8px;
|
|
309
|
+
font-size: 15px;
|
|
310
|
+
font-family: inherit;
|
|
311
|
+
transition: all 0.2s;
|
|
312
|
+
}}
|
|
313
|
+
|
|
314
|
+
input:focus,
|
|
315
|
+
select:focus,
|
|
316
|
+
textarea:focus {{
|
|
317
|
+
outline: none;
|
|
318
|
+
border-color: #000;
|
|
319
|
+
}}
|
|
320
|
+
|
|
321
|
+
.help-text {{
|
|
322
|
+
font-size: 13px;
|
|
323
|
+
color: #666;
|
|
324
|
+
margin-top: 6px;
|
|
325
|
+
}}
|
|
326
|
+
|
|
327
|
+
.field-preview {{
|
|
328
|
+
background: #f5f5f5;
|
|
329
|
+
padding: 20px;
|
|
330
|
+
border-radius: 8px;
|
|
331
|
+
margin-top: 10px;
|
|
332
|
+
display: none;
|
|
333
|
+
}}
|
|
334
|
+
|
|
335
|
+
.field-preview.active {{
|
|
336
|
+
display: block;
|
|
337
|
+
}}
|
|
338
|
+
|
|
339
|
+
.preview-label {{
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
font-weight: 600;
|
|
342
|
+
color: #666;
|
|
343
|
+
text-transform: uppercase;
|
|
344
|
+
margin-bottom: 8px;
|
|
345
|
+
}}
|
|
346
|
+
|
|
347
|
+
.preview-content {{
|
|
348
|
+
font-size: 14px;
|
|
349
|
+
color: #000;
|
|
350
|
+
}}
|
|
351
|
+
|
|
352
|
+
.checkbox-group {{
|
|
353
|
+
display: flex;
|
|
354
|
+
gap: 20px;
|
|
355
|
+
flex-wrap: wrap;
|
|
356
|
+
}}
|
|
357
|
+
|
|
358
|
+
.checkbox-item {{
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
gap: 8px;
|
|
362
|
+
}}
|
|
363
|
+
|
|
364
|
+
input[type="checkbox"] {{
|
|
365
|
+
width: 20px;
|
|
366
|
+
height: 20px;
|
|
367
|
+
cursor: pointer;
|
|
368
|
+
}}
|
|
369
|
+
|
|
370
|
+
.btn {{
|
|
371
|
+
padding: 14px 28px;
|
|
372
|
+
border: none;
|
|
373
|
+
border-radius: 8px;
|
|
374
|
+
font-size: 15px;
|
|
375
|
+
font-weight: 600;
|
|
376
|
+
cursor: pointer;
|
|
377
|
+
text-decoration: none;
|
|
378
|
+
display: inline-block;
|
|
379
|
+
transition: all 0.2s;
|
|
380
|
+
}}
|
|
381
|
+
|
|
382
|
+
.btn-primary {{
|
|
383
|
+
background: #000;
|
|
384
|
+
color: #fff;
|
|
385
|
+
}}
|
|
386
|
+
|
|
387
|
+
.btn-primary:hover {{
|
|
388
|
+
background: #333;
|
|
389
|
+
}}
|
|
390
|
+
|
|
391
|
+
.btn-secondary {{
|
|
392
|
+
background: #f5f5f5;
|
|
393
|
+
color: #000;
|
|
394
|
+
border: 2px solid #e0e0e0;
|
|
395
|
+
}}
|
|
396
|
+
|
|
397
|
+
.btn-secondary:hover {{
|
|
398
|
+
background: #e0e0e0;
|
|
399
|
+
}}
|
|
400
|
+
|
|
401
|
+
.actions {{
|
|
402
|
+
display: flex;
|
|
403
|
+
gap: 15px;
|
|
404
|
+
margin-top: 30px;
|
|
405
|
+
}}
|
|
406
|
+
|
|
407
|
+
.examples-grid {{
|
|
408
|
+
display: grid;
|
|
409
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
410
|
+
gap: 10px;
|
|
411
|
+
margin-top: 15px;
|
|
412
|
+
}}
|
|
413
|
+
|
|
414
|
+
.example-chip {{
|
|
415
|
+
padding: 8px 12px;
|
|
416
|
+
background: #f0f0f0;
|
|
417
|
+
border-radius: 6px;
|
|
418
|
+
font-size: 13px;
|
|
419
|
+
text-align: center;
|
|
420
|
+
color: #333;
|
|
421
|
+
}}
|
|
422
|
+
</style>
|
|
423
|
+
</head>
|
|
424
|
+
<body>
|
|
425
|
+
<div class="container">
|
|
426
|
+
<div class="header">
|
|
427
|
+
<div class="logo">
|
|
428
|
+
<img src="/logo.png" alt="Logo" onerror="this.style.display='none'">
|
|
429
|
+
</div>
|
|
430
|
+
<h1>Add Field to {model_name}</h1>
|
|
431
|
+
<div class="breadcrumb">
|
|
432
|
+
<a href="/admin">Admin</a> /
|
|
433
|
+
<a href="/admin/model-manager/{model_name.lower()}">Models</a> /
|
|
434
|
+
<a href="/admin/model-manager/{model_name.lower()}">{model_name}</a> /
|
|
435
|
+
Add Field
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<form method="POST" id="addFieldForm">
|
|
440
|
+
<div class="form-card">
|
|
441
|
+
<div class="form-group">
|
|
442
|
+
<label for="field_type">Field Type <span class="required">*</span></label>
|
|
443
|
+
<select id="field_type" name="field_type" required>
|
|
444
|
+
<option value="">Select field type...</option>
|
|
445
|
+
{field_types_html}
|
|
446
|
+
</select>
|
|
447
|
+
<div class="help-text">Choose the type of data this field will store</div>
|
|
448
|
+
<div class="field-preview" id="fieldPreview">
|
|
449
|
+
<div class="preview-label">Description</div>
|
|
450
|
+
<div class="preview-content" id="previewDescription"></div>
|
|
451
|
+
<div class="preview-label" style="margin-top: 15px;">Common Examples</div>
|
|
452
|
+
<div class="examples-grid" id="previewExamples"></div>
|
|
453
|
+
</div>
|
|
454
|
+
</div>
|
|
455
|
+
|
|
456
|
+
<div class="form-group">
|
|
457
|
+
<label for="field_name">Field Name <span class="required">*</span></label>
|
|
458
|
+
<input type="text" id="field_name" name="field_name" placeholder="e.g., role, phone_number, bio" required>
|
|
459
|
+
<div class="help-text">Lowercase letters, numbers, and underscores only (e.g., user_role, phone_number)</div>
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<div class="form-group" id="lengthGroup">
|
|
463
|
+
<label for="field_length">Max Length</label>
|
|
464
|
+
<input type="number" id="field_length" name="field_length" placeholder="150">
|
|
465
|
+
<div class="help-text">Maximum character length (leave empty for unlimited)</div>
|
|
466
|
+
</div>
|
|
467
|
+
|
|
468
|
+
<div class="form-group">
|
|
469
|
+
<label for="default_value">Default Value</label>
|
|
470
|
+
<input type="text" id="default_value" name="default_value" placeholder="Optional default value">
|
|
471
|
+
<div class="help-text">Value to use when creating new records (optional)</div>
|
|
472
|
+
</div>
|
|
473
|
+
|
|
474
|
+
<div class="form-group">
|
|
475
|
+
<label>Constraints</label>
|
|
476
|
+
<div class="checkbox-group">
|
|
477
|
+
<div class="checkbox-item">
|
|
478
|
+
<input type="checkbox" id="required" name="required" value="1">
|
|
479
|
+
<label for="required" style="margin: 0;">Required (NOT NULL)</label>
|
|
480
|
+
</div>
|
|
481
|
+
<div class="checkbox-item">
|
|
482
|
+
<input type="checkbox" id="unique" name="unique" value="1">
|
|
483
|
+
<label for="unique" style="margin: 0;">Unique</label>
|
|
484
|
+
</div>
|
|
485
|
+
<div class="checkbox-item">
|
|
486
|
+
<input type="checkbox" id="index" name="index" value="1" checked>
|
|
487
|
+
<label for="index" style="margin: 0;">Indexed (faster searches)</label>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
|
|
492
|
+
<div class="form-group">
|
|
493
|
+
<label for="help_text">Help Text</label>
|
|
494
|
+
<textarea id="help_text" name="help_text" rows="2" placeholder="Helpful description for users filling out forms"></textarea>
|
|
495
|
+
<div class="help-text">This will be shown in forms to help users understand what to enter</div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
<div class="actions">
|
|
500
|
+
<button type="submit" class="btn btn-primary">✓ Add Field</button>
|
|
501
|
+
<a href="/admin/model-manager/{model_name.lower()}" class="btn btn-secondary">Cancel</a>
|
|
502
|
+
</div>
|
|
503
|
+
</form>
|
|
504
|
+
</div>
|
|
505
|
+
|
|
506
|
+
<script>
|
|
507
|
+
// Field type selector preview
|
|
508
|
+
const fieldTypeSelect = document.getElementById('field_type');
|
|
509
|
+
const fieldPreview = document.getElementById('fieldPreview');
|
|
510
|
+
const previewDescription = document.getElementById('previewDescription');
|
|
511
|
+
const previewExamples = document.getElementById('previewExamples');
|
|
512
|
+
const lengthGroup = document.getElementById('lengthGroup');
|
|
513
|
+
const fieldLengthInput = document.getElementById('field_length');
|
|
514
|
+
|
|
515
|
+
fieldTypeSelect.addEventListener('change', function() {{
|
|
516
|
+
const selectedOption = this.options[this.selectedIndex];
|
|
517
|
+
|
|
518
|
+
if (selectedOption.value) {{
|
|
519
|
+
const description = selectedOption.getAttribute('data-description');
|
|
520
|
+
const examples = selectedOption.getAttribute('data-examples').split(', ');
|
|
521
|
+
const length = selectedOption.getAttribute('data-length');
|
|
522
|
+
|
|
523
|
+
// Show preview
|
|
524
|
+
fieldPreview.classList.add('active');
|
|
525
|
+
previewDescription.textContent = description;
|
|
526
|
+
|
|
527
|
+
// Show examples
|
|
528
|
+
previewExamples.innerHTML = examples.map(ex =>
|
|
529
|
+
`<div class="example-chip">${{ex}}</div>`
|
|
530
|
+
).join('');
|
|
531
|
+
|
|
532
|
+
// Update length field
|
|
533
|
+
if (length) {{
|
|
534
|
+
lengthGroup.style.display = 'block';
|
|
535
|
+
fieldLengthInput.value = length;
|
|
536
|
+
}} else {{
|
|
537
|
+
lengthGroup.style.display = 'none';
|
|
538
|
+
fieldLengthInput.value = '';
|
|
539
|
+
}}
|
|
540
|
+
}} else {{
|
|
541
|
+
fieldPreview.classList.remove('active');
|
|
542
|
+
lengthGroup.style.display = 'block';
|
|
543
|
+
}}
|
|
544
|
+
}});
|
|
545
|
+
|
|
546
|
+
// Auto-format field name
|
|
547
|
+
const fieldNameInput = document.getElementById('field_name');
|
|
548
|
+
fieldNameInput.addEventListener('input', function() {{
|
|
549
|
+
this.value = this.value
|
|
550
|
+
.toLowerCase()
|
|
551
|
+
.replace(/[^a-z0-9_]/g, '_')
|
|
552
|
+
.replace(/_+/g, '_')
|
|
553
|
+
.replace(/^_|_$/g, '');
|
|
554
|
+
}});
|
|
555
|
+
</script>
|
|
556
|
+
</body>
|
|
557
|
+
</html>
|
|
558
|
+
"""
|
|
559
|
+
return html
|