From 42ed1b04cb1361bd553bb1e1bb2b87f495f3c0ca Mon Sep 17 00:00:00 2001 From: Victor Alexandrovich Tsyrenschikov <77172321+tsyrenschikov@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:54:59 +0500 Subject: [PATCH] Django --- apps/charts/__init__.py | 0 apps/charts/admin.py | 3 + apps/charts/apps.py | 6 + apps/charts/migrations/__init__.py | 0 apps/charts/models.py | 3 + apps/charts/tests.py | 3 + apps/charts/urls.py | 7 + apps/charts/views.py | 13 + apps/dyn_api/__init__.py | 5 + apps/dyn_api/admin.py | 8 + apps/dyn_api/apps.py | 10 + apps/dyn_api/helpers.py | 63 ++ apps/dyn_api/migrations/__init__.py | 0 apps/dyn_api/tests.py | 8 + apps/dyn_api/urls.py | 16 + apps/dyn_api/views.py | 155 ++++ apps/dyn_dt/.gitkeep | 0 apps/dyn_dt/__init__.py | 0 apps/dyn_dt/admin.py | 8 + apps/dyn_dt/apps.py | 5 + apps/dyn_dt/forms.py | 3 + apps/dyn_dt/migrations/0001_initial.py | 63 ++ apps/dyn_dt/migrations/__init__.py | 0 apps/dyn_dt/models.py | 24 + apps/dyn_dt/templatetags/__init__.py | 0 apps/dyn_dt/templatetags/get_attribute.py | 22 + apps/dyn_dt/tests.py | 3 + apps/dyn_dt/urls.py | 18 + apps/dyn_dt/utils.py | 13 + apps/dyn_dt/views.py | 315 +++++++ apps/pages/__init__.py | 0 apps/pages/admin.py | 3 + apps/pages/apps.py | 6 + apps/pages/migrations/0001_initial.py | 23 + apps/pages/migrations/__init__.py | 0 apps/pages/models.py | 12 + apps/pages/tests.py | 3 + apps/pages/urls.py | 7 + apps/pages/views.py | 9 + config/__init__.py | 0 config/asgi.py | 16 + config/settings.py | 207 +++++ config/urls.py | 26 + config/wsgi.py | 16 + static/.gitkeep | 0 static/gulpfile.js | 59 ++ static/img/csv.png | Bin 0 -> 9739 bytes static/img/export.png | Bin 0 -> 6060 bytes static/package.json | 44 + templates/.gitkeep | 0 templates/charts/index.html | 63 ++ templates/dyn_api/index.html | 38 + templates/dyn_dt/index.html | 36 + templates/dyn_dt/items-table.html | 22 + templates/dyn_dt/model.html | 585 ++++++++++++ templates/includes/sidebar.html | 165 ++++ templates/pages/billing.html | 321 +++++++ templates/pages/icons.html | 28 + templates/pages/index.html | 735 +++++++++++++++ templates/pages/map.html | 79 ++ templates/pages/notifications.html | 152 ++++ templates/pages/profile.html | 370 ++++++++ templates/pages/rtl.html | 1009 +++++++++++++++++++++ templates/pages/sign-in.html | 96 ++ templates/pages/sign-up.html | 56 ++ templates/pages/tables.html | 432 +++++++++ templates/pages/template.html | 10 + templates/pages/typography.html | 47 + templates/pages/virtual-reality.html | 165 ++++ 69 files changed, 5614 insertions(+) create mode 100644 apps/charts/__init__.py create mode 100644 apps/charts/admin.py create mode 100644 apps/charts/apps.py create mode 100644 apps/charts/migrations/__init__.py create mode 100644 apps/charts/models.py create mode 100644 apps/charts/tests.py create mode 100644 apps/charts/urls.py create mode 100644 apps/charts/views.py create mode 100644 apps/dyn_api/__init__.py create mode 100644 apps/dyn_api/admin.py create mode 100644 apps/dyn_api/apps.py create mode 100644 apps/dyn_api/helpers.py create mode 100644 apps/dyn_api/migrations/__init__.py create mode 100644 apps/dyn_api/tests.py create mode 100644 apps/dyn_api/urls.py create mode 100644 apps/dyn_api/views.py create mode 100644 apps/dyn_dt/.gitkeep create mode 100644 apps/dyn_dt/__init__.py create mode 100644 apps/dyn_dt/admin.py create mode 100644 apps/dyn_dt/apps.py create mode 100644 apps/dyn_dt/forms.py create mode 100644 apps/dyn_dt/migrations/0001_initial.py create mode 100644 apps/dyn_dt/migrations/__init__.py create mode 100644 apps/dyn_dt/models.py create mode 100644 apps/dyn_dt/templatetags/__init__.py create mode 100644 apps/dyn_dt/templatetags/get_attribute.py create mode 100644 apps/dyn_dt/tests.py create mode 100644 apps/dyn_dt/urls.py create mode 100644 apps/dyn_dt/utils.py create mode 100644 apps/dyn_dt/views.py create mode 100644 apps/pages/__init__.py create mode 100644 apps/pages/admin.py create mode 100644 apps/pages/apps.py create mode 100644 apps/pages/migrations/0001_initial.py create mode 100644 apps/pages/migrations/__init__.py create mode 100644 apps/pages/models.py create mode 100644 apps/pages/tests.py create mode 100644 apps/pages/urls.py create mode 100644 apps/pages/views.py create mode 100644 config/__init__.py create mode 100644 config/asgi.py create mode 100644 config/settings.py create mode 100644 config/urls.py create mode 100644 config/wsgi.py create mode 100644 static/.gitkeep create mode 100644 static/gulpfile.js create mode 100644 static/img/csv.png create mode 100644 static/img/export.png create mode 100644 static/package.json create mode 100644 templates/.gitkeep create mode 100644 templates/charts/index.html create mode 100644 templates/dyn_api/index.html create mode 100644 templates/dyn_dt/index.html create mode 100644 templates/dyn_dt/items-table.html create mode 100644 templates/dyn_dt/model.html create mode 100644 templates/includes/sidebar.html create mode 100644 templates/pages/billing.html create mode 100644 templates/pages/icons.html create mode 100644 templates/pages/index.html create mode 100644 templates/pages/map.html create mode 100644 templates/pages/notifications.html create mode 100644 templates/pages/profile.html create mode 100644 templates/pages/rtl.html create mode 100644 templates/pages/sign-in.html create mode 100644 templates/pages/sign-up.html create mode 100644 templates/pages/tables.html create mode 100644 templates/pages/template.html create mode 100644 templates/pages/typography.html create mode 100644 templates/pages/virtual-reality.html diff --git a/apps/charts/__init__.py b/apps/charts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/charts/admin.py b/apps/charts/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/charts/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/charts/apps.py b/apps/charts/apps.py new file mode 100644 index 0000000..ea2efb7 --- /dev/null +++ b/apps/charts/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ChartsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.charts' diff --git a/apps/charts/migrations/__init__.py b/apps/charts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/charts/models.py b/apps/charts/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/charts/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/charts/tests.py b/apps/charts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/charts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/charts/urls.py b/apps/charts/urls.py new file mode 100644 index 0000000..b04ad52 --- /dev/null +++ b/apps/charts/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from apps.charts import views + +urlpatterns = [ + path("", views.index, name="charts"), +] \ No newline at end of file diff --git a/apps/charts/views.py b/apps/charts/views.py new file mode 100644 index 0000000..a524f0b --- /dev/null +++ b/apps/charts/views.py @@ -0,0 +1,13 @@ +from django.shortcuts import render +from django.core import serializers +from apps.pages.models import * + +# Create your views here. + +def index(request): + products = serializers.serialize('json', Product.objects.all()) + context = { + 'segment': 'charts', + 'products': products + } + return render(request, 'charts/index.html', context) diff --git a/apps/dyn_api/__init__.py b/apps/dyn_api/__init__.py new file mode 100644 index 0000000..6c7c5a7 --- /dev/null +++ b/apps/dyn_api/__init__.py @@ -0,0 +1,5 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + diff --git a/apps/dyn_api/admin.py b/apps/dyn_api/admin.py new file mode 100644 index 0000000..304ee83 --- /dev/null +++ b/apps/dyn_api/admin.py @@ -0,0 +1,8 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from django.contrib import admin + +# Register your models here. diff --git a/apps/dyn_api/apps.py b/apps/dyn_api/apps.py new file mode 100644 index 0000000..a96e878 --- /dev/null +++ b/apps/dyn_api/apps.py @@ -0,0 +1,10 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from django.apps import AppConfig + +class DynApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.dyn_api' diff --git a/apps/dyn_api/helpers.py b/apps/dyn_api/helpers.py new file mode 100644 index 0000000..cce2cb7 --- /dev/null +++ b/apps/dyn_api/helpers.py @@ -0,0 +1,63 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +import datetime, sys, inspect, importlib + +from functools import wraps + +from django.db import models +from django.http import HttpResponseRedirect, HttpResponse + +from rest_framework import serializers + +class Utils: + @staticmethod + def get_class(config, name: str) -> models.Model: + return Utils.model_name_to_class(config[name]) + + @staticmethod + def get_manager(config, name: str) -> models.Manager: + return Utils.get_class(config, name).objects + + @staticmethod + def get_serializer(config, name: str): + class Serializer(serializers.ModelSerializer): + class Meta: + model = Utils.get_class(config, name) + fields = '__all__' + + return Serializer + + @staticmethod + def model_name_to_class(name: str): + + model_name = name.split('.')[-1] + model_import = name.replace('.'+model_name, '') + + module = importlib.import_module(model_import) + cls = getattr(module, model_name) + + return cls + +def check_permission(function): + @wraps(function) + def wrap(viewRequest, *args, **kwargs): + + try: + + # Check user + if viewRequest.request.user.is_authenticated: + + return function(viewRequest, *args, **kwargs) + + # For authentication for guests + return HttpResponseRedirect('/login/') + + except Exception as e: + + # On error + return HttpResponse( 'Error: ' + str( e ) ) + + return wrap diff --git a/apps/dyn_api/migrations/__init__.py b/apps/dyn_api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dyn_api/tests.py b/apps/dyn_api/tests.py new file mode 100644 index 0000000..c77ada2 --- /dev/null +++ b/apps/dyn_api/tests.py @@ -0,0 +1,8 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from django.test import TestCase + +# Create your tests here. diff --git a/apps/dyn_api/urls.py b/apps/dyn_api/urls.py new file mode 100644 index 0000000..e9e1cd8 --- /dev/null +++ b/apps/dyn_api/urls.py @@ -0,0 +1,16 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from django.contrib import admin +from django.urls import path +from apps.dyn_api import views + +urlpatterns = [ + path('api/', views.index, name="dynamic_api"), + + path('api//' , views.DynamicAPI.as_view(), name="model_api"), + path('api//' , views.DynamicAPI.as_view()), + path('api///' , views.DynamicAPI.as_view()), +] diff --git a/apps/dyn_api/views.py b/apps/dyn_api/views.py new file mode 100644 index 0000000..f77d080 --- /dev/null +++ b/apps/dyn_api/views.py @@ -0,0 +1,155 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from django.http import Http404 + +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator +from django.shortcuts import render, redirect, get_object_or_404 + +from rest_framework.generics import get_object_or_404 +from rest_framework.views import APIView +from rest_framework.response import Response +from django.http import HttpResponse + +from django.conf import settings + +DYNAMIC_API = {} + +try: + DYNAMIC_API = getattr(settings, 'DYNAMIC_API') +except: + pass + +from .helpers import Utils + +def index(request): + + context = { + 'routes' : settings.DYNAMIC_API.keys(), + 'segment': 'dynamic_api' + } + + return render(request, 'dyn_api/index.html', context) + +class DynamicAPI(APIView): + + # READ : GET api/model/id or api/model + def get(self, request, **kwargs): + + model_id = kwargs.get('id', None) + try: + if model_id is not None: + + # Validate for integer + try: + model_id = int(model_id) + + if model_id < 0: + raise ValueError('Expect positive int') + + except ValueError as e: + return Response(data={ + 'message': 'Input Error = ' + str(e), + 'success': False + }, status=400) + + thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=model_id) + model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing) + output = model_serializer.data + else: + all_things = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')).all() + thing_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name')) + output = [] + for thing in all_things: + output.append(thing_serializer(instance=thing).data) + except KeyError: + return Response(data={ + 'message': 'this model is not activated or not exist.', + 'success': False + }, status=400) + except Http404: + return Response(data={ + 'message': 'object with given id not found.', + 'success': False + }, status=404) + return Response(data={ + 'data': output, + 'success': True + }, status=200) + + # CREATE : POST api/model/ + #@check_permission + def post(self, request, **kwargs): + try: + model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(data=request.data) + if model_serializer.is_valid(): + model_serializer.save() + else: + return Response(data={ + **model_serializer.errors, + 'success': False + }, status=400) + except KeyError: + return Response(data={ + 'message': 'this model is not activated or not exist.', + 'success': False + }, status=400) + return Response(data={ + 'message': 'Record Created.', + 'success': True + }, status=200) + + # UPDATE : PUT api/model/id/ + #@check_permission + def put(self, request, **kwargs): + try: + thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=kwargs.get('id')) + model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing, + data=request.data, + partial=True) + if model_serializer.is_valid(): + model_serializer.save() + else: + return Response(data={ + **model_serializer.errors, + 'success': False + }, status=400) + except KeyError: + return Response(data={ + 'message': 'this model is not activated or not exist.', + 'success': False + }, status=400) + except Http404: + return Response(data={ + 'message': 'object with given id not found.', + 'success': False + }, status=404) + return Response(data={ + 'message': 'Record Updated.', + 'success': True + }, status=200) + + # DELETE : DELETE api/model/id/ + #@check_permission + def delete(self, request, **kwargs): + try: + model_manager = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')) + to_delete_id = kwargs.get('id') + model_manager.get(id=to_delete_id).delete() + except KeyError: + return Response(data={ + 'message': 'this model is not activated or not exist.', + 'success': False + }, status=400) + except Utils.get_class(DYNAMIC_API, kwargs.get('model_name')).DoesNotExist as e: + return Response(data={ + 'message': 'object with given id not found.', + 'success': False + }, status=404) + return Response(data={ + 'message': 'Record Deleted.', + 'success': True + }, status=200) diff --git a/apps/dyn_dt/.gitkeep b/apps/dyn_dt/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/dyn_dt/__init__.py b/apps/dyn_dt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dyn_dt/admin.py b/apps/dyn_dt/admin.py new file mode 100644 index 0000000..aeb9506 --- /dev/null +++ b/apps/dyn_dt/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from .models import * + +# Register your models here. + +admin.site.register(PageItems) +admin.site.register(HideShowFilter) +admin.site.register(ModelFilter) diff --git a/apps/dyn_dt/apps.py b/apps/dyn_dt/apps.py new file mode 100644 index 0000000..a92c5a5 --- /dev/null +++ b/apps/dyn_dt/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + +class DynDtConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.dyn_dt' diff --git a/apps/dyn_dt/forms.py b/apps/dyn_dt/forms.py new file mode 100644 index 0000000..d650f88 --- /dev/null +++ b/apps/dyn_dt/forms.py @@ -0,0 +1,3 @@ +from django import forms + +# Create your forms here. diff --git a/apps/dyn_dt/migrations/0001_initial.py b/apps/dyn_dt/migrations/0001_initial.py new file mode 100644 index 0000000..9aaeb35 --- /dev/null +++ b/apps/dyn_dt/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# Generated by Django 4.2.9 on 2025-03-30 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="HideShowFilter", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("parent", models.CharField(blank=True, max_length=255, null=True)), + ("key", models.CharField(max_length=255)), + ("value", models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name="ModelFilter", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("parent", models.CharField(blank=True, max_length=255, null=True)), + ("key", models.CharField(max_length=255)), + ("value", models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name="PageItems", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("parent", models.CharField(blank=True, max_length=255, null=True)), + ("items_per_page", models.IntegerField(default=25)), + ], + ), + ] diff --git a/apps/dyn_dt/migrations/__init__.py b/apps/dyn_dt/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dyn_dt/models.py b/apps/dyn_dt/models.py new file mode 100644 index 0000000..6a12d09 --- /dev/null +++ b/apps/dyn_dt/models.py @@ -0,0 +1,24 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +# Create your models here. + +class PageItems(models.Model): + parent = models.CharField(max_length=255, null=True, blank=True) + items_per_page = models.IntegerField(default=25) + +class HideShowFilter(models.Model): + parent = models.CharField(max_length=255, null=True, blank=True) + key = models.CharField(max_length=255) + value = models.BooleanField(default=False) + + def __str__(self): + return self.key + +class ModelFilter(models.Model): + parent = models.CharField(max_length=255, null=True, blank=True) + key = models.CharField(max_length=255) + value = models.CharField(max_length=255) + + def __str__(self): + return self.key \ No newline at end of file diff --git a/apps/dyn_dt/templatetags/__init__.py b/apps/dyn_dt/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dyn_dt/templatetags/get_attribute.py b/apps/dyn_dt/templatetags/get_attribute.py new file mode 100644 index 0000000..9aab7fc --- /dev/null +++ b/apps/dyn_dt/templatetags/get_attribute.py @@ -0,0 +1,22 @@ +from django import template +from datetime import datetime + +register = template.Library() + + +@register.filter(name="getattribute") +def getattribute(value, arg): + try: + attr_value = getattr(value, arg) + + if isinstance(attr_value, datetime): + return attr_value.strftime("%Y-%m-%d %H:%M:%S") + + return attr_value + except: + return '' + + +@register.filter +def get(dict_data, key): + return dict_data.get(key, []) \ No newline at end of file diff --git a/apps/dyn_dt/tests.py b/apps/dyn_dt/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/dyn_dt/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/dyn_dt/urls.py b/apps/dyn_dt/urls.py new file mode 100644 index 0000000..c180ea5 --- /dev/null +++ b/apps/dyn_dt/urls.py @@ -0,0 +1,18 @@ +from django.urls import path +from apps.dyn_dt import views + +urlpatterns = [ + path('dynamic-dt/', views.index, name="dynamic_dt"), + + path('create-filter//', views.create_filter, name="create_filter"), + path('create-page-items//', views.create_page_items, name="create_page_items"), + path('create-hide-show-items//', views.create_hide_show_filter, name="create_hide_show_filter"), + path('delete-filter///', views.delete_filter, name="delete_filter"), + path('create//', views.create, name="create"), + path('delete///', views.delete, name="delete"), + path('update///', views.update, name="update"), + + path('export-csv//', views.ExportCSVView.as_view(), name='export_csv'), + + path('dynamic-dt//', views.model_dt, name="model_dt"), +] diff --git a/apps/dyn_dt/utils.py b/apps/dyn_dt/utils.py new file mode 100644 index 0000000..4042ea8 --- /dev/null +++ b/apps/dyn_dt/utils.py @@ -0,0 +1,13 @@ +from django.db.models import Q + +def user_filter(request, queryset, fields, fk_fields=[]): + value = request.GET.get('search') + + if value: + dynamic_q = Q() + for field in fields: + if field not in fk_fields: + dynamic_q |= Q(**{f'{field}__icontains': value}) + return queryset.filter(dynamic_q) + + return queryset \ No newline at end of file diff --git a/apps/dyn_dt/views.py b/apps/dyn_dt/views.py new file mode 100644 index 0000000..54b7775 --- /dev/null +++ b/apps/dyn_dt/views.py @@ -0,0 +1,315 @@ +import requests, base64, json, csv +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.utils import timezone +from django.http import HttpResponse, JsonResponse +from django.utils.safestring import mark_safe +from django.conf import settings +from django.urls import reverse +from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage +from django.urls import reverse +from django.views import View +from django.db import models +from pprint import pp + +from apps.dyn_dt.models import ModelFilter, PageItems, HideShowFilter +from apps.dyn_dt.utils import user_filter + +from cli import * + +# Create your views here. + +def index(request): + + context = { + 'routes' : settings.DYNAMIC_DATATB.keys(), + 'segment': 'dynamic_dt' + } + + return render(request, 'dyn_dt/index.html', context) + +def create_filter(request, model_name): + model_name = model_name.lower() + if request.method == "POST": + keys = request.POST.getlist('key') + values = request.POST.getlist('value') + for i in range(len(keys)): + key = keys[i] + value = values[i] + + ModelFilter.objects.update_or_create( + parent=model_name, + key=key, + defaults={'value': value} + ) + + return redirect(reverse('model_dt', args=[model_name])) + + +def create_page_items(request, model_name): + model_name = model_name.lower() + if request.method == 'POST': + items = request.POST.get('items') + page_items, created = PageItems.objects.update_or_create( + parent=model_name, + defaults={'items_per_page':items} + ) + return redirect(reverse('model_dt', args=[model_name])) + + +def create_hide_show_filter(request, model_name): + model_name = model_name.lower() + if request.method == "POST": + data_str = list(request.POST.keys())[0] + data = json.loads(data_str) + + HideShowFilter.objects.update_or_create( + parent=model_name, + key=data.get('key'), + defaults={'value': data.get('value')} + ) + + response_data = {'message': 'Model updated successfully'} + return JsonResponse(response_data) + + return JsonResponse({'error': 'Invalid request'}, status=400) + + +def delete_filter(request, model_name, id): + model_name = model_name.lower() + filter_instance = ModelFilter.objects.get(id=id, parent=model_name) + filter_instance.delete() + return redirect(reverse('model_dt', args=[model_name])) + + +def get_model_field_names(model, field_type): + """Returns a list of field names based on the given field type.""" + return [ + field.name for field in model._meta.get_fields() + if isinstance(field, field_type) + ] + +def model_dt(request, aPath): + aModelName = None + aModelClass = None + choices_dict = {} + + if aPath in settings.DYNAMIC_DATATB.keys(): + aModelName = settings.DYNAMIC_DATATB[aPath] + aModelClass = name_to_class(aModelName) + + if not aModelClass: + return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath ) + + #db_fields = [field.name for field in aModelClass._meta.get_fields() if not field.is_relation] + db_fields = [field.name for field in aModelClass._meta.fields] + fk_fields = get_model_fk_values(aModelClass) + db_filters = [] + for f in db_fields: + if f not in fk_fields.keys(): + db_filters.append( f ) + + for field in aModelClass._meta.fields: + if field.choices: + choices_dict[field.name] = field.choices + + field_names = [] + for field_name in db_fields: + fields, created = HideShowFilter.objects.get_or_create(key=field_name, parent=aPath.lower()) + if fields.key in db_fields: + field_names.append(fields) + + model_series = {} + for f in db_fields: + f_values = list ( aModelClass.objects.values_list( f, flat=True) ) + model_series[ f ] = ', '.join( str(i) for i in f_values) + + # model filter + filter_string = {} + filter_instance = ModelFilter.objects.filter(parent=aPath.lower()) + for filter_data in filter_instance: + if filter_data.key in db_fields: + filter_string[f'{filter_data.key}__icontains'] = filter_data.value + + order_by = request.GET.get('order_by', 'id') + if order_by not in db_fields: + order_by = 'id' + + queryset = aModelClass.objects.filter(**filter_string).order_by(order_by) + item_list = user_filter(request, queryset, db_fields, fk_fields.keys()) + + # pagination + page_items = PageItems.objects.filter(parent=aPath.lower()).last() + p_items = 25 + if page_items: + p_items = page_items.items_per_page + + page = request.GET.get('page', 1) + paginator = Paginator(item_list, p_items) + + try: + items = paginator.page(page) + except PageNotAnInteger: + return redirect(reverse('model_dt', args=[aPath])) + except EmptyPage: + return redirect(reverse('model_dt', args=[aPath])) + + read_only_fields = ('id', ) + + integer_fields = get_model_field_names(aModelClass, models.IntegerField) + date_time_fields = get_model_field_names(aModelClass, models.DateTimeField) + email_fields = get_model_field_names(aModelClass, models.EmailField) + text_fields = get_model_field_names(aModelClass, (models.TextField, models.CharField)) + + context = { + 'page_title': 'Dynamic DataTable - ' + aPath.lower().title(), + 'link': aPath, + 'field_names': field_names, + 'db_field_names': db_fields, + 'db_filters': db_filters, + 'items': items, + 'page_items': p_items, + 'filter_instance': filter_instance, + 'read_only_fields': read_only_fields, + + 'integer_fields': integer_fields, + 'date_time_fields': date_time_fields, + 'email_fields': email_fields, + 'text_fields': text_fields, + 'fk_fields_keys': list( fk_fields.keys() ), + 'fk_fields': fk_fields , + 'choices_dict': choices_dict, + 'segment': 'dynamic_dt' + } + return render(request, 'dyn_dt/model.html', context) + + +@login_required(login_url='/accounts/login/') +def create(request, aPath): + aModelClass = None + + if aPath in settings.DYNAMIC_DATATB.keys(): + aModelName = settings.DYNAMIC_DATATB[aPath] + aModelClass = name_to_class(aModelName) + + if not aModelClass: + return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath ) + + if request.method == 'POST': + data = {} + fk_fields = get_model_fk(aModelClass) + + for attribute, value in request.POST.items(): + if attribute == 'csrfmiddlewaretoken': + continue + + # Process FKs + if attribute in fk_fields.keys(): + value = name_to_class( fk_fields[attribute] ).objects.filter(id=value).first() + + data[attribute] = value if value else '' + + aModelClass.objects.create(**data) + + return redirect(request.META.get('HTTP_REFERER')) + + +@login_required(login_url='/accounts/login/') +def delete(request, aPath, id): + aModelClass = None + + if aPath in settings.DYNAMIC_DATATB.keys(): + aModelName = settings.DYNAMIC_DATATB[aPath] + aModelClass = name_to_class(aModelName) + + if not aModelClass: + return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath ) + + item = aModelClass.objects.get(id=id) + item.delete() + return redirect(request.META.get('HTTP_REFERER')) + + +@login_required(login_url='/accounts/login/') +def update(request, aPath, id): + aModelClass = None + + if aPath in settings.DYNAMIC_DATATB.keys(): + aModelName = settings.DYNAMIC_DATATB[aPath] + aModelClass = name_to_class(aModelName) + + if not aModelClass: + return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath ) + + item = aModelClass.objects.get(id=id) + fk_fields = get_model_fk(aModelClass) + + if request.method == 'POST': + for attribute, value in request.POST.items(): + + if attribute == 'csrfmiddlewaretoken': + continue + + if getattr(item, attribute, value) is not None: + + # Process FKs + if attribute in fk_fields.keys(): + value = name_to_class( fk_fields[attribute] ).objects.filter(id=value).first() + + setattr(item, attribute, value) + + item.save() + + return redirect(request.META.get('HTTP_REFERER')) + + + +# Export as CSV +class ExportCSVView(View): + def get(self, request, aPath): + aModelName = None + aModelClass = None + + if aPath in settings.DYNAMIC_DATATB.keys(): + aModelName = settings.DYNAMIC_DATATB[aPath] + aModelClass = name_to_class(aModelName) + + if not aModelClass: + return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath ) + + db_field_names = [field.name for field in aModelClass._meta.get_fields()] + fields = [] + show_fields = HideShowFilter.objects.filter(value=False, parent=aPath.lower()) + + for field in show_fields: + if field.key in db_field_names: + fields.append(field.key) + else: + print(f"Field {field.key} does not exist in {aModelClass} model.") + + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = f'attachment; filename="{aPath.lower()}.csv"' + + writer = csv.writer(response) + writer.writerow(fields) # Write the header + + filter_string = {} + filter_instance = ModelFilter.objects.filter(parent=aPath.lower()) + for filter_data in filter_instance: + filter_string[f'{filter_data.key}__icontains'] = filter_data.value + + order_by = request.GET.get('order_by', 'id') + queryset = aModelClass.objects.filter(**filter_string).order_by(order_by) + + items = user_filter(request, queryset, db_field_names) + + for item in items: + row_data = [] + for field in fields: + try: + row_data.append(getattr(item, field)) + except AttributeError: + row_data.append('') + writer.writerow(row_data) + + return response \ No newline at end of file diff --git a/apps/pages/__init__.py b/apps/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/pages/admin.py b/apps/pages/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/pages/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/pages/apps.py b/apps/pages/apps.py new file mode 100644 index 0000000..9b06e8d --- /dev/null +++ b/apps/pages/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PagesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.pages" diff --git a/apps/pages/migrations/0001_initial.py b/apps/pages/migrations/0001_initial.py new file mode 100644 index 0000000..bf11793 --- /dev/null +++ b/apps/pages/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.9 on 2025-04-06 16:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('info', models.CharField(default='', max_length=100)), + ('price', models.IntegerField(blank=True, null=True)), + ], + ), + ] diff --git a/apps/pages/migrations/__init__.py b/apps/pages/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/pages/models.py b/apps/pages/models.py new file mode 100644 index 0000000..3a239d4 --- /dev/null +++ b/apps/pages/models.py @@ -0,0 +1,12 @@ +from django.db import models + +# Create your models here. + +class Product(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length = 100) + info = models.CharField(max_length = 100, default = '') + price = models.IntegerField(blank=True, null=True) + + def __str__(self): + return self.name diff --git a/apps/pages/tests.py b/apps/pages/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/pages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/pages/urls.py b/apps/pages/urls.py new file mode 100644 index 0000000..88a9cac --- /dev/null +++ b/apps/pages/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name='index'), +] diff --git a/apps/pages/views.py b/apps/pages/views.py new file mode 100644 index 0000000..ad772ae --- /dev/null +++ b/apps/pages/views.py @@ -0,0 +1,9 @@ +from django.shortcuts import render +from django.http import HttpResponse + +# Create your views here. + +def index(request): + + # Page from the theme + return render(request, 'pages/index.html', {'segment': 'dashboard'}) diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/asgi.py b/config/asgi.py new file mode 100644 index 0000000..8832d8e --- /dev/null +++ b/config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for core project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_asgi_application() diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..9974600 --- /dev/null +++ b/config/settings.py @@ -0,0 +1,207 @@ +""" +Django settings for core project. + +Generated by 'django-admin startproject' using Django 4.1.2. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" + +import os, random, string +from pathlib import Path +from dotenv import load_dotenv +from str2bool import str2bool + +load_dotenv() # take environment variables from .env. + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get('SECRET_KEY', 'Super_Secr3t_9999') + +# Enable/Disable DEBUG Mode +DEBUG = str2bool(os.environ.get('DEBUG')) +#print(' DEBUG -> ' + str(DEBUG) ) + +# Docker HOST +ALLOWED_HOSTS = ['*'] + +# Add here your deployment HOSTS +CSRF_TRUSTED_ORIGINS = ['http://localhost:8000', 'http://localhost:5085', 'http://127.0.0.1:8000', 'http://127.0.0.1:5085'] + +RENDER_EXTERNAL_HOSTNAME = os.environ.get('RENDER_EXTERNAL_HOSTNAME') +if RENDER_EXTERNAL_HOSTNAME: + ALLOWED_HOSTS.append(RENDER_EXTERNAL_HOSTNAME) + +# Application definition + +INSTALLED_APPS = [ + 'jazzmin', + 'admin_material.apps.AdminMaterialDashboardConfig', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + + # Serve UI pages + "apps.pages", + + # Dynamic DT + "apps.dyn_dt", + + # Dynamic API + "apps.dyn_api", + + # Charts + "apps.charts", + + # Tooling API-GEN + 'rest_framework', # Include DRF # <-- NEW + 'rest_framework.authtoken', # Include DRF Auth # <-- NEW +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "config.urls" + +UI_TEMPLATES = os.path.join(BASE_DIR, 'templates') + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [UI_TEMPLATES], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "config.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +DB_ENGINE = os.getenv('DB_ENGINE' , None) +DB_USERNAME = os.getenv('DB_USERNAME' , None) +DB_PASS = os.getenv('DB_PASS' , None) +DB_HOST = os.getenv('DB_HOST' , None) +DB_PORT = os.getenv('DB_PORT' , None) +DB_NAME = os.getenv('DB_NAME' , None) + +if DB_ENGINE and DB_NAME and DB_USERNAME: + DATABASES = { + 'default': { + 'ENGINE' : 'django.db.backends.' + DB_ENGINE, + 'NAME' : DB_NAME, + 'USER' : DB_USERNAME, + 'PASSWORD': DB_PASS, + 'HOST' : DB_HOST, + 'PORT' : DB_PORT, + }, + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite3', + } + } + +# Password validation +# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.1/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.1/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') + +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, 'static'), +) + +#if not DEBUG: +# STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +LOGIN_REDIRECT_URL = '/' +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + + +# ### DYNAMIC_DATATB Settings ### +DYNAMIC_DATATB = { + # SLUG -> Import_PATH + 'product' : "apps.pages.models.Product", +} +######################################## + +# Syntax: URI -> Import_PATH +DYNAMIC_API = { + # SLUG -> Import_PATH + 'product' : "apps.pages.models.Product", +} + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ], +} +######################################## diff --git a/config/urls.py b/config/urls.py new file mode 100644 index 0000000..adba413 --- /dev/null +++ b/config/urls.py @@ -0,0 +1,26 @@ +"""core URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path('', include('apps.pages.urls')), + path('', include('apps.dyn_dt.urls')), + path('', include('apps.dyn_api.urls')), + path('charts/', include('apps.charts.urls')), + path("admin/", admin.site.urls), + path("", include('admin_material.urls')) +] diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..6194205 --- /dev/null +++ b/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for core project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/static/gulpfile.js b/static/gulpfile.js new file mode 100644 index 0000000..90f9055 --- /dev/null +++ b/static/gulpfile.js @@ -0,0 +1,59 @@ +/* + +========================================================= +* AppSeed - Simple SCSS compiler via Gulp +========================================================= + +*/ + +var autoprefixer = require('gulp-autoprefixer'); +var browserSync = require('browser-sync').create(); +var cleanCss = require('gulp-clean-css'); +var gulp = require('gulp'); +const npmDist = require('gulp-npm-dist'); +var sass = require('gulp-sass')(require('node-sass')); +var wait = require('gulp-wait'); +var sourcemaps = require('gulp-sourcemaps'); +var rename = require("gulp-rename"); + +// Define COMMON paths + +const paths = { + src: { + base: './', + css: './css', + scss: './scss', + node_modules: './node_modules/', + vendor: './vendor' + } +}; + +// Compile SCSS +gulp.task('scss', function() { + return gulp.src([paths.src.scss + '/material-dashboard.scss']) + .pipe(wait(500)) + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer({ + overrideBrowserslist: ['> 1%'] + })) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest(paths.src.css)) + .pipe(browserSync.stream()); +}); + +// Minify CSS +gulp.task('minify:css', function() { + return gulp.src([ + paths.src.css + '/material-dashboard.css' + ]) + .pipe(cleanCss()) + .pipe(rename(function(path) { + // Updates the object in-place + path.extname = ".min.css"; + })) + .pipe(gulp.dest(paths.src.css)) +}); + +// Default Task: Compile SCSS and minify the result +gulp.task('default', gulp.series('scss', 'minify:css')); diff --git a/static/img/csv.png b/static/img/csv.png new file mode 100644 index 0000000000000000000000000000000000000000..330065b7216d212debf61c2c8e6e6d31729f0173 GIT binary patch literal 9739 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 WWtK_1XuS;pf3!6WF667*-Tn^~pOfzZ literal 0 HcmV?d00001 diff --git a/static/img/export.png b/static/img/export.png new file mode 100644 index 0000000000000000000000000000000000000000..25cac3cd936e4242dc7485f3e966a75e0e292186 GIT binary patch literal 6060 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 u`HQV)lHE4||Q)xWggm?~4vlFaHa*Dt)m4 literal 0 HcmV?d00001 diff --git a/static/package.json b/static/package.json new file mode 100644 index 0000000..7849559 --- /dev/null +++ b/static/package.json @@ -0,0 +1,44 @@ +{ + "name": "appseed-generic", + "version": "1.0.0", + "description": "Template", + "main": "gulpfile.js", + "author": "ApPSeed", + "keywords": [ + "css", + "sass", + "gulp", + "web" + ], + "homepage": "https://appseed.us", + "repository": { + "type": "git", + "url": "https://github.com/app-generator/app-generator" + }, + "bugs": { + "email": "support@appseed.us" + }, + "license": "MIT License", + "devDependencies": { + "browser-sync": "^2.27.4", + "del": "^6.0.0", + "gulp": "^4.0.2", + "gulp-autoprefixer": "^8.0.0", + "gulp-clean-css": "^4.3.0", + "gulp-cssbeautify": "^3.0.0", + "node-sass": "^6.0.1", + "gulp-file-include": "^2.3.0", + "gulp-header": "^2.0.9", + "gulp-htmlmin": "^5.0.1", + "gulp-npm-dist": "^1.0.3", + "gulp-plumber": "^1.2.1", + "gulp-rename": "^2.0.0", + "gulp-sass": "^5.0.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-uglify": "^3.0.2", + "gulp-wait": "^0.0.2", + "merge-stream": "^2.0.0" + }, + "dependencies": { + } +} \ No newline at end of file diff --git a/templates/.gitkeep b/templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/templates/charts/index.html b/templates/charts/index.html new file mode 100644 index 0000000..0aff9fa --- /dev/null +++ b/templates/charts/index.html @@ -0,0 +1,63 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block title %}Charts{% endblock title %} + +{% block content %} +
+
+ +
+
+
+
Bar Chart
+
+
+
+
+
+
+ + + +
+
+
+
Pie Chart
+
+
+
+
+
+
+ +
+
+{% endblock content %} + +{% block extra_js %} + + +{% endblock extra_js %} diff --git a/templates/dyn_api/index.html b/templates/dyn_api/index.html new file mode 100644 index 0000000..3e47f35 --- /dev/null +++ b/templates/dyn_api/index.html @@ -0,0 +1,38 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block title %} Dynamic APIs {% endblock title %} + +{% block content %} + +
+
+
+
+
+
+ Available Routes - defined in settings.DYNAMIC_API - Read Documentation. +
+
+
+
    + {% for link in routes %} +
  • + {{ link }} +
  • + {% endfor %} +
+
+
+
+
+
+ +{% endblock content %} + + +{% block extra_js %} + + + +{% endblock extra_js %} \ No newline at end of file diff --git a/templates/dyn_dt/index.html b/templates/dyn_dt/index.html new file mode 100644 index 0000000..fa70e6f --- /dev/null +++ b/templates/dyn_dt/index.html @@ -0,0 +1,36 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block title %} {% if page_title %} page_title {% else %} Dynamic DataTables {% endif %} {% endblock title %} + +{% block content %} + +
+
+
+
+
+
+ Available Routes - defined in settings.DYNAMIC_DATATB - Read Documentation. +
+
+
+
    + {% for link in routes %} +
  • + {{ link }} +
  • + {% endfor %} +
+
+
+
+
+
+ +{% endblock content %} + + +{% block extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/templates/dyn_dt/items-table.html b/templates/dyn_dt/items-table.html new file mode 100644 index 0000000..032889b --- /dev/null +++ b/templates/dyn_dt/items-table.html @@ -0,0 +1,22 @@ +{% load get_attribute %} + +
+ + + + {% for field in db_field_names %} + + {% endfor %} + + + + {% for item in items %} + + {% for field_name in db_field_names %} + + {% endfor %} + + {% endfor %} + +
{{ field }}
{{ item|getattribute:field_name }}
+
\ No newline at end of file diff --git a/templates/dyn_dt/model.html b/templates/dyn_dt/model.html new file mode 100644 index 0000000..b9fc5b2 --- /dev/null +++ b/templates/dyn_dt/model.html @@ -0,0 +1,585 @@ +{% extends "layouts/base.html" %} +{% load static get_attribute %} + +{% block title %} {% if page_title %} {{page_title}} {% else %} Dynamic DataTables {% endif %} {% endblock title %} + +{% block extrastyle %} + + + +{% endblock extrastyle %} + +{% block content %} +
+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ {% csrf_token %} + +
+
+ + img + +
+ {% if request.user.is_authenticated %} +
+ +
+ {% endif %} +
+
+
+ +
+
+ {% csrf_token %} + +
+

Filters

+ +
+ +
+ {% if filter_instance %} + {% for filter_data in filter_instance %} +
+
+ + +
+ X +
+ {% endfor %} + {% endif %} +
+ +
+ +
+
+ + + + {% for field in db_field_names %} + + {% endfor %} + + + + {% for item in items %} + + {% for field_name in db_field_names %} + + {% endfor %} + + {% if request.user.is_authenticated %} + + {% else %} + + {% endif %} + + + + + + + + + + + + {% endfor %} + +
{{ field }}
{{ item|getattribute:field_name }} + + edit + + + delete + + + + visibility + +
+
+
+ {% if items.has_other_pages %} + + {% endif %} +
+
+ + + + + + + +
+
+
+
+ +{% endblock content %} + + +{% block extra_js %} + + + + + + + +{% endblock extra_js %} \ No newline at end of file diff --git a/templates/includes/sidebar.html b/templates/includes/sidebar.html new file mode 100644 index 0000000..9f22630 --- /dev/null +++ b/templates/includes/sidebar.html @@ -0,0 +1,165 @@ +{% load i18n static admin_material %} + + \ No newline at end of file diff --git a/templates/pages/billing.html b/templates/pages/billing.html new file mode 100644 index 0000000..24bc212 --- /dev/null +++ b/templates/pages/billing.html @@ -0,0 +1,321 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
+
+ pattern-tree + +
+ wifi +
4562   1122   4594   7852
+
+
+
+

Card Holder

+
Jack Peterson
+
+
+

Expires

+
11/22
+
+
+
+ logo +
+
+
+
+
+
+
+
+
+
+
+
+ account_balance +
+
+
+
Salary
+ Belong Interactive +
+
+$2000
+
+
+
+
+
+
+
+ account_balance_wallet +
+
+
+
Paypal
+ Freelance Payment +
+
$455.00
+
+
+
+
+
+
+
+
+
+
+
Payment Method
+
+ +
+
+
+
+
+
+ logo +
****   ****   ****   7852
+ edit +
+
+
+
+ logo +
****   ****   ****   5248
+ edit +
+
+
+
+
+
+
+
+
+
+
+
+
+
Invoices
+
+
+ +
+
+
+
+
    +
  • +
    +
    March, 01, 2020
    + #MS-415646 +
    +
    + $180 + +
    +
  • +
  • +
    +
    February, 10, 2021
    + #RV-126749 +
    +
    + $250 + +
    +
  • +
  • +
    +
    April, 05, 2020
    + #FB-212562 +
    +
    + $560 + +
    +
  • +
  • +
    +
    June, 25, 2019
    + #QW-103578 +
    +
    + $120 + +
    +
  • +
  • +
    +
    March, 01, 2019
    + #AR-803481 +
    +
    + $300 + +
    +
  • +
+
+
+
+
+
+
+
+
+
Billing Information
+
+
+
    +
  • +
    +
    Oliver Liam
    + Company Name: Viking Burrito + Email Address: oliver@burrito.com + VAT Number: FRB1235476 +
    + +
  • +
  • +
    +
    Lucas Harper
    + Company Name: Stone Tech Zone + Email Address: lucas@stone-tech.com + VAT Number: FRB1235476 +
    + +
  • +
  • +
    +
    Ethan James
    + Company Name: Fiber Notion + Email Address: ethan@fiber.com + VAT Number: FRB1235476 +
    + +
  • +
+
+
+
+
+
+
+
+
+
Your Transaction's
+
+
+ date_range + 23 - 30 March 2020 +
+
+
+
+
Newest
+
    +
  • +
    + +
    +
    Netflix
    + 27 March 2020, at 12:30 PM +
    +
    +
    + - $ 2,500 +
    +
  • +
  • +
    + +
    +
    Apple
    + 27 March 2020, at 04:30 AM +
    +
    +
    + + $ 2,000 +
    +
  • +
+
Yesterday
+
    +
  • +
    + +
    +
    Stripe
    + 26 March 2020, at 13:45 PM +
    +
    +
    + + $ 750 +
    +
  • +
  • +
    + +
    +
    HubSpot
    + 26 March 2020, at 12:30 PM +
    +
    +
    + + $ 1,000 +
    +
  • +
  • +
    + +
    +
    Creative Tim
    + 26 March 2020, at 08:30 AM +
    +
    +
    + + $ 2,500 +
    +
  • +
  • +
    + +
    +
    Webflow
    + 26 March 2020, at 05:00 AM +
    +
    +
    + Pending +
    +
  • +
+
+
+
+
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} \ No newline at end of file diff --git a/templates/pages/icons.html b/templates/pages/icons.html new file mode 100644 index 0000000..acf49a5 --- /dev/null +++ b/templates/pages/icons.html @@ -0,0 +1,28 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
+
Material Design Icons
+

Handcrafted by our friends from + Google +

+
+
+
+

Through most of the examples in this dashboard, we have used the default Icons for the Material Design provided by Google.

+ face +
+
+
+
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} diff --git a/templates/pages/index.html b/templates/pages/index.html new file mode 100644 index 0000000..341ad2d --- /dev/null +++ b/templates/pages/index.html @@ -0,0 +1,735 @@ +{% extends "layouts/base.html" %} +{% load static %} + + +{% block content %} + +
+
+
+

Dashboard

+

+ Check the sales, value and bounce rate by country. +

+
+
+
+
+
+
+

Today's Money

+

$53k

+
+
+ weekend +
+
+
+
+ +
+
+
+
+
+
+
+

Today's Users

+

2300

+
+
+ person +
+
+
+
+ +
+
+
+
+
+
+
+

Ads Views

+

3,462

+
+
+ leaderboard +
+
+
+
+ +
+
+
+
+
+
+
+

Sales

+

$103,430

+
+
+ weekend +
+
+
+
+ +
+
+
+
+
+
+
+
Website Views
+

Last Campaign Performance

+
+
+ +
+
+
+
+ schedule +

campaign sent 2 days ago

+
+
+
+
+
+
+
+
Daily Sales
+

(+15%) increase in today sales.

+
+
+ +
+
+
+
+ schedule +

updated 4 min ago

+
+
+
+
+
+
+
+
Completed Tasks
+

Last Campaign Performance

+
+
+ +
+
+
+
+ schedule +

just updated

+
+
+
+
+
+
+
+
+
+
+
+
Projects
+

+ + 30 done this month +

+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompaniesMembersBudgetCompletion
+
+
+ xd +
+
+
Material XD Version
+
+
+
+ + + $14,000 + +
+
+
+ 60% +
+
+
+
+
+
+
+
+
+ atlassian +
+
+
Add Progress Track
+
+
+
+ + + $3,000 + +
+
+
+ 10% +
+
+
+
+
+
+
+
+
+ team7 +
+
+
Fix Platform Errors
+
+
+
+ + + Not set + +
+
+
+ 100% +
+
+
+
+
+
+
+
+
+ spotify +
+
+
Launch our Mobile App
+
+
+
+ + + $20,500 + +
+
+
+ 100% +
+
+
+
+
+
+
+
+
+ jira +
+
+
Add the New Pricing Page
+
+
+
+
+ + user5 + +
+
+ $500 + +
+
+
+ 25% +
+
+
+
+
+
+
+
+
+ invision +
+
+
Redesign New Online Shop
+
+
+
+ + + $2,000 + +
+
+
+ 40% +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Orders overview
+

+ + 24% this month +

+
+
+
+
+ + notifications + +
+
$2400, Design changes
+

22 DEC 7:20 PM

+
+
+
+ + code + +
+
New order #1832412
+

21 DEC 11 PM

+
+
+
+ + shopping_cart + +
+
Server payments for April
+

21 DEC 9:34 PM

+
+
+
+ + credit_card + +
+
New card added for order #4395133
+

20 DEC 2:20 AM

+
+
+
+ + key + +
+
Unlock packages for development
+

18 DEC 4:54 AM

+
+
+
+ + payments + +
+
New order #9583120
+

17 DEC

+
+
+
+
+
+
+
+ {% include "includes/footer.html" %} +
+ + {% endblock content %} + + {% block extra_js %} + + + + +{% endblock extra_js %} \ No newline at end of file diff --git a/templates/pages/map.html b/templates/pages/map.html new file mode 100644 index 0000000..de399a7 --- /dev/null +++ b/templates/pages/map.html @@ -0,0 +1,79 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
+
Vector Map
+
+
+
+
+
+
+
+
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} + +{% block extra_js %} + + + + + +{% endblock extra_js %} \ No newline at end of file diff --git a/templates/pages/notifications.html b/templates/pages/notifications.html new file mode 100644 index 0000000..1891cd3 --- /dev/null +++ b/templates/pages/notifications.html @@ -0,0 +1,152 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
Alerts
+
+
+ + + + + + + + +
+
+
+
+
Notifications
+

+ Notifications on this page use Toasts from Bootstrap. Read more details here. +

+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ + + + +
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} \ No newline at end of file diff --git a/templates/pages/profile.html b/templates/pages/profile.html new file mode 100644 index 0000000..09cc8fb --- /dev/null +++ b/templates/pages/profile.html @@ -0,0 +1,370 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+ +
+
+
+
+ profile_image +
+
+
+
+
+ Richard Davis +
+

+ CEO / Co-Founder +

+
+
+ +
+
+
+
+
+
+
Platform Settings
+
+
+
Account
+
    +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
+
Application
+
    +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
+
+
+
+
+
+
+
+
+
Profile Information
+
+
+ + + +
+
+
+
+

+ Hi, I’m Alec Thompson, Decisions: If you can’t decide, the answer is no. If two equally difficult paths, choose the one more painful in the short term (pain avoidance is creating an illusion of equality). +

+
+
    +
  • Full Name:   Alec M. Thompson
  • +
  • Mobile:   (44) 123 1234 123
  • +
  • Email:   alecthompson@mail.com
  • +
  • Location:   USA
  • +
  • + Social:   + + + + + + + + + +
  • +
+
+
+
+
+
+
+
Conversations
+
+
+
    +
  • +
    + kal +
    +
    +
    Sophie B.
    +

    Hi! I need more information..

    +
    + Reply +
  • +
  • +
    + kal +
    +
    +
    Anne Marie
    +

    Awesome work, can you..

    +
    + Reply +
  • +
  • +
    + kal +
    +
    +
    Ivanna
    +

    About files I can..

    +
    + Reply +
  • +
  • +
    + kal +
    +
    +
    Peterson
    +

    Have a great afternoon..

    +
    + Reply +
  • +
  • +
    + kal +
    +
    +
    Nick Daniel
    +

    Hi! I need more information..

    +
    + Reply +
  • +
+
+
+
+
+
+
Projects
+

Architects design houses

+
+
+
+
+
+ + img-blur-shadow + +
+
+

Project #2

+ +
+ Modern +
+
+

+ As Uber works through a huge amount of internal management turmoil. +

+
+ + +
+
+
+
+
+
+
+ + img-blur-shadow + +
+
+

Project #1

+ +
+ Scandinavian +
+
+

+ Music is something that every person has his or her own specific opinion about. +

+
+ + +
+
+
+
+
+
+
+ + img-blur-shadow + +
+
+

Project #3

+ +
+ Minimalist +
+
+

+ Different people have different taste, and various types of music. Music is life. +

+
+ + +
+
+
+
+
+
+
+ + img-blur-shadow + +
+
+

Project #4

+ +
+ Gothic +
+
+

+ Why would anyone pick blue over pink? Pink is obviously a better color. +

+
+ + +
+
+
+
+
+
+
+
+
+
+ + {% include "includes/footer.html" %} + + +{% endblock content %} \ No newline at end of file diff --git a/templates/pages/rtl.html b/templates/pages/rtl.html new file mode 100644 index 0000000..394180c --- /dev/null +++ b/templates/pages/rtl.html @@ -0,0 +1,1009 @@ +{% load static %} + + + + + {% include "includes/head.html" %} + + + + +
+ + + +
+
+
+
+
+
+ weekend +
+
+

أموال اليوم

+

$53k

+
+
+
+ +
+
+
+
+
+
+ leaderboard +
+
+

مستخدمو اليوم

+

2,300

+
+
+
+ +
+
+
+
+
+
+ store +
+
+

عملاء جدد

+

+ -2% + +3,462 +

+
+
+
+ +
+
+
+
+
+
+ weekend +
+
+

مبيعات

+

$103,430

+
+
+
+ +
+
+
+
+
+
+
+
مشاهدات الموقع
+

آخر أداء للحملة

+
+
+ +
+
+
+
+ schedule +

لحملة أرسلت قبل يومين

+
+
+
+
+
+
+
+
المبيعات اليومية
+

(+15%) زيادة في مبيعات اليوم

+
+
+ +
+
+
+
+ schedule +

لحملة أرسلت قبل يومين

+
+
+
+
+
+
+
+
المهام المكتملة
+

آخر أداء للحملة

+
+
+ +
+
+
+
+ schedule +

تم تحديثه للتو

+
+
+
+
+
+
+
+
+
+
+
+
المشاريع
+

+ + 30 انتهى هذا الشهر +

+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
المشروعأعضاءميزانيةإكمال
+
+
+ +
+
+
Material XD الإصدار
+
+
+
+ + + $14,000 + +
+
+
+ 60% +
+
+
+
+
+
+
+
+
+ +
+
+
أضف مسار التقدم إلى التطبيق الداخلي
+
+
+
+ + + $3,000 + +
+
+
+ 10% +
+
+
+
+
+
+
+
+
+ +
+
+
إصلاح أخطاء النظام الأساسي
+
+
+
+ + + غير مضبوط + +
+
+
+ 100% +
+
+
+
+
+
+
+
+
+ +
+
+
إطلاق تطبيق الهاتف المحمول الخاص بنا
+
+
+
+ + + $20,500 + +
+
+
+ 100% +
+
+
+
+
+
+
+
+
+ +
+
+
أضف صفحة التسعير الجديدة
+
+
+
+
+ + Image placeholder + +
+
+ $500 + +
+
+
+ 25% +
+
+
+
+
+
+
+
+
+ +
+
+
إعادة تصميم متجر جديد على الإنترنت
+
+
+
+ + + $2,000 + +
+
+
+ 40% +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
نظرة عامة على الطلبات
+

+ + 24% هذا الشهر +

+
+
+
+
+ + notifications + +
+
$2400, تغييرات في التصميم
+

22 DEC 7:20 PM

+
+
+
+ + code + +
+
طلب جديد #1832412
+

21 DEC 11 PM

+
+
+
+ + shopping_cart + +
+
مدفوعات الخادم لشهر أبريل
+

21 DEC 9:34 PM

+
+
+
+ + credit_card + +
+
تمت إضافة بطاقة جديدة للطلب #4395133
+

20 DEC 2:20 AM

+
+
+
+ + key + +
+
فتح الحزم من أجل التطوير
+

18 DEC 4:54 AM

+
+
+
+ + payments + +
+
طلب جديد #9583120
+

17 DEC

+
+
+
+
+
+
+
+ +
+
+
+ + settings + +
+
+
+
Material UI Configurator
+

See our dashboard options.

+
+
+ +
+ +
+
+
+ +
+
Sidebar Colors
+
+ +
+ + + + + + +
+
+ +
+
Sidenav Type
+

Choose between different sidenav types.

+
+
+ + + +
+

You can change the sidenav type just on desktop view.

+ +
+
Navbar Fixed
+
+ +
+
+
+
+
Light / Dark
+
+ +
+
+
+ Download + Documentation +
+
+
+ + + {% include "includes/scripts.html" %} + + + + + + + \ No newline at end of file diff --git a/templates/pages/sign-in.html b/templates/pages/sign-in.html new file mode 100644 index 0000000..d4bd30f --- /dev/null +++ b/templates/pages/sign-in.html @@ -0,0 +1,96 @@ +{% extends "layouts/base-auth.html" %} +{% load static %} + +{% block body %} bg-gray-200 {% endblock body %} + +{% block content %} + +
+ +
+ +{% endblock content %} diff --git a/templates/pages/sign-up.html b/templates/pages/sign-up.html new file mode 100644 index 0000000..0c34cc0 --- /dev/null +++ b/templates/pages/sign-up.html @@ -0,0 +1,56 @@ +{% extends "layouts/base-auth.html" %} +{% load static %} + +{% block content %} + +
+
+ +
+
+ +{% endblock content %} diff --git a/templates/pages/tables.html b/templates/pages/tables.html new file mode 100644 index 0000000..0d4a56a --- /dev/null +++ b/templates/pages/tables.html @@ -0,0 +1,432 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
+
Authors table
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AuthorFunctionStatusEmployed
+
+
+ user1 +
+
+
John Michael
+

john@creative-tim.com

+
+
+
+

Manager

+

Organization

+
+ Online + + 23/04/18 + + + Edit + +
+
+
+ user2 +
+
+
Alexa Liras
+

alexa@creative-tim.com

+
+
+
+

Programator

+

Developer

+
+ Offline + + 11/01/19 + + + Edit + +
+
+
+ user3 +
+
+
Laurent Perrier
+

laurent@creative-tim.com

+
+
+
+

Executive

+

Projects

+
+ Online + + 19/09/17 + + + Edit + +
+
+
+ user4 +
+
+
Michael Levi
+

michael@creative-tim.com

+
+
+
+

Programator

+

Developer

+
+ Online + + 24/12/08 + + + Edit + +
+
+
+ user5 +
+
+
Richard Gran
+

richard@creative-tim.com

+
+
+
+

Manager

+

Executive

+
+ Offline + + 04/10/21 + + + Edit + +
+
+
+ user6 +
+
+
Miriam Eric
+

miriam@creative-tim.com

+
+
+
+

Programator

+

Developer

+
+ Offline + + 14/09/20 + + + Edit + +
+
+
+
+
+
+
+
+
+
+
+
Projects table
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProjectBudgetStatusCompletion
+
+
+ spotify +
+
+
Asana
+
+
+
+

$2,500

+
+ working + +
+ 60% +
+
+
+
+
+
+
+ +
+
+
+ invision +
+
+
Github
+
+
+
+

$5,000

+
+ done + +
+ 100% +
+
+
+
+
+
+
+ +
+
+
+ jira +
+
+
Atlassian
+
+
+
+

$3,400

+
+ canceled + +
+ 30% +
+
+
+
+
+
+
+ +
+
+
+ webdev +
+
+
Bootstrap
+
+
+
+

$14,000

+
+ working + +
+ 80% +
+
+
+
+
+
+
+ +
+
+
+ slack +
+
+
Slack
+
+
+
+

$1,000

+
+ canceled + +
+ 0% +
+
+
+
+
+
+
+ +
+
+
+ xd +
+
+
Devto
+
+
+
+

$2,300

+
+ done + +
+ 100% +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} diff --git a/templates/pages/template.html b/templates/pages/template.html new file mode 100644 index 0000000..369353b --- /dev/null +++ b/templates/pages/template.html @@ -0,0 +1,10 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} \ No newline at end of file diff --git a/templates/pages/typography.html b/templates/pages/typography.html new file mode 100644 index 0000000..e194648 --- /dev/null +++ b/templates/pages/typography.html @@ -0,0 +1,47 @@ +{% extends "layouts/base.html" %} +{% load static %} + +{% block content %} + +
+
+
+
+
+
+
Material Dashboard Heading
+

Created using Roboto Slab Font Family +

+
+
+
+

h1. Bootstrap heading

+

h2. Bootstrap heading

+

h3. Bootstrap heading

+

h4. Bootstrap heading

+
h5. Bootstrap heading
+
h6. Bootstrap heading
+

You can use the mark tag to highlight text.

+

This line of text is meant to be treated as deleted text.

+

This line of text is meant to be treated as no longer accurate.

+

This line of text is meant to be treated as an addition to the document.

+

This line of text will render as underlined

+

This line of text is meant to be treated as fine print.

+

This line rendered as bold text.

+

This line rendered as italicized text.

+
+
+

Because I’m here to follow my dreams and inspire other people to follow their dreams, too.

+
+ +
+
+
+
+
+ {% include "includes/footer.html" %} +
+ +{% endblock content %} diff --git a/templates/pages/virtual-reality.html b/templates/pages/virtual-reality.html new file mode 100644 index 0000000..9f7c45c --- /dev/null +++ b/templates/pages/virtual-reality.html @@ -0,0 +1,165 @@ + +{% load static %} + + + + {% include "includes/head.html" %} + + + +
+ + {% include "includes/navigation.html" %} + +
+
+ {% include "includes/sidebar.html" %} + +
+
+
+
+
+ + Image placeholder + + + + +
+
+
+
+

28°C

+
Cloudy
+
+
+ image sun +
+
+
+
+
+
+
+
08:00
+
Synk up with Mark + Hangouts +
+
+
+
+
09:30
+
Gym
+ World Class +
+
+
+
+
11:00
+
Design Review
+ Zoom +
+
+
+ + expand_more + +
+
+
+
+
+
+
To Do
+
+

7

+

items

+
+
+

Shopping

+

Meeting

+
+ + expand_more + +
+
+
+
+

Emails (21)

+ + Check + +
+
+
+
+
+
+
+
+
+
Night Jazz
+

Gary Coleman

+
+ + + +
+
+
+
+
+
+
+

Messages

+ +
+
+
+
+
+
+
+
+
+
+
+ + {% include "includes/footer.html" %} + + {% include "includes/configurator.html" %} + + {% include "includes/scripts.html" %} + + + + \ No newline at end of file