| @ -0,0 +1,3 @@ | |||||
| Dockerfile | |||||
| Makefile | |||||
| volcadossql/ | |||||
| @ -0,0 +1,73 @@ | |||||
| # syntax=docker/dockerfile:1 | |||||
| ################## | |||||
| # BUILDER # | |||||
| ################## | |||||
| FROM python:3.11.4-slim-buster AS builder | |||||
| # set work directory | |||||
| WORKDIR /app | |||||
| # set environment variables | |||||
| ENV PYTHONDONTWRITEBYTECODE=1 | |||||
| ENV PYTHONUNBUFFERED=1 | |||||
| # install system dependencies | |||||
| RUN apt-get update && \ | |||||
| apt-get install -y --no-install-recommends gcc | |||||
| # lint | |||||
| RUN pip install --upgrade pip | |||||
| RUN pip install flake8==6.0.0 | |||||
| COPY . /app/ | |||||
| RUN flake8 --ignore=E501,F401,E126 . | |||||
| COPY ./requirements.txt . | |||||
| RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt | |||||
| ################## | |||||
| # FINAL # | |||||
| ################## | |||||
| FROM python:3.11.4-slim-buster | |||||
| # create directory for the app user | |||||
| RUN mkdir -p /app | |||||
| # create the app user | |||||
| #RUN addgroup --system app && adduser --system --group app | |||||
| # create the appropriate directories | |||||
| ENV APP_HOME=/app | |||||
| RUN mkdir -p $APP_HOME | |||||
| #RUN mkdir -p $APP_HOME/staticfiles | |||||
| #RUN mkdir -p $APP_HOME/mediafiles | |||||
| WORKDIR $APP_HOME | |||||
| # install system dependencies | |||||
| RUN apt-get update && apt-get install -y sqlite3 netcat | |||||
| COPY --from=builder /app/wheels /wheels | |||||
| COPY --from=builder /app/requirements.txt . | |||||
| RUN pip install --upgrade pip | |||||
| RUN pip install --no-cache /wheels/* | |||||
| # copy entrypoint.sh | |||||
| COPY ./entrypoint.sh . | |||||
| # copy project | |||||
| COPY . $APP_HOME | |||||
| # chown all the files to the app user | |||||
| #RUN chown -R app:app $APP_HOME | |||||
| # change to the app user | |||||
| #USER app | |||||
| WORKDIR $APP_HOME/biblioteca | |||||
| # run entrypoint.sh | |||||
| ENTRYPOINT ["/app/entrypoint.sh"] | |||||
| @ -0,0 +1,48 @@ | |||||
| export ARQUITECTURA := $(shell lscpu |grep itectur | tr -d ' '| cut -f2 -d':') | |||||
| export IMG_VERSION = 1.43 | |||||
| export IMG_NGINX_VERSION = 1.17 | |||||
| # limpia todo | |||||
| all: imagen nginx clean install | |||||
| imagen: | |||||
| cd ../; make | |||||
| install: | |||||
| -kubectl create -f namespace.yaml | |||||
| -kubectl create -f reg-secret.yaml | |||||
| -kubectl create -f env-prod-configmap.yaml | |||||
| -kubectl create -f env-prod-db-configmap.yaml | |||||
| -kubectl create -f pv-local-libros.yaml | |||||
| -kubectl create -f libros-prod-persistentvolumeclaim.yaml | |||||
| -kubectl create -f static-volume-persistentvolumeclaim.yaml | |||||
| -kubectl create -f postgres-data-persistentvolumeclaim.yaml | |||||
| -kubectl create -f db-deployment.yaml | |||||
| -kubectl create -f db-service.yaml | |||||
| -envsubst < libros-deployment.yaml |kubectl create -f - | |||||
| -envsubst < nginx-deployment.yaml |kubectl create -f - | |||||
| -kubectl create -f nginx-service.yaml | |||||
| clean: | |||||
| -envsubst < nginx-deployment.yaml |kubectl delete -f - | |||||
| -kubectl delete -f nginx-service.yaml | |||||
| -envsubst < libros-deployment.yaml |kubectl delete -f - | |||||
| -kubectl delete -f db-deployment.yaml | |||||
| -kubectl delete -f db-service.yaml | |||||
| -kubectl delete -f env-prod-configmap.yaml | |||||
| -kubectl delete -f env-prod-db-configmap.yaml | |||||
| -kubectl delete -f postgres-data-persistentvolumeclaim.yaml | |||||
| -kubectl delete -f static-volume-persistentvolumeclaim.yaml | |||||
| -kubectl delete -f libros-prod-persistentvolumeclaim.yaml | |||||
| -kubectl delete -f pv-local-libros.yaml | |||||
| -kubectl delete -f reg-secret.yaml | |||||
| -kubectl delete -f namespace.yaml | |||||
| nginx: | |||||
| cd ../nginx; make | |||||
| @ -0,0 +1,52 @@ | |||||
| apiVersion: apps/v1 | |||||
| kind: Deployment | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: db | |||||
| name: db | |||||
| namespace: libros | |||||
| spec: | |||||
| replicas: 1 | |||||
| selector: | |||||
| matchLabels: | |||||
| io.kompose.service: db | |||||
| strategy: | |||||
| type: Recreate | |||||
| template: | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: db | |||||
| spec: | |||||
| containers: | |||||
| - env: | |||||
| - name: POSTGRES_DB | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: POSTGRES_DB | |||||
| name: env-prod-db | |||||
| - name: POSTGRES_PASSWORD | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: POSTGRES_PASSWORD | |||||
| name: env-prod-db | |||||
| - name: POSTGRES_USER | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: POSTGRES_USER | |||||
| name: env-prod-db | |||||
| image: postgres:15 | |||||
| name: db | |||||
| volumeMounts: | |||||
| - mountPath: /var/lib/postgresql/data | |||||
| name: postgres-data | |||||
| restartPolicy: Always | |||||
| volumes: | |||||
| - name: postgres-data | |||||
| persistentVolumeClaim: | |||||
| claimName: postgres-data | |||||
| @ -0,0 +1,17 @@ | |||||
| apiVersion: v1 | |||||
| kind: Service | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: db | |||||
| name: db | |||||
| namespace: libros | |||||
| spec: | |||||
| ports: | |||||
| - name: "5432" | |||||
| port: 5432 | |||||
| targetPort: 5432 | |||||
| selector: | |||||
| io.kompose.service: db | |||||
| @ -0,0 +1 @@ | |||||
| kubectl -n libros exec -ti deployment.apps/libros -- /bin/bash | |||||
| @ -0,0 +1 @@ | |||||
| kubectl -n libros exec -ti deployment.apps/db -- psql --username=creylopez --dbname=libros | |||||
| @ -0,0 +1,19 @@ | |||||
| apiVersion: v1 | |||||
| data: | |||||
| DEBUG: "0" | |||||
| DJANGO_ALLOWED_HOSTS: "libros.reymota.es k8s-server localhost 127.0.0.1 [::1]" | |||||
| CSRF_TRUSTED_ORIGINS: "https://libros.reymota.es" | |||||
| SECRET_KEY: change_me | |||||
| SQL_DATABASE: libros | |||||
| SQL_ENGINE: django.db.backends.postgresql | |||||
| SQL_HOST: db | |||||
| SQL_PASSWORD: Dsa-0213 | |||||
| SQL_PORT: "5432" | |||||
| SQL_USER: creylopez | |||||
| DATABASE: postgres | |||||
| kind: ConfigMap | |||||
| metadata: | |||||
| labels: | |||||
| io.kompose.service: web-env-prod | |||||
| name: env-prod | |||||
| namespace: libros | |||||
| @ -0,0 +1,11 @@ | |||||
| apiVersion: v1 | |||||
| data: | |||||
| POSTGRES_DB: libros | |||||
| POSTGRES_PASSWORD: Dsa-0213 | |||||
| POSTGRES_USER: creylopez | |||||
| kind: ConfigMap | |||||
| metadata: | |||||
| labels: | |||||
| io.kompose.service: db-env-prod-db | |||||
| name: env-prod-db | |||||
| namespace: libros | |||||
| @ -0,0 +1,124 @@ | |||||
| apiVersion: v1 | |||||
| kind: Service | |||||
| metadata: | |||||
| name: libros | |||||
| namespace: libros | |||||
| spec: | |||||
| ports: | |||||
| - name: "8000" | |||||
| port: 8000 | |||||
| targetPort: 8000 | |||||
| selector: | |||||
| app: libros | |||||
| --- | |||||
| apiVersion: apps/v1 | |||||
| kind: Deployment | |||||
| metadata: | |||||
| name: libros | |||||
| namespace: libros | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| replicas: 1 | |||||
| selector: | |||||
| matchLabels: | |||||
| app: libros | |||||
| strategy: | |||||
| type: Recreate | |||||
| template: | |||||
| metadata: | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| containers: | |||||
| - args: | |||||
| - gunicorn | |||||
| - biblioteca.wsgi:application | |||||
| - --bind | |||||
| - 0.0.0.0:8000 | |||||
| name: libros | |||||
| image: registry.reymota.es/libros-$ARQUITECTURA:$IMG_VERSION | |||||
| env: | |||||
| - name: VERSION | |||||
| value: "$IMG_VERSION" | |||||
| - name: ENVIRONMENT | |||||
| value: "Producción" | |||||
| - name: DEBUG | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: DEBUG | |||||
| name: env-prod | |||||
| - name: DJANGO_ALLOWED_HOSTS | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: DJANGO_ALLOWED_HOSTS | |||||
| name: env-prod | |||||
| - name: CSRF_TRUSTED_ORIGINS | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: CSRF_TRUSTED_ORIGINS | |||||
| name: env-prod | |||||
| - name: SECRET_KEY | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SECRET_KEY | |||||
| name: env-prod | |||||
| - name: DATABASE | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: DATABASE | |||||
| name: env-prod | |||||
| - name: SQL_HOST | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_HOST | |||||
| name: env-prod | |||||
| - name: SQL_PORT | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_PORT | |||||
| name: env-prod | |||||
| - name: SQL_ENGINE | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_ENGINE | |||||
| name: env-prod | |||||
| - name: SQL_DATABASE | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_DATABASE | |||||
| name: env-prod | |||||
| - name: SQL_USER | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_USER | |||||
| name: env-prod | |||||
| - name: SQL_PASSWORD | |||||
| valueFrom: | |||||
| configMapKeyRef: | |||||
| key: SQL_PASSWORD | |||||
| name: env-prod | |||||
| ports: | |||||
| - containerPort: 8000 | |||||
| protocol: TCP | |||||
| volumeMounts: | |||||
| - mountPath: /app/biblioteca/mediafiles | |||||
| name: libros-media | |||||
| - mountPath: /app/gestion/migrations | |||||
| name: libros-migrations | |||||
| - mountPath: /app/biblioteca/staticfiles | |||||
| name: static-volume | |||||
| imagePullSecrets: | |||||
| - name: myregistrykey | |||||
| restartPolicy: Always | |||||
| volumes: | |||||
| - name: libros-media | |||||
| persistentVolumeClaim: | |||||
| claimName: libros-media | |||||
| - name: libros-migrations | |||||
| persistentVolumeClaim: | |||||
| claimName: libros-migrations | |||||
| - name: static-volume | |||||
| persistentVolumeClaim: | |||||
| claimName: static-volume | |||||
| status: {} | |||||
| @ -0,0 +1,32 @@ | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolumeClaim | |||||
| metadata: | |||||
| creationTimestamp: null | |||||
| labels: | |||||
| io.kompose.service: libros-media | |||||
| name: libros-media | |||||
| namespace: libros | |||||
| spec: | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| resources: | |||||
| requests: | |||||
| storage: 100Mi | |||||
| status: {} | |||||
| --- | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolumeClaim | |||||
| metadata: | |||||
| creationTimestamp: null | |||||
| labels: | |||||
| io.kompose.service: libros-migrations | |||||
| name: libros-migrations | |||||
| namespace: libros | |||||
| spec: | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| resources: | |||||
| requests: | |||||
| storage: 50Mi | |||||
| status: {} | |||||
| @ -0,0 +1,7 @@ | |||||
| ################################################### | |||||
| # Namespace Libros | |||||
| ################################################### | |||||
| apiVersion: v1 | |||||
| kind: Namespace | |||||
| metadata: | |||||
| name: libros | |||||
| @ -0,0 +1,46 @@ | |||||
| apiVersion: apps/v1 | |||||
| kind: Deployment | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: nginx | |||||
| name: nginx | |||||
| namespace: libros | |||||
| spec: | |||||
| replicas: 1 | |||||
| selector: | |||||
| matchLabels: | |||||
| io.kompose.service: nginx | |||||
| strategy: | |||||
| type: Recreate | |||||
| template: | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: nginx | |||||
| spec: | |||||
| containers: | |||||
| - image: registry.reymota.es/nginx-libros-$ARQUITECTURA:$IMG_NGINX_VERSION | |||||
| name: nginx | |||||
| ports: | |||||
| - containerPort: 80 | |||||
| protocol: TCP | |||||
| volumeMounts: | |||||
| - mountPath: /app/biblioteca/staticfiles | |||||
| name: static-volume | |||||
| - mountPath: /app/biblioteca/mediafiles | |||||
| name: libros-media | |||||
| imagePullSecrets: | |||||
| - name: myregistrykey | |||||
| restartPolicy: Always | |||||
| volumes: | |||||
| - name: static-volume | |||||
| persistentVolumeClaim: | |||||
| claimName: static-volume | |||||
| - name: libros-media | |||||
| persistentVolumeClaim: | |||||
| claimName: libros-media | |||||
| @ -0,0 +1,20 @@ | |||||
| apiVersion: v1 | |||||
| kind: Service | |||||
| metadata: | |||||
| annotations: | |||||
| kompose.cmd: kompose convert | |||||
| kompose.version: 1.34.0 (cbf2835db) | |||||
| labels: | |||||
| io.kompose.service: nginx | |||||
| name: nginx | |||||
| namespace: libros | |||||
| spec: | |||||
| type: NodePort | |||||
| ports: | |||||
| - name: "1337" | |||||
| port: 1337 | |||||
| nodePort: 30343 | |||||
| targetPort: 80 | |||||
| selector: | |||||
| io.kompose.service: nginx | |||||
| @ -0,0 +1,13 @@ | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolumeClaim | |||||
| metadata: | |||||
| labels: | |||||
| io.kompose.service: postgres-data | |||||
| name: postgres-data | |||||
| namespace: libros | |||||
| spec: | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| resources: | |||||
| requests: | |||||
| storage: 100Mi | |||||
| @ -0,0 +1,59 @@ | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolume | |||||
| metadata: | |||||
| name: libros-media-folder | |||||
| namespace: libros | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| capacity: | |||||
| storage: 100Mi | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| hostPath: | |||||
| path: "/mnt/Externo/libros/media" | |||||
| --- | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolume | |||||
| metadata: | |||||
| name: libros-migrations-folder | |||||
| namespace: libros | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| capacity: | |||||
| storage: 50Mi | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| hostPath: | |||||
| path: "/mnt/Externo/libros/migrations" | |||||
| --- | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolume | |||||
| metadata: | |||||
| name: libros-static-folder | |||||
| namespace: libros | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| capacity: | |||||
| storage: 70Mi | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| hostPath: | |||||
| path: "/mnt/Externo/libros/static" | |||||
| --- | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolume | |||||
| metadata: | |||||
| name: libros-pg-folder | |||||
| namespace: libros | |||||
| labels: | |||||
| app: libros | |||||
| spec: | |||||
| capacity: | |||||
| storage: 200Mi | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| hostPath: | |||||
| path: "/mnt/Externo/libros/pg" | |||||
| @ -0,0 +1,8 @@ | |||||
| apiVersion: v1 | |||||
| kind: Secret | |||||
| metadata: | |||||
| name: myregistrykey | |||||
| namespace: libros | |||||
| data: | |||||
| .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJyZWdpc3RyeS5yZXltb3RhLmVzIjogewoJCQkiYXV0aCI6ICJZM0psZVd4dmNHVjZPbEpsZVMweE1UYzIiCgkJfQoJfQp9 | |||||
| type: kubernetes.io/dockerconfigjson | |||||
| @ -0,0 +1,13 @@ | |||||
| apiVersion: v1 | |||||
| kind: PersistentVolumeClaim | |||||
| metadata: | |||||
| labels: | |||||
| io.kompose.service: static-volume | |||||
| name: static-volume | |||||
| namespace: libros | |||||
| spec: | |||||
| accessModes: | |||||
| - ReadWriteOnce | |||||
| resources: | |||||
| requests: | |||||
| storage: 70Mi | |||||
| @ -0,0 +1 @@ | |||||
| docker run -it registry.reymota.es/libros:1.19 bash | |||||
| @ -0,0 +1,8 @@ | |||||
| install: | |||||
| echo "Creando imagen con version ${IMG_VERSION}" | |||||
| docker build --no-cache -t registry.reymota.es/libros-${ARQUITECTURA}:${IMG_VERSION} . | |||||
| docker push registry.reymota.es/libros-${ARQUITECTURA}:${IMG_VERSION} | |||||
| @ -0,0 +1,16 @@ | |||||
| https://learndjango.com/tutorials/django-login-and-logout-tutorial | |||||
| Username: {{ user.username }} | |||||
| User Full name: {{ user.get_full_name }} | |||||
| User Group: {{ user.groups.all.0 }} | |||||
| Email: {{ user.email }} | |||||
| Session Started at: {{ user.last_login }} | |||||
| ## Para funcionar con gunicorn y nginx | |||||
| https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/ | |||||
| @ -0,0 +1,19 @@ | |||||
| #!/bin/bash | |||||
| if [ "$DATABASE" = "postgres" ] | |||||
| then | |||||
| echo "Waiting for postgres..." | |||||
| while ! nc -z $SQL_HOST $SQL_PORT; do | |||||
| sleep 0.1 | |||||
| done | |||||
| echo "PostgreSQL started" | |||||
| else | |||||
| echo "la base de datos no es postgres: '$DATABASE'" | |||||
| fi | |||||
| #python manage.py flush --no-input | |||||
| #python manage.py migrate | |||||
| exec "$@" | |||||
| @ -0,0 +1,4 @@ | |||||
| FROM nginx:1.25 | |||||
| RUN rm /etc/nginx/conf.d/default.conf | |||||
| COPY nginx.conf /etc/nginx/conf.d | |||||
| @ -0,0 +1,8 @@ | |||||
| install: | |||||
| echo "Creando imagen con version ${IMG_NGINX_VERSION} para la arquitectura ${ARQUITECTURA}" | |||||
| docker build --no-cache -t registry.reymota.es/nginx-libros-${ARQUITECTURA}:${IMG_NGINX_VERSION} . | |||||
| docker push registry.reymota.es/nginx-libros-${ARQUITECTURA}:${IMG_NGINX_VERSION} | |||||
| @ -0,0 +1,25 @@ | |||||
| upstream libros { | |||||
| server libros:8000; | |||||
| } | |||||
| server { | |||||
| listen 80; | |||||
| location / { | |||||
| proxy_pass http://libros; | |||||
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |||||
| proxy_set_header Host $http_host; | |||||
| proxy_redirect off; | |||||
| client_max_body_size 100M; | |||||
| } | |||||
| location /static/ { | |||||
| alias /app/biblioteca/staticfiles/; | |||||
| } | |||||
| location /media/ { | |||||
| alias /app/biblioteca/mediafiles/; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,4 @@ | |||||
| asgiref==3.8.1 | |||||
| Django==5.1 | |||||
| sqlparse==0.5.1 | |||||
| typing_extensions==4.12.2 | |||||
| @ -0,0 +1,3 @@ | |||||
| from django.contrib import admin | |||||
| # Register your models here. | |||||
| @ -0,0 +1,6 @@ | |||||
| from django.apps import AppConfig | |||||
| class AccountsConfig(AppConfig): | |||||
| default_auto_field = 'django.db.models.BigAutoField' | |||||
| name = 'accounts' | |||||
| @ -0,0 +1,3 @@ | |||||
| from django.db import models | |||||
| # Create your models here. | |||||
| @ -0,0 +1,3 @@ | |||||
| from django.test import TestCase | |||||
| # Create your tests here. | |||||
| @ -0,0 +1,8 @@ | |||||
| # accounts/urls.py | |||||
| from django.urls import path | |||||
| from django.contrib.auth import views as auth_views | |||||
| urlpatterns = [ | |||||
| path('login/', auth_views.LoginView.as_view(), name='login'), | |||||
| path('logout/', auth_views.LogoutView.as_view(), name='logout'), | |||||
| ] | |||||
| @ -0,0 +1 @@ | |||||
| from django.shortcuts import render | |||||
| @ -0,0 +1,22 @@ | |||||
| #!/usr/bin/env python | |||||
| """Django's command-line utility for administrative tasks.""" | |||||
| import os | |||||
| import sys | |||||
| def main(): | |||||
| """Run administrative tasks.""" | |||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'vehiculos.settings') | |||||
| try: | |||||
| from django.core.management import execute_from_command_line | |||||
| except ImportError as exc: | |||||
| raise ImportError( | |||||
| "Couldn't import Django. Are you sure it's installed and " | |||||
| "available on your PYTHONPATH environment variable? Did you " | |||||
| "forget to activate a virtual environment?" | |||||
| ) from exc | |||||
| execute_from_command_line(sys.argv) | |||||
| if __name__ == '__main__': | |||||
| main() | |||||
| @ -0,0 +1,45 @@ | |||||
| from django.contrib import admin | |||||
| from django.contrib.auth.admin import UserAdmin | |||||
| # Register your models here. | |||||
| from .models import Vehiculos, Repostajes, ReyMotaUser | |||||
| from .forms import ReyMotaUserCreationForm, ReyMotaUserChangeForm | |||||
| admin.site.register(Vehiculos) | |||||
| admin.site.register(Repostajes) | |||||
| class ReyMotaUserAdmin(UserAdmin): | |||||
| add_form = ReyMotaUserCreationForm | |||||
| form = ReyMotaUserChangeForm | |||||
| model = ReyMotaUser | |||||
| list_display = ("email", "nombre", "is_staff", "is_active", "foto") | |||||
| list_filter = ("email", "nombre", "is_staff", "is_active",) | |||||
| fieldsets = ( | |||||
| (None, {"fields": ("email", "password")}), | |||||
| ("Personal", {"fields": ("nombre",)}), | |||||
| ("Permissions", {"fields": ("is_staff", "is_active", "groups", | |||||
| "user_permissions")}), | |||||
| ("Varios", {"fields": ("foto",)}), | |||||
| ) | |||||
| add_fieldsets = ( | |||||
| ( | |||||
| None, | |||||
| { | |||||
| "classes": ("wide",), | |||||
| "fields": ( | |||||
| "email", "password1", "password2", "is_staff", | |||||
| "is_active", "groups", "user_permissions" | |||||
| ) | |||||
| } | |||||
| ), | |||||
| ("Personal", {"fields": ("nombre",)}), | |||||
| ("Varios", {"fields": ("foto",)}), | |||||
| ) | |||||
| search_fields = ("email",) | |||||
| ordering = ("email",) | |||||
| admin.site.register(ReyMotaUser, ReyMotaUserAdmin) | |||||
| @ -0,0 +1,6 @@ | |||||
| from django.apps import AppConfig | |||||
| class RepostajesConfig(AppConfig): | |||||
| default_auto_field = 'django.db.models.BigAutoField' | |||||
| name = 'repostajes' | |||||
| @ -0,0 +1,47 @@ | |||||
| from django import forms | |||||
| from django.contrib.auth.forms import UserCreationForm, UserChangeForm | |||||
| from .models import Vehiculo, Repostaje, ReyMotaUser | |||||
| class VehiculoForm(forms.ModelForm): | |||||
| class Meta: | |||||
| model = Vehiculo | |||||
| fields = ['marca', 'modelo', 'matricula', 'foto'] | |||||
| marca = forms.CharField( | |||||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||||
| modelo = forms.CharField( | |||||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||||
| matricula = forms.CharField( | |||||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||||
| class RepostajeForm(forms.ModelForm): | |||||
| class Meta: | |||||
| model = Repostaje | |||||
| fields = ['fecha', 'kms', 'litros', 'descuento'] | |||||
| fecha = forms.DateFieldField( | |||||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||||
| kms = forms.DecimalField( | |||||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||||
| Vehiculo = forms.ModelChoiceField( | |||||
| queryset=Vehiculo.objects.all(), | |||||
| widget=forms.Select(attrs={'class': 'form-control'})) | |||||
| class ReyMotaUserCreationForm(UserCreationForm): | |||||
| class Meta: | |||||
| model = ReyMotaUser | |||||
| fields = ("email", "nombre", "foto") | |||||
| labels = {'email': 'Dirección de correo'} | |||||
| class ReyMotaUserChangeForm(UserChangeForm): | |||||
| class Meta: | |||||
| model = ReyMotaUser | |||||
| fields = ("email", "foto") | |||||
| @ -0,0 +1,34 @@ | |||||
| from django.contrib.auth.base_user import BaseUserManager | |||||
| from django.utils.translation import gettext_lazy as _ | |||||
| class ReyMotaUserManager(BaseUserManager): | |||||
| """ | |||||
| ReyMota user model manager where email is the unique identifiers | |||||
| for authentication instead of usernames. | |||||
| """ | |||||
| def create_user(self, email, password, **extra_fields): | |||||
| """ | |||||
| Create and save a user with the given email and password. | |||||
| """ | |||||
| if not email: | |||||
| raise ValueError(_("The Email must be set")) | |||||
| email = self.normalize_email(email) | |||||
| user = self.model(email=email, **extra_fields) | |||||
| user.set_password(password) | |||||
| user.save() | |||||
| return user | |||||
| def create_superuser(self, email, password, **extra_fields): | |||||
| """ | |||||
| Create and save a SuperUser with the given email and password. | |||||
| """ | |||||
| extra_fields.setdefault("is_staff", True) | |||||
| extra_fields.setdefault("is_superuser", True) | |||||
| extra_fields.setdefault("is_active", True) | |||||
| if extra_fields.get("is_staff") is not True: | |||||
| raise ValueError(_("Superuser must have is_staff=True.")) | |||||
| if extra_fields.get("is_superuser") is not True: | |||||
| raise ValueError(_("Superuser must have is_superuser=True.")) | |||||
| return self.create_user(email, password, **extra_fields) | |||||
| @ -0,0 +1,55 @@ | |||||
| from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin | |||||
| from django.db import models | |||||
| import datetime | |||||
| from django.core.validators import MaxValueValidator, MinValueValidator | |||||
| from django.utils.translation import gettext_lazy as _ | |||||
| from .managers import ReyMotaUserManager | |||||
| def current_year(): | |||||
| return datetime.date.today().year | |||||
| def max_value_current_year(value): | |||||
| return MaxValueValidator(current_year())(value) | |||||
| class Vehiculo(models.Model): | |||||
| marca = models.CharField(max_length=200) | |||||
| modelo = models.CharField(max_length=200) | |||||
| matricula = models.CharField(max_length=200) | |||||
| foto = models.ImageField(upload_to='vehiculos/', blank=True, null=True) # Nuevo campo | |||||
| def __str__(self): | |||||
| return self.marca | |||||
| class Repostaje(models.Model): | |||||
| vehiculo = models.ForeignKey(Vehiculo, on_delete=models.CASCADE) | |||||
| fecha = models.DateField() | |||||
| kms = models.DecimalField(blank=True, null=True) | |||||
| litros = models.DecimalField(blank=True, null=True) | |||||
| descuento = models.DecimalField(blank=True, null=True) | |||||
| precioxlitro = models.DecimalField(blank=True, null=True) | |||||
| kmsrecorridos = models.DecimalField(blank=True, null=True) | |||||
| consumo = models.DecimalField(blank=True, null=True) | |||||
| def __str__(self): | |||||
| return self.fecha | |||||
| class ReyMotaUser(AbstractBaseUser, PermissionsMixin): | |||||
| email = models.EmailField(_("email address"), unique=True) | |||||
| foto = models.ImageField(upload_to="profile_images", blank=True) | |||||
| is_staff = models.BooleanField(default=False) | |||||
| is_active = models.BooleanField(default=True) | |||||
| nombre = models.CharField(max_length=200, blank=True, null=True) | |||||
| USERNAME_FIELD = "email" | |||||
| REQUIRED_FIELDS = [] | |||||
| objects = ReyMotaUserManager() | |||||
| def __str__(self): | |||||
| return self.email | |||||
| @ -0,0 +1,21 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <svg width="215px" height="215px" viewBox="0 0 215 215" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||||
| <!-- Generator: Sketch 52.6 (67491) - http://www.bohemiancoding.com/sketch --> | |||||
| <title>portal-logo</title> | |||||
| <desc>Created with Sketch.</desc> | |||||
| <defs> | |||||
| <path d="M51.165,8.742 C54.505,12.619 56.876,17.365 57.892,22.588 C60.148,17.225 65.452,13.46 71.636,13.46 C79.867,13.46 86.541,20.134 86.541,28.365 C86.541,36.597 79.867,43.269 71.636,43.269 C63.404,43.269 56.728,36.597 56.728,28.365 C56.728,12.7 44.03,0 28.365,0 C12.7,0 0,12.7 0,28.365 C0,44.031 12.7,56.731 28.365,56.731 C36.419,56.731 43.695,53.393 48.858,48.003 C45.501,44.117 43.128,39.383 42.108,34.14 C39.852,39.504 34.548,43.269 28.365,43.269 C20.133,43.269 13.46,36.597 13.46,28.365 C13.46,20.134 20.133,13.46 28.365,13.46 C36.966,13.46 43.27,20.577 43.27,28.365 C43.27,44.031 55.97,56.731 71.636,56.731 C87.3,56.731 100,44.031 100,28.365 C100,12.7 87.3,0 71.636,0 C63.589,0 56.327,3.358 51.165,8.742 Z" id="path-1"></path> | |||||
| </defs> | |||||
| <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||||
| <g id="portal-logo"> | |||||
| <circle id="Oval" fill="#51B37F" fill-rule="nonzero" cx="107.5" cy="107.5" r="107.5"></circle> | |||||
| <g id="logo" transform="translate(58.000000, 79.000000)"> | |||||
| <mask id="mask-2" fill="white"> | |||||
| <use xlink:href="#path-1"></use> | |||||
| </mask> | |||||
| <g id="Clip-2"></g> | |||||
| <polygon id="Fill-1" fill="#FFFFFE" mask="url(#mask-2)" points="-5 61.73 105 61.73 105 -5 -5 -5"></polygon> | |||||
| </g> | |||||
| </g> | |||||
| </g> | |||||
| </svg> | |||||
| @ -0,0 +1,21 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <svg width="215px" height="215px" viewBox="0 0 215 215" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||||
| <!-- Generator: Sketch 52.6 (67491) - http://www.bohemiancoding.com/sketch --> | |||||
| <title>portal-logo</title> | |||||
| <desc>Created with Sketch.</desc> | |||||
| <defs> | |||||
| <path d="M51.165,8.742 C54.505,12.619 56.876,17.365 57.892,22.588 C60.148,17.225 65.452,13.46 71.636,13.46 C79.867,13.46 86.541,20.134 86.541,28.365 C86.541,36.597 79.867,43.269 71.636,43.269 C63.404,43.269 56.728,36.597 56.728,28.365 C56.728,12.7 44.03,0 28.365,0 C12.7,0 0,12.7 0,28.365 C0,44.031 12.7,56.731 28.365,56.731 C36.419,56.731 43.695,53.393 48.858,48.003 C45.501,44.117 43.128,39.383 42.108,34.14 C39.852,39.504 34.548,43.269 28.365,43.269 C20.133,43.269 13.46,36.597 13.46,28.365 C13.46,20.134 20.133,13.46 28.365,13.46 C36.966,13.46 43.27,20.577 43.27,28.365 C43.27,44.031 55.97,56.731 71.636,56.731 C87.3,56.731 100,44.031 100,28.365 C100,12.7 87.3,0 71.636,0 C63.589,0 56.327,3.358 51.165,8.742 Z" id="path-1"></path> | |||||
| </defs> | |||||
| <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||||
| <g id="portal-logo"> | |||||
| <circle id="Oval" fill="#09B6CA" fill-rule="nonzero" cx="107.5" cy="107.5" r="107.5"></circle> | |||||
| <g id="logo" transform="translate(58.000000, 79.000000)"> | |||||
| <mask id="mask-2" fill="white"> | |||||
| <use xlink:href="#path-1"></use> | |||||
| </mask> | |||||
| <g id="Clip-2"></g> | |||||
| <polygon id="Fill-1" fill="#FFFFFE" mask="url(#mask-2)" points="-5 61.73 105 61.73 105 -5 -5 -5"></polygon> | |||||
| </g> | |||||
| </g> | |||||
| </g> | |||||
| </svg> | |||||
| @ -0,0 +1,18 @@ | |||||
| <svg width="400" height="400" xmlns="http://www.w3.org/2000/svg"> | |||||
| <!-- Fondo --> | |||||
| <rect width="400" height="400" fill="#ffffff" /> | |||||
| <!-- Corona --> | |||||
| <g transform="translate(100, 100) scale(2)"> | |||||
| <polygon points="50,150 75,50 100,150" fill="#FFD700" /> | |||||
| <polygon points="0,150 50,0 100,150" fill="#FFD700" /> | |||||
| <polygon points="100,150 125,50 150,150" fill="#FFD700" /> | |||||
| </g> | |||||
| <!-- Letra R --> | |||||
| <!-- | |||||
| <text x="100" y="360" font-family="Arial, sans-serif" font-size="400" fill="#000000" font-weight="bold">R</text> | |||||
| --> | |||||
| <text x="100" y="360" font-family="Open Sans" font-size="400" fill="#000000" font-weight="bold">R</text> | |||||
| </svg> | |||||
| @ -0,0 +1,96 @@ | |||||
| 'use strict'; | |||||
| /* ===== Enable Bootstrap Popover (on element ====== */ | |||||
| const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]') | |||||
| const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)) | |||||
| /* ==== Enable Bootstrap Alert ====== */ | |||||
| //var alertList = document.querySelectorAll('.alert') | |||||
| //alertList.forEach(function (alert) { | |||||
| // new bootstrap.Alert(alert) | |||||
| //}); | |||||
| const alertList = document.querySelectorAll('.alert') | |||||
| const alerts = [...alertList].map(element => new bootstrap.Alert(element)) | |||||
| /* ===== Responsive Sidepanel ====== */ | |||||
| const sidePanelToggler = document.getElementById('sidepanel-toggler'); | |||||
| const sidePanel = document.getElementById('app-sidepanel'); | |||||
| const sidePanelDrop = document.getElementById('sidepanel-drop'); | |||||
| const sidePanelClose = document.getElementById('sidepanel-close'); | |||||
| window.addEventListener('load', function(){ | |||||
| responsiveSidePanel(); | |||||
| }); | |||||
| window.addEventListener('resize', function(){ | |||||
| responsiveSidePanel(); | |||||
| }); | |||||
| function responsiveSidePanel() { | |||||
| let w = window.innerWidth; | |||||
| if(w >= 1200) { | |||||
| // if larger | |||||
| //console.log('larger'); | |||||
| sidePanel.classList.remove('sidepanel-hidden'); | |||||
| sidePanel.classList.add('sidepanel-visible'); | |||||
| } else { | |||||
| // if smaller | |||||
| //console.log('smaller'); | |||||
| sidePanel.classList.remove('sidepanel-visible'); | |||||
| sidePanel.classList.add('sidepanel-hidden'); | |||||
| } | |||||
| }; | |||||
| sidePanelToggler.addEventListener('click', () => { | |||||
| if (sidePanel.classList.contains('sidepanel-visible')) { | |||||
| console.log('visible'); | |||||
| sidePanel.classList.remove('sidepanel-visible'); | |||||
| sidePanel.classList.add('sidepanel-hidden'); | |||||
| } else { | |||||
| console.log('hidden'); | |||||
| sidePanel.classList.remove('sidepanel-hidden'); | |||||
| sidePanel.classList.add('sidepanel-visible'); | |||||
| } | |||||
| }); | |||||
| sidePanelClose.addEventListener('click', (e) => { | |||||
| e.preventDefault(); | |||||
| sidePanelToggler.click(); | |||||
| }); | |||||
| sidePanelDrop.addEventListener('click', (e) => { | |||||
| sidePanelToggler.click(); | |||||
| }); | |||||
| /* ====== Mobile search ======= */ | |||||
| const searchMobileTrigger = document.querySelector('.search-mobile-trigger'); | |||||
| const searchBox = document.querySelector('.app-search-box'); | |||||
| searchMobileTrigger.addEventListener('click', () => { | |||||
| searchBox.classList.toggle('is-visible'); | |||||
| let searchMobileTriggerIcon = document.querySelector('.search-mobile-trigger-icon'); | |||||
| if(searchMobileTriggerIcon.classList.contains('fa-magnifying-glass')) { | |||||
| searchMobileTriggerIcon.classList.remove('fa-magnifying-glass'); | |||||
| searchMobileTriggerIcon.classList.add('fa-xmark'); | |||||
| } else { | |||||
| searchMobileTriggerIcon.classList.remove('fa-xmark'); | |||||
| searchMobileTriggerIcon.classList.add('fa-magnifying-glass'); | |||||
| } | |||||
| }); | |||||
| @ -0,0 +1,366 @@ | |||||
| 'use strict'; | |||||
| /* Chart.js docs: https://www.chartjs.org/ */ | |||||
| window.chartColors = { | |||||
| green: '#75c181', // rgba(117,193,129, 1) | |||||
| blue: '#5b99ea', // rgba(91,153,234, 1) | |||||
| gray: '#a9b5c9', | |||||
| text: '#252930', | |||||
| border: '#e7e9ed' | |||||
| }; | |||||
| /* Random number generator for demo purpose */ | |||||
| var randomDataPoint = function(){ return Math.round(Math.random()*100)}; | |||||
| //Area line Chart Demo | |||||
| var lineChartConfig = { | |||||
| type: 'line', | |||||
| data: { | |||||
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'], | |||||
| datasets: [{ | |||||
| label: 'Dataset', | |||||
| backgroundColor: "rgba(117,193,129,0.2)", | |||||
| borderColor: "rgba(117,193,129, 0.8)", | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint() | |||||
| ], | |||||
| }] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| legend: { | |||||
| display: true, | |||||
| position: 'bottom', | |||||
| align: 'end', | |||||
| }, | |||||
| tooltips: { | |||||
| mode: 'index', | |||||
| intersect: false, | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| callbacks: { | |||||
| label: function(tooltipItem, data) { | |||||
| return tooltipItem.value + '%'; | |||||
| } | |||||
| }, | |||||
| }, | |||||
| hover: { | |||||
| mode: 'nearest', | |||||
| intersect: true | |||||
| }, | |||||
| scales: { | |||||
| xAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| scaleLabel: { | |||||
| display: false, | |||||
| } | |||||
| }], | |||||
| yAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| scaleLabel: { | |||||
| display: false, | |||||
| }, | |||||
| ticks: { | |||||
| beginAtZero: true, | |||||
| userCallback: function(value, index, values) { | |||||
| return value.toLocaleString() + '%'; | |||||
| } | |||||
| }, | |||||
| }] | |||||
| } | |||||
| } | |||||
| }; | |||||
| //Bar Chart Demo | |||||
| var barChartConfig = { | |||||
| type: 'bar', | |||||
| data: { | |||||
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'], | |||||
| datasets: [{ | |||||
| label: 'Dataset 1', | |||||
| backgroundColor: "rgba(117,193,129,0.8)", | |||||
| hoverBackgroundColor: "rgba(117,193,129,1)", | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint() | |||||
| ] | |||||
| }, | |||||
| { | |||||
| label: 'Dataset 2', | |||||
| backgroundColor: "rgba(91,153,234,0.8)", | |||||
| hoverBackgroundColor: "rgba(91,153,234,1)", | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint() | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| legend: { | |||||
| position: 'bottom', | |||||
| align: 'end', | |||||
| }, | |||||
| tooltips: { | |||||
| mode: 'index', | |||||
| intersect: false, | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| callbacks: { | |||||
| label: function(tooltipItem, data) { | |||||
| return tooltipItem.value + '%'; | |||||
| } | |||||
| }, | |||||
| }, | |||||
| scales: { | |||||
| xAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| }], | |||||
| yAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.borders, | |||||
| }, | |||||
| ticks: { | |||||
| beginAtZero: true, | |||||
| userCallback: function(value, index, values) { | |||||
| return value + '%'; | |||||
| } | |||||
| }, | |||||
| }] | |||||
| } | |||||
| } | |||||
| } | |||||
| // Pie Chart Demo | |||||
| var pieChartConfig = { | |||||
| type: 'pie', | |||||
| data: { | |||||
| datasets: [{ | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| ], | |||||
| backgroundColor: [ | |||||
| window.chartColors.green, | |||||
| window.chartColors.blue, | |||||
| window.chartColors.gray, | |||||
| ], | |||||
| label: 'Dataset 1' | |||||
| }], | |||||
| labels: [ | |||||
| 'Green', | |||||
| 'Blue', | |||||
| 'Gray', | |||||
| ] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| legend: { | |||||
| display: true, | |||||
| position: 'bottom', | |||||
| align: 'center', | |||||
| }, | |||||
| tooltips: { | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| /* Display % in tooltip - https://stackoverflow.com/questions/37257034/chart-js-2-0-doughnut-tooltip-percentages */ | |||||
| callbacks: { | |||||
| label: function(tooltipItem, data) { | |||||
| //get the concerned dataset | |||||
| var dataset = data.datasets[tooltipItem.datasetIndex]; | |||||
| //calculate the total of this data set | |||||
| var total = dataset.data.reduce(function(previousValue, currentValue, currentIndex, array) { | |||||
| return previousValue + currentValue; | |||||
| }); | |||||
| //get the current items value | |||||
| var currentValue = dataset.data[tooltipItem.index]; | |||||
| //calculate the precentage based on the total and current item, also this does a rough rounding to give a whole number | |||||
| var percentage = Math.floor(((currentValue/total) * 100)+0.5); | |||||
| return percentage + "%"; | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| } | |||||
| }; | |||||
| // Doughnut Chart Demo | |||||
| var doughnutChartConfig = { | |||||
| type: 'doughnut', | |||||
| data: { | |||||
| datasets: [{ | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| ], | |||||
| backgroundColor: [ | |||||
| window.chartColors.green, | |||||
| window.chartColors.blue, | |||||
| window.chartColors.gray, | |||||
| ], | |||||
| label: 'Dataset 1' | |||||
| }], | |||||
| labels: [ | |||||
| 'Green', | |||||
| 'Blue', | |||||
| 'Gray', | |||||
| ] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| legend: { | |||||
| display: true, | |||||
| position: 'bottom', | |||||
| align: 'center', | |||||
| }, | |||||
| tooltips: { | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| animation: { | |||||
| animateScale: true, | |||||
| animateRotate: true | |||||
| }, | |||||
| /* Display % in tooltip - https://stackoverflow.com/questions/37257034/chart-js-2-0-doughnut-tooltip-percentages */ | |||||
| callbacks: { | |||||
| label: function(tooltipItem, data) { | |||||
| //get the concerned dataset | |||||
| var dataset = data.datasets[tooltipItem.datasetIndex]; | |||||
| //calculate the total of this data set | |||||
| var total = dataset.data.reduce(function(previousValue, currentValue, currentIndex, array) { | |||||
| return previousValue + currentValue; | |||||
| }); | |||||
| //get the current items value | |||||
| var currentValue = dataset.data[tooltipItem.index]; | |||||
| //calculate the precentage based on the total and current item, also this does a rough rounding to give a whole number | |||||
| var percentage = Math.floor(((currentValue/total) * 100)+0.5); | |||||
| return percentage + "%"; | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| } | |||||
| }; | |||||
| // Generate charts on load | |||||
| window.addEventListener('load', function(){ | |||||
| var lineChart = document.getElementById('chart-line').getContext('2d'); | |||||
| window.myLine = new Chart(lineChart, lineChartConfig); | |||||
| var barChart = document.getElementById('chart-bar').getContext('2d'); | |||||
| window.myBar = new Chart(barChart, barChartConfig); | |||||
| var pieChart = document.getElementById('chart-pie').getContext('2d'); | |||||
| window.myPie = new Chart(pieChart, pieChartConfig); | |||||
| var doughnutChart = document.getElementById('chart-doughnut').getContext('2d'); | |||||
| window.myDoughnut = new Chart(doughnutChart, doughnutChartConfig); | |||||
| }); | |||||
| @ -0,0 +1,224 @@ | |||||
| 'use strict'; | |||||
| /* Chart.js docs: https://www.chartjs.org/ */ | |||||
| window.chartColors = { | |||||
| green: '#75c181', | |||||
| gray: '#a9b5c9', | |||||
| text: '#252930', | |||||
| border: '#e7e9ed' | |||||
| }; | |||||
| /* Random number generator for demo purpose */ | |||||
| var randomDataPoint = function(){ return Math.round(Math.random()*10000)}; | |||||
| //Chart.js Line Chart Example | |||||
| var lineChartConfig = { | |||||
| type: 'line', | |||||
| data: { | |||||
| labels: ['Day 1', 'Day 2', 'Day 3', 'Day 4', 'Day 5', 'Day 6', 'Day 7'], | |||||
| datasets: [{ | |||||
| label: 'Current week', | |||||
| fill: false, | |||||
| backgroundColor: window.chartColors.green, | |||||
| borderColor: window.chartColors.green, | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint() | |||||
| ], | |||||
| }, { | |||||
| label: 'Previous week', | |||||
| borderDash: [3, 5], | |||||
| backgroundColor: window.chartColors.gray, | |||||
| borderColor: window.chartColors.gray, | |||||
| data: [ | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint(), | |||||
| randomDataPoint() | |||||
| ], | |||||
| fill: false, | |||||
| }] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| aspectRatio: 1.5, | |||||
| legend: { | |||||
| display: true, | |||||
| position: 'bottom', | |||||
| align: 'end', | |||||
| }, | |||||
| title: { | |||||
| display: true, | |||||
| text: 'Chart.js Line Chart Example', | |||||
| }, | |||||
| tooltips: { | |||||
| mode: 'index', | |||||
| intersect: false, | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| callbacks: { | |||||
| //Ref: https://stackoverflow.com/questions/38800226/chart-js-add-commas-to-tooltip-and-y-axis | |||||
| label: function(tooltipItem, data) { | |||||
| if (parseInt(tooltipItem.value) >= 1000) { | |||||
| return "$" + tooltipItem.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |||||
| } else { | |||||
| return '$' + tooltipItem.value; | |||||
| } | |||||
| } | |||||
| }, | |||||
| }, | |||||
| hover: { | |||||
| mode: 'nearest', | |||||
| intersect: true | |||||
| }, | |||||
| scales: { | |||||
| xAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| scaleLabel: { | |||||
| display: false, | |||||
| } | |||||
| }], | |||||
| yAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| scaleLabel: { | |||||
| display: false, | |||||
| }, | |||||
| ticks: { | |||||
| beginAtZero: true, | |||||
| userCallback: function(value, index, values) { | |||||
| return '$' + value.toLocaleString(); //Ref: https://stackoverflow.com/questions/38800226/chart-js-add-commas-to-tooltip-and-y-axis | |||||
| } | |||||
| }, | |||||
| }] | |||||
| } | |||||
| } | |||||
| }; | |||||
| // Chart.js Bar Chart Example | |||||
| var barChartConfig = { | |||||
| type: 'bar', | |||||
| data: { | |||||
| labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], | |||||
| datasets: [{ | |||||
| label: 'Orders', | |||||
| backgroundColor: window.chartColors.green, | |||||
| borderColor: window.chartColors.green, | |||||
| borderWidth: 1, | |||||
| maxBarThickness: 16, | |||||
| data: [ | |||||
| 23, | |||||
| 45, | |||||
| 76, | |||||
| 75, | |||||
| 62, | |||||
| 37, | |||||
| 83 | |||||
| ] | |||||
| }] | |||||
| }, | |||||
| options: { | |||||
| responsive: true, | |||||
| aspectRatio: 1.5, | |||||
| legend: { | |||||
| position: 'bottom', | |||||
| align: 'end', | |||||
| }, | |||||
| title: { | |||||
| display: true, | |||||
| text: 'Chart.js Bar Chart Example' | |||||
| }, | |||||
| tooltips: { | |||||
| mode: 'index', | |||||
| intersect: false, | |||||
| titleMarginBottom: 10, | |||||
| bodySpacing: 10, | |||||
| xPadding: 16, | |||||
| yPadding: 16, | |||||
| borderColor: window.chartColors.border, | |||||
| borderWidth: 1, | |||||
| backgroundColor: '#fff', | |||||
| bodyFontColor: window.chartColors.text, | |||||
| titleFontColor: window.chartColors.text, | |||||
| }, | |||||
| scales: { | |||||
| xAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.border, | |||||
| }, | |||||
| }], | |||||
| yAxes: [{ | |||||
| display: true, | |||||
| gridLines: { | |||||
| drawBorder: false, | |||||
| color: window.chartColors.borders, | |||||
| }, | |||||
| }] | |||||
| } | |||||
| } | |||||
| } | |||||
| // Generate charts on load | |||||
| window.addEventListener('load', function(){ | |||||
| var lineChart = document.getElementById('canvas-linechart').getContext('2d'); | |||||
| window.myLine = new Chart(lineChart, lineChartConfig); | |||||
| var barChart = document.getElementById('canvas-barchart').getContext('2d'); | |||||
| window.myBar = new Chart(barChart, barChartConfig); | |||||
| }); | |||||
| @ -0,0 +1,593 @@ | |||||
| /*! | |||||
| * Bootstrap Reboot v5.3.0 (https://getbootstrap.com/) | |||||
| * Copyright 2011-2023 The Bootstrap Authors | |||||
| * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) | |||||
| */ | |||||
| :root, | |||||
| [data-bs-theme=light] { | |||||
| --bs-blue: #0d6efd; | |||||
| --bs-indigo: #6610f2; | |||||
| --bs-purple: #6f42c1; | |||||
| --bs-pink: #d63384; | |||||
| --bs-red: #dc3545; | |||||
| --bs-orange: #fd7e14; | |||||
| --bs-yellow: #ffc107; | |||||
| --bs-green: #198754; | |||||
| --bs-teal: #20c997; | |||||
| --bs-cyan: #0dcaf0; | |||||
| --bs-black: #000; | |||||
| --bs-white: #fff; | |||||
| --bs-gray: #6c757d; | |||||
| --bs-gray-dark: #343a40; | |||||
| --bs-gray-100: #f8f9fa; | |||||
| --bs-gray-200: #e9ecef; | |||||
| --bs-gray-300: #dee2e6; | |||||
| --bs-gray-400: #ced4da; | |||||
| --bs-gray-500: #adb5bd; | |||||
| --bs-gray-600: #6c757d; | |||||
| --bs-gray-700: #495057; | |||||
| --bs-gray-800: #343a40; | |||||
| --bs-gray-900: #212529; | |||||
| --bs-primary: #0d6efd; | |||||
| --bs-secondary: #6c757d; | |||||
| --bs-success: #198754; | |||||
| --bs-info: #0dcaf0; | |||||
| --bs-warning: #ffc107; | |||||
| --bs-danger: #dc3545; | |||||
| --bs-light: #f8f9fa; | |||||
| --bs-dark: #212529; | |||||
| --bs-primary-rgb: 13, 110, 253; | |||||
| --bs-secondary-rgb: 108, 117, 125; | |||||
| --bs-success-rgb: 25, 135, 84; | |||||
| --bs-info-rgb: 13, 202, 240; | |||||
| --bs-warning-rgb: 255, 193, 7; | |||||
| --bs-danger-rgb: 220, 53, 69; | |||||
| --bs-light-rgb: 248, 249, 250; | |||||
| --bs-dark-rgb: 33, 37, 41; | |||||
| --bs-primary-text-emphasis: #052c65; | |||||
| --bs-secondary-text-emphasis: #2b2f32; | |||||
| --bs-success-text-emphasis: #0a3622; | |||||
| --bs-info-text-emphasis: #055160; | |||||
| --bs-warning-text-emphasis: #664d03; | |||||
| --bs-danger-text-emphasis: #58151c; | |||||
| --bs-light-text-emphasis: #495057; | |||||
| --bs-dark-text-emphasis: #495057; | |||||
| --bs-primary-bg-subtle: #cfe2ff; | |||||
| --bs-secondary-bg-subtle: #e2e3e5; | |||||
| --bs-success-bg-subtle: #d1e7dd; | |||||
| --bs-info-bg-subtle: #cff4fc; | |||||
| --bs-warning-bg-subtle: #fff3cd; | |||||
| --bs-danger-bg-subtle: #f8d7da; | |||||
| --bs-light-bg-subtle: #fcfcfd; | |||||
| --bs-dark-bg-subtle: #ced4da; | |||||
| --bs-primary-border-subtle: #9ec5fe; | |||||
| --bs-secondary-border-subtle: #c4c8cb; | |||||
| --bs-success-border-subtle: #a3cfbb; | |||||
| --bs-info-border-subtle: #9eeaf9; | |||||
| --bs-warning-border-subtle: #ffe69c; | |||||
| --bs-danger-border-subtle: #f1aeb5; | |||||
| --bs-light-border-subtle: #e9ecef; | |||||
| --bs-dark-border-subtle: #adb5bd; | |||||
| --bs-white-rgb: 255, 255, 255; | |||||
| --bs-black-rgb: 0, 0, 0; | |||||
| --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | |||||
| --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |||||
| --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); | |||||
| --bs-body-font-family: var(--bs-font-sans-serif); | |||||
| --bs-body-font-size: 1rem; | |||||
| --bs-body-font-weight: 400; | |||||
| --bs-body-line-height: 1.5; | |||||
| --bs-body-color: #212529; | |||||
| --bs-body-color-rgb: 33, 37, 41; | |||||
| --bs-body-bg: #fff; | |||||
| --bs-body-bg-rgb: 255, 255, 255; | |||||
| --bs-emphasis-color: #000; | |||||
| --bs-emphasis-color-rgb: 0, 0, 0; | |||||
| --bs-secondary-color: rgba(33, 37, 41, 0.75); | |||||
| --bs-secondary-color-rgb: 33, 37, 41; | |||||
| --bs-secondary-bg: #e9ecef; | |||||
| --bs-secondary-bg-rgb: 233, 236, 239; | |||||
| --bs-tertiary-color: rgba(33, 37, 41, 0.5); | |||||
| --bs-tertiary-color-rgb: 33, 37, 41; | |||||
| --bs-tertiary-bg: #f8f9fa; | |||||
| --bs-tertiary-bg-rgb: 248, 249, 250; | |||||
| --bs-heading-color: inherit; | |||||
| --bs-link-color: #0d6efd; | |||||
| --bs-link-color-rgb: 13, 110, 253; | |||||
| --bs-link-decoration: underline; | |||||
| --bs-link-hover-color: #0a58ca; | |||||
| --bs-link-hover-color-rgb: 10, 88, 202; | |||||
| --bs-code-color: #d63384; | |||||
| --bs-highlight-bg: #fff3cd; | |||||
| --bs-border-width: 1px; | |||||
| --bs-border-style: solid; | |||||
| --bs-border-color: #dee2e6; | |||||
| --bs-border-color-translucent: rgba(0, 0, 0, 0.175); | |||||
| --bs-border-radius: 0.375rem; | |||||
| --bs-border-radius-sm: 0.25rem; | |||||
| --bs-border-radius-lg: 0.5rem; | |||||
| --bs-border-radius-xl: 1rem; | |||||
| --bs-border-radius-xxl: 2rem; | |||||
| --bs-border-radius-2xl: var(--bs-border-radius-xxl); | |||||
| --bs-border-radius-pill: 50rem; | |||||
| --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); | |||||
| --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); | |||||
| --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); | |||||
| --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); | |||||
| --bs-focus-ring-width: 0.25rem; | |||||
| --bs-focus-ring-opacity: 0.25; | |||||
| --bs-focus-ring-color: rgba(13, 110, 253, 0.25); | |||||
| --bs-form-valid-color: #198754; | |||||
| --bs-form-valid-border-color: #198754; | |||||
| --bs-form-invalid-color: #dc3545; | |||||
| --bs-form-invalid-border-color: #dc3545; | |||||
| } | |||||
| [data-bs-theme=dark] { | |||||
| color-scheme: dark; | |||||
| --bs-body-color: #adb5bd; | |||||
| --bs-body-color-rgb: 173, 181, 189; | |||||
| --bs-body-bg: #212529; | |||||
| --bs-body-bg-rgb: 33, 37, 41; | |||||
| --bs-emphasis-color: #fff; | |||||
| --bs-emphasis-color-rgb: 255, 255, 255; | |||||
| --bs-secondary-color: rgba(173, 181, 189, 0.75); | |||||
| --bs-secondary-color-rgb: 173, 181, 189; | |||||
| --bs-secondary-bg: #343a40; | |||||
| --bs-secondary-bg-rgb: 52, 58, 64; | |||||
| --bs-tertiary-color: rgba(173, 181, 189, 0.5); | |||||
| --bs-tertiary-color-rgb: 173, 181, 189; | |||||
| --bs-tertiary-bg: #2b3035; | |||||
| --bs-tertiary-bg-rgb: 43, 48, 53; | |||||
| --bs-primary-text-emphasis: #6ea8fe; | |||||
| --bs-secondary-text-emphasis: #a7acb1; | |||||
| --bs-success-text-emphasis: #75b798; | |||||
| --bs-info-text-emphasis: #6edff6; | |||||
| --bs-warning-text-emphasis: #ffda6a; | |||||
| --bs-danger-text-emphasis: #ea868f; | |||||
| --bs-light-text-emphasis: #f8f9fa; | |||||
| --bs-dark-text-emphasis: #dee2e6; | |||||
| --bs-primary-bg-subtle: #031633; | |||||
| --bs-secondary-bg-subtle: #161719; | |||||
| --bs-success-bg-subtle: #051b11; | |||||
| --bs-info-bg-subtle: #032830; | |||||
| --bs-warning-bg-subtle: #332701; | |||||
| --bs-danger-bg-subtle: #2c0b0e; | |||||
| --bs-light-bg-subtle: #343a40; | |||||
| --bs-dark-bg-subtle: #1a1d20; | |||||
| --bs-primary-border-subtle: #084298; | |||||
| --bs-secondary-border-subtle: #41464b; | |||||
| --bs-success-border-subtle: #0f5132; | |||||
| --bs-info-border-subtle: #087990; | |||||
| --bs-warning-border-subtle: #997404; | |||||
| --bs-danger-border-subtle: #842029; | |||||
| --bs-light-border-subtle: #495057; | |||||
| --bs-dark-border-subtle: #343a40; | |||||
| --bs-heading-color: inherit; | |||||
| --bs-link-color: #6ea8fe; | |||||
| --bs-link-hover-color: #8bb9fe; | |||||
| --bs-link-color-rgb: 110, 168, 254; | |||||
| --bs-link-hover-color-rgb: 139, 185, 254; | |||||
| --bs-code-color: #e685b5; | |||||
| --bs-border-color: #495057; | |||||
| --bs-border-color-translucent: rgba(255, 255, 255, 0.15); | |||||
| --bs-form-valid-color: #75b798; | |||||
| --bs-form-valid-border-color: #75b798; | |||||
| --bs-form-invalid-color: #ea868f; | |||||
| --bs-form-invalid-border-color: #ea868f; | |||||
| } | |||||
| *, | |||||
| *::before, | |||||
| *::after { | |||||
| box-sizing: border-box; | |||||
| } | |||||
| @media (prefers-reduced-motion: no-preference) { | |||||
| :root { | |||||
| scroll-behavior: smooth; | |||||
| } | |||||
| } | |||||
| body { | |||||
| margin: 0; | |||||
| font-family: var(--bs-body-font-family); | |||||
| font-size: var(--bs-body-font-size); | |||||
| font-weight: var(--bs-body-font-weight); | |||||
| line-height: var(--bs-body-line-height); | |||||
| color: var(--bs-body-color); | |||||
| text-align: var(--bs-body-text-align); | |||||
| background-color: var(--bs-body-bg); | |||||
| -webkit-text-size-adjust: 100%; | |||||
| -webkit-tap-highlight-color: rgba(0, 0, 0, 0); | |||||
| } | |||||
| hr { | |||||
| margin: 1rem 0; | |||||
| color: inherit; | |||||
| border: 0; | |||||
| border-top: var(--bs-border-width) solid; | |||||
| opacity: 0.25; | |||||
| } | |||||
| h6, h5, h4, h3, h2, h1 { | |||||
| margin-top: 0; | |||||
| margin-bottom: 0.5rem; | |||||
| font-weight: 500; | |||||
| line-height: 1.2; | |||||
| color: var(--bs-heading-color); | |||||
| } | |||||
| h1 { | |||||
| font-size: calc(1.375rem + 1.5vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h1 { | |||||
| font-size: 2.5rem; | |||||
| } | |||||
| } | |||||
| h2 { | |||||
| font-size: calc(1.325rem + 0.9vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h2 { | |||||
| font-size: 2rem; | |||||
| } | |||||
| } | |||||
| h3 { | |||||
| font-size: calc(1.3rem + 0.6vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h3 { | |||||
| font-size: 1.75rem; | |||||
| } | |||||
| } | |||||
| h4 { | |||||
| font-size: calc(1.275rem + 0.3vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h4 { | |||||
| font-size: 1.5rem; | |||||
| } | |||||
| } | |||||
| h5 { | |||||
| font-size: 1.25rem; | |||||
| } | |||||
| h6 { | |||||
| font-size: 1rem; | |||||
| } | |||||
| p { | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| } | |||||
| abbr[title] { | |||||
| -webkit-text-decoration: underline dotted; | |||||
| text-decoration: underline dotted; | |||||
| cursor: help; | |||||
| -webkit-text-decoration-skip-ink: none; | |||||
| text-decoration-skip-ink: none; | |||||
| } | |||||
| address { | |||||
| margin-bottom: 1rem; | |||||
| font-style: normal; | |||||
| line-height: inherit; | |||||
| } | |||||
| ol, | |||||
| ul { | |||||
| padding-left: 2rem; | |||||
| } | |||||
| ol, | |||||
| ul, | |||||
| dl { | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| } | |||||
| ol ol, | |||||
| ul ul, | |||||
| ol ul, | |||||
| ul ol { | |||||
| margin-bottom: 0; | |||||
| } | |||||
| dt { | |||||
| font-weight: 700; | |||||
| } | |||||
| dd { | |||||
| margin-bottom: 0.5rem; | |||||
| margin-left: 0; | |||||
| } | |||||
| blockquote { | |||||
| margin: 0 0 1rem; | |||||
| } | |||||
| b, | |||||
| strong { | |||||
| font-weight: bolder; | |||||
| } | |||||
| small { | |||||
| font-size: 0.875em; | |||||
| } | |||||
| mark { | |||||
| padding: 0.1875em; | |||||
| background-color: var(--bs-highlight-bg); | |||||
| } | |||||
| sub, | |||||
| sup { | |||||
| position: relative; | |||||
| font-size: 0.75em; | |||||
| line-height: 0; | |||||
| vertical-align: baseline; | |||||
| } | |||||
| sub { | |||||
| bottom: -0.25em; | |||||
| } | |||||
| sup { | |||||
| top: -0.5em; | |||||
| } | |||||
| a { | |||||
| color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); | |||||
| text-decoration: underline; | |||||
| } | |||||
| a:hover { | |||||
| --bs-link-color-rgb: var(--bs-link-hover-color-rgb); | |||||
| } | |||||
| a:not([href]):not([class]), a:not([href]):not([class]):hover { | |||||
| color: inherit; | |||||
| text-decoration: none; | |||||
| } | |||||
| pre, | |||||
| code, | |||||
| kbd, | |||||
| samp { | |||||
| font-family: var(--bs-font-monospace); | |||||
| font-size: 1em; | |||||
| } | |||||
| pre { | |||||
| display: block; | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| overflow: auto; | |||||
| font-size: 0.875em; | |||||
| } | |||||
| pre code { | |||||
| font-size: inherit; | |||||
| color: inherit; | |||||
| word-break: normal; | |||||
| } | |||||
| code { | |||||
| font-size: 0.875em; | |||||
| color: var(--bs-code-color); | |||||
| word-wrap: break-word; | |||||
| } | |||||
| a > code { | |||||
| color: inherit; | |||||
| } | |||||
| kbd { | |||||
| padding: 0.1875rem 0.375rem; | |||||
| font-size: 0.875em; | |||||
| color: var(--bs-body-bg); | |||||
| background-color: var(--bs-body-color); | |||||
| border-radius: 0.25rem; | |||||
| } | |||||
| kbd kbd { | |||||
| padding: 0; | |||||
| font-size: 1em; | |||||
| } | |||||
| figure { | |||||
| margin: 0 0 1rem; | |||||
| } | |||||
| img, | |||||
| svg { | |||||
| vertical-align: middle; | |||||
| } | |||||
| table { | |||||
| caption-side: bottom; | |||||
| border-collapse: collapse; | |||||
| } | |||||
| caption { | |||||
| padding-top: 0.5rem; | |||||
| padding-bottom: 0.5rem; | |||||
| color: var(--bs-secondary-color); | |||||
| text-align: left; | |||||
| } | |||||
| th { | |||||
| text-align: inherit; | |||||
| text-align: -webkit-match-parent; | |||||
| } | |||||
| thead, | |||||
| tbody, | |||||
| tfoot, | |||||
| tr, | |||||
| td, | |||||
| th { | |||||
| border-color: inherit; | |||||
| border-style: solid; | |||||
| border-width: 0; | |||||
| } | |||||
| label { | |||||
| display: inline-block; | |||||
| } | |||||
| button { | |||||
| border-radius: 0; | |||||
| } | |||||
| button:focus:not(:focus-visible) { | |||||
| outline: 0; | |||||
| } | |||||
| input, | |||||
| button, | |||||
| select, | |||||
| optgroup, | |||||
| textarea { | |||||
| margin: 0; | |||||
| font-family: inherit; | |||||
| font-size: inherit; | |||||
| line-height: inherit; | |||||
| } | |||||
| button, | |||||
| select { | |||||
| text-transform: none; | |||||
| } | |||||
| [role=button] { | |||||
| cursor: pointer; | |||||
| } | |||||
| select { | |||||
| word-wrap: normal; | |||||
| } | |||||
| select:disabled { | |||||
| opacity: 1; | |||||
| } | |||||
| [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { | |||||
| display: none !important; | |||||
| } | |||||
| button, | |||||
| [type=button], | |||||
| [type=reset], | |||||
| [type=submit] { | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| button:not(:disabled), | |||||
| [type=button]:not(:disabled), | |||||
| [type=reset]:not(:disabled), | |||||
| [type=submit]:not(:disabled) { | |||||
| cursor: pointer; | |||||
| } | |||||
| ::-moz-focus-inner { | |||||
| padding: 0; | |||||
| border-style: none; | |||||
| } | |||||
| textarea { | |||||
| resize: vertical; | |||||
| } | |||||
| fieldset { | |||||
| min-width: 0; | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| border: 0; | |||||
| } | |||||
| legend { | |||||
| float: left; | |||||
| width: 100%; | |||||
| padding: 0; | |||||
| margin-bottom: 0.5rem; | |||||
| font-size: calc(1.275rem + 0.3vw); | |||||
| line-height: inherit; | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| legend { | |||||
| font-size: 1.5rem; | |||||
| } | |||||
| } | |||||
| legend + * { | |||||
| clear: left; | |||||
| } | |||||
| ::-webkit-datetime-edit-fields-wrapper, | |||||
| ::-webkit-datetime-edit-text, | |||||
| ::-webkit-datetime-edit-minute, | |||||
| ::-webkit-datetime-edit-hour-field, | |||||
| ::-webkit-datetime-edit-day-field, | |||||
| ::-webkit-datetime-edit-month-field, | |||||
| ::-webkit-datetime-edit-year-field { | |||||
| padding: 0; | |||||
| } | |||||
| ::-webkit-inner-spin-button { | |||||
| height: auto; | |||||
| } | |||||
| [type=search] { | |||||
| outline-offset: -2px; | |||||
| -webkit-appearance: textfield; | |||||
| } | |||||
| /* rtl:raw: | |||||
| [type="tel"], | |||||
| [type="url"], | |||||
| [type="email"], | |||||
| [type="number"] { | |||||
| direction: ltr; | |||||
| } | |||||
| */ | |||||
| ::-webkit-search-decoration { | |||||
| -webkit-appearance: none; | |||||
| } | |||||
| ::-webkit-color-swatch-wrapper { | |||||
| padding: 0; | |||||
| } | |||||
| ::-webkit-file-upload-button { | |||||
| font: inherit; | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| ::file-selector-button { | |||||
| font: inherit; | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| output { | |||||
| display: inline-block; | |||||
| } | |||||
| iframe { | |||||
| border: 0; | |||||
| } | |||||
| summary { | |||||
| display: list-item; | |||||
| cursor: pointer; | |||||
| } | |||||
| progress { | |||||
| vertical-align: baseline; | |||||
| } | |||||
| [hidden] { | |||||
| display: none !important; | |||||
| } | |||||
| /*# sourceMappingURL=bootstrap-reboot.css.map */ | |||||
| @ -0,0 +1,590 @@ | |||||
| /*! | |||||
| * Bootstrap Reboot v5.3.0 (https://getbootstrap.com/) | |||||
| * Copyright 2011-2023 The Bootstrap Authors | |||||
| * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) | |||||
| */ | |||||
| :root, | |||||
| [data-bs-theme=light] { | |||||
| --bs-blue: #0d6efd; | |||||
| --bs-indigo: #6610f2; | |||||
| --bs-purple: #6f42c1; | |||||
| --bs-pink: #d63384; | |||||
| --bs-red: #dc3545; | |||||
| --bs-orange: #fd7e14; | |||||
| --bs-yellow: #ffc107; | |||||
| --bs-green: #198754; | |||||
| --bs-teal: #20c997; | |||||
| --bs-cyan: #0dcaf0; | |||||
| --bs-black: #000; | |||||
| --bs-white: #fff; | |||||
| --bs-gray: #6c757d; | |||||
| --bs-gray-dark: #343a40; | |||||
| --bs-gray-100: #f8f9fa; | |||||
| --bs-gray-200: #e9ecef; | |||||
| --bs-gray-300: #dee2e6; | |||||
| --bs-gray-400: #ced4da; | |||||
| --bs-gray-500: #adb5bd; | |||||
| --bs-gray-600: #6c757d; | |||||
| --bs-gray-700: #495057; | |||||
| --bs-gray-800: #343a40; | |||||
| --bs-gray-900: #212529; | |||||
| --bs-primary: #0d6efd; | |||||
| --bs-secondary: #6c757d; | |||||
| --bs-success: #198754; | |||||
| --bs-info: #0dcaf0; | |||||
| --bs-warning: #ffc107; | |||||
| --bs-danger: #dc3545; | |||||
| --bs-light: #f8f9fa; | |||||
| --bs-dark: #212529; | |||||
| --bs-primary-rgb: 13, 110, 253; | |||||
| --bs-secondary-rgb: 108, 117, 125; | |||||
| --bs-success-rgb: 25, 135, 84; | |||||
| --bs-info-rgb: 13, 202, 240; | |||||
| --bs-warning-rgb: 255, 193, 7; | |||||
| --bs-danger-rgb: 220, 53, 69; | |||||
| --bs-light-rgb: 248, 249, 250; | |||||
| --bs-dark-rgb: 33, 37, 41; | |||||
| --bs-primary-text-emphasis: #052c65; | |||||
| --bs-secondary-text-emphasis: #2b2f32; | |||||
| --bs-success-text-emphasis: #0a3622; | |||||
| --bs-info-text-emphasis: #055160; | |||||
| --bs-warning-text-emphasis: #664d03; | |||||
| --bs-danger-text-emphasis: #58151c; | |||||
| --bs-light-text-emphasis: #495057; | |||||
| --bs-dark-text-emphasis: #495057; | |||||
| --bs-primary-bg-subtle: #cfe2ff; | |||||
| --bs-secondary-bg-subtle: #e2e3e5; | |||||
| --bs-success-bg-subtle: #d1e7dd; | |||||
| --bs-info-bg-subtle: #cff4fc; | |||||
| --bs-warning-bg-subtle: #fff3cd; | |||||
| --bs-danger-bg-subtle: #f8d7da; | |||||
| --bs-light-bg-subtle: #fcfcfd; | |||||
| --bs-dark-bg-subtle: #ced4da; | |||||
| --bs-primary-border-subtle: #9ec5fe; | |||||
| --bs-secondary-border-subtle: #c4c8cb; | |||||
| --bs-success-border-subtle: #a3cfbb; | |||||
| --bs-info-border-subtle: #9eeaf9; | |||||
| --bs-warning-border-subtle: #ffe69c; | |||||
| --bs-danger-border-subtle: #f1aeb5; | |||||
| --bs-light-border-subtle: #e9ecef; | |||||
| --bs-dark-border-subtle: #adb5bd; | |||||
| --bs-white-rgb: 255, 255, 255; | |||||
| --bs-black-rgb: 0, 0, 0; | |||||
| --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | |||||
| --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |||||
| --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); | |||||
| --bs-body-font-family: var(--bs-font-sans-serif); | |||||
| --bs-body-font-size: 1rem; | |||||
| --bs-body-font-weight: 400; | |||||
| --bs-body-line-height: 1.5; | |||||
| --bs-body-color: #212529; | |||||
| --bs-body-color-rgb: 33, 37, 41; | |||||
| --bs-body-bg: #fff; | |||||
| --bs-body-bg-rgb: 255, 255, 255; | |||||
| --bs-emphasis-color: #000; | |||||
| --bs-emphasis-color-rgb: 0, 0, 0; | |||||
| --bs-secondary-color: rgba(33, 37, 41, 0.75); | |||||
| --bs-secondary-color-rgb: 33, 37, 41; | |||||
| --bs-secondary-bg: #e9ecef; | |||||
| --bs-secondary-bg-rgb: 233, 236, 239; | |||||
| --bs-tertiary-color: rgba(33, 37, 41, 0.5); | |||||
| --bs-tertiary-color-rgb: 33, 37, 41; | |||||
| --bs-tertiary-bg: #f8f9fa; | |||||
| --bs-tertiary-bg-rgb: 248, 249, 250; | |||||
| --bs-heading-color: inherit; | |||||
| --bs-link-color: #0d6efd; | |||||
| --bs-link-color-rgb: 13, 110, 253; | |||||
| --bs-link-decoration: underline; | |||||
| --bs-link-hover-color: #0a58ca; | |||||
| --bs-link-hover-color-rgb: 10, 88, 202; | |||||
| --bs-code-color: #d63384; | |||||
| --bs-highlight-bg: #fff3cd; | |||||
| --bs-border-width: 1px; | |||||
| --bs-border-style: solid; | |||||
| --bs-border-color: #dee2e6; | |||||
| --bs-border-color-translucent: rgba(0, 0, 0, 0.175); | |||||
| --bs-border-radius: 0.375rem; | |||||
| --bs-border-radius-sm: 0.25rem; | |||||
| --bs-border-radius-lg: 0.5rem; | |||||
| --bs-border-radius-xl: 1rem; | |||||
| --bs-border-radius-xxl: 2rem; | |||||
| --bs-border-radius-2xl: var(--bs-border-radius-xxl); | |||||
| --bs-border-radius-pill: 50rem; | |||||
| --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); | |||||
| --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); | |||||
| --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); | |||||
| --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); | |||||
| --bs-focus-ring-width: 0.25rem; | |||||
| --bs-focus-ring-opacity: 0.25; | |||||
| --bs-focus-ring-color: rgba(13, 110, 253, 0.25); | |||||
| --bs-form-valid-color: #198754; | |||||
| --bs-form-valid-border-color: #198754; | |||||
| --bs-form-invalid-color: #dc3545; | |||||
| --bs-form-invalid-border-color: #dc3545; | |||||
| } | |||||
| [data-bs-theme=dark] { | |||||
| color-scheme: dark; | |||||
| --bs-body-color: #adb5bd; | |||||
| --bs-body-color-rgb: 173, 181, 189; | |||||
| --bs-body-bg: #212529; | |||||
| --bs-body-bg-rgb: 33, 37, 41; | |||||
| --bs-emphasis-color: #fff; | |||||
| --bs-emphasis-color-rgb: 255, 255, 255; | |||||
| --bs-secondary-color: rgba(173, 181, 189, 0.75); | |||||
| --bs-secondary-color-rgb: 173, 181, 189; | |||||
| --bs-secondary-bg: #343a40; | |||||
| --bs-secondary-bg-rgb: 52, 58, 64; | |||||
| --bs-tertiary-color: rgba(173, 181, 189, 0.5); | |||||
| --bs-tertiary-color-rgb: 173, 181, 189; | |||||
| --bs-tertiary-bg: #2b3035; | |||||
| --bs-tertiary-bg-rgb: 43, 48, 53; | |||||
| --bs-primary-text-emphasis: #6ea8fe; | |||||
| --bs-secondary-text-emphasis: #a7acb1; | |||||
| --bs-success-text-emphasis: #75b798; | |||||
| --bs-info-text-emphasis: #6edff6; | |||||
| --bs-warning-text-emphasis: #ffda6a; | |||||
| --bs-danger-text-emphasis: #ea868f; | |||||
| --bs-light-text-emphasis: #f8f9fa; | |||||
| --bs-dark-text-emphasis: #dee2e6; | |||||
| --bs-primary-bg-subtle: #031633; | |||||
| --bs-secondary-bg-subtle: #161719; | |||||
| --bs-success-bg-subtle: #051b11; | |||||
| --bs-info-bg-subtle: #032830; | |||||
| --bs-warning-bg-subtle: #332701; | |||||
| --bs-danger-bg-subtle: #2c0b0e; | |||||
| --bs-light-bg-subtle: #343a40; | |||||
| --bs-dark-bg-subtle: #1a1d20; | |||||
| --bs-primary-border-subtle: #084298; | |||||
| --bs-secondary-border-subtle: #41464b; | |||||
| --bs-success-border-subtle: #0f5132; | |||||
| --bs-info-border-subtle: #087990; | |||||
| --bs-warning-border-subtle: #997404; | |||||
| --bs-danger-border-subtle: #842029; | |||||
| --bs-light-border-subtle: #495057; | |||||
| --bs-dark-border-subtle: #343a40; | |||||
| --bs-heading-color: inherit; | |||||
| --bs-link-color: #6ea8fe; | |||||
| --bs-link-hover-color: #8bb9fe; | |||||
| --bs-link-color-rgb: 110, 168, 254; | |||||
| --bs-link-hover-color-rgb: 139, 185, 254; | |||||
| --bs-code-color: #e685b5; | |||||
| --bs-border-color: #495057; | |||||
| --bs-border-color-translucent: rgba(255, 255, 255, 0.15); | |||||
| --bs-form-valid-color: #75b798; | |||||
| --bs-form-valid-border-color: #75b798; | |||||
| --bs-form-invalid-color: #ea868f; | |||||
| --bs-form-invalid-border-color: #ea868f; | |||||
| } | |||||
| *, | |||||
| *::before, | |||||
| *::after { | |||||
| box-sizing: border-box; | |||||
| } | |||||
| @media (prefers-reduced-motion: no-preference) { | |||||
| :root { | |||||
| scroll-behavior: smooth; | |||||
| } | |||||
| } | |||||
| body { | |||||
| margin: 0; | |||||
| font-family: var(--bs-body-font-family); | |||||
| font-size: var(--bs-body-font-size); | |||||
| font-weight: var(--bs-body-font-weight); | |||||
| line-height: var(--bs-body-line-height); | |||||
| color: var(--bs-body-color); | |||||
| text-align: var(--bs-body-text-align); | |||||
| background-color: var(--bs-body-bg); | |||||
| -webkit-text-size-adjust: 100%; | |||||
| -webkit-tap-highlight-color: rgba(0, 0, 0, 0); | |||||
| } | |||||
| hr { | |||||
| margin: 1rem 0; | |||||
| color: inherit; | |||||
| border: 0; | |||||
| border-top: var(--bs-border-width) solid; | |||||
| opacity: 0.25; | |||||
| } | |||||
| h6, h5, h4, h3, h2, h1 { | |||||
| margin-top: 0; | |||||
| margin-bottom: 0.5rem; | |||||
| font-weight: 500; | |||||
| line-height: 1.2; | |||||
| color: var(--bs-heading-color); | |||||
| } | |||||
| h1 { | |||||
| font-size: calc(1.375rem + 1.5vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h1 { | |||||
| font-size: 2.5rem; | |||||
| } | |||||
| } | |||||
| h2 { | |||||
| font-size: calc(1.325rem + 0.9vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h2 { | |||||
| font-size: 2rem; | |||||
| } | |||||
| } | |||||
| h3 { | |||||
| font-size: calc(1.3rem + 0.6vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h3 { | |||||
| font-size: 1.75rem; | |||||
| } | |||||
| } | |||||
| h4 { | |||||
| font-size: calc(1.275rem + 0.3vw); | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| h4 { | |||||
| font-size: 1.5rem; | |||||
| } | |||||
| } | |||||
| h5 { | |||||
| font-size: 1.25rem; | |||||
| } | |||||
| h6 { | |||||
| font-size: 1rem; | |||||
| } | |||||
| p { | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| } | |||||
| abbr[title] { | |||||
| -webkit-text-decoration: underline dotted; | |||||
| text-decoration: underline dotted; | |||||
| cursor: help; | |||||
| -webkit-text-decoration-skip-ink: none; | |||||
| text-decoration-skip-ink: none; | |||||
| } | |||||
| address { | |||||
| margin-bottom: 1rem; | |||||
| font-style: normal; | |||||
| line-height: inherit; | |||||
| } | |||||
| ol, | |||||
| ul { | |||||
| padding-right: 2rem; | |||||
| } | |||||
| ol, | |||||
| ul, | |||||
| dl { | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| } | |||||
| ol ol, | |||||
| ul ul, | |||||
| ol ul, | |||||
| ul ol { | |||||
| margin-bottom: 0; | |||||
| } | |||||
| dt { | |||||
| font-weight: 700; | |||||
| } | |||||
| dd { | |||||
| margin-bottom: 0.5rem; | |||||
| margin-right: 0; | |||||
| } | |||||
| blockquote { | |||||
| margin: 0 0 1rem; | |||||
| } | |||||
| b, | |||||
| strong { | |||||
| font-weight: bolder; | |||||
| } | |||||
| small { | |||||
| font-size: 0.875em; | |||||
| } | |||||
| mark { | |||||
| padding: 0.1875em; | |||||
| background-color: var(--bs-highlight-bg); | |||||
| } | |||||
| sub, | |||||
| sup { | |||||
| position: relative; | |||||
| font-size: 0.75em; | |||||
| line-height: 0; | |||||
| vertical-align: baseline; | |||||
| } | |||||
| sub { | |||||
| bottom: -0.25em; | |||||
| } | |||||
| sup { | |||||
| top: -0.5em; | |||||
| } | |||||
| a { | |||||
| color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); | |||||
| text-decoration: underline; | |||||
| } | |||||
| a:hover { | |||||
| --bs-link-color-rgb: var(--bs-link-hover-color-rgb); | |||||
| } | |||||
| a:not([href]):not([class]), a:not([href]):not([class]):hover { | |||||
| color: inherit; | |||||
| text-decoration: none; | |||||
| } | |||||
| pre, | |||||
| code, | |||||
| kbd, | |||||
| samp { | |||||
| font-family: var(--bs-font-monospace); | |||||
| font-size: 1em; | |||||
| } | |||||
| pre { | |||||
| display: block; | |||||
| margin-top: 0; | |||||
| margin-bottom: 1rem; | |||||
| overflow: auto; | |||||
| font-size: 0.875em; | |||||
| } | |||||
| pre code { | |||||
| font-size: inherit; | |||||
| color: inherit; | |||||
| word-break: normal; | |||||
| } | |||||
| code { | |||||
| font-size: 0.875em; | |||||
| color: var(--bs-code-color); | |||||
| word-wrap: break-word; | |||||
| } | |||||
| a > code { | |||||
| color: inherit; | |||||
| } | |||||
| kbd { | |||||
| padding: 0.1875rem 0.375rem; | |||||
| font-size: 0.875em; | |||||
| color: var(--bs-body-bg); | |||||
| background-color: var(--bs-body-color); | |||||
| border-radius: 0.25rem; | |||||
| } | |||||
| kbd kbd { | |||||
| padding: 0; | |||||
| font-size: 1em; | |||||
| } | |||||
| figure { | |||||
| margin: 0 0 1rem; | |||||
| } | |||||
| img, | |||||
| svg { | |||||
| vertical-align: middle; | |||||
| } | |||||
| table { | |||||
| caption-side: bottom; | |||||
| border-collapse: collapse; | |||||
| } | |||||
| caption { | |||||
| padding-top: 0.5rem; | |||||
| padding-bottom: 0.5rem; | |||||
| color: var(--bs-secondary-color); | |||||
| text-align: right; | |||||
| } | |||||
| th { | |||||
| text-align: inherit; | |||||
| text-align: -webkit-match-parent; | |||||
| } | |||||
| thead, | |||||
| tbody, | |||||
| tfoot, | |||||
| tr, | |||||
| td, | |||||
| th { | |||||
| border-color: inherit; | |||||
| border-style: solid; | |||||
| border-width: 0; | |||||
| } | |||||
| label { | |||||
| display: inline-block; | |||||
| } | |||||
| button { | |||||
| border-radius: 0; | |||||
| } | |||||
| button:focus:not(:focus-visible) { | |||||
| outline: 0; | |||||
| } | |||||
| input, | |||||
| button, | |||||
| select, | |||||
| optgroup, | |||||
| textarea { | |||||
| margin: 0; | |||||
| font-family: inherit; | |||||
| font-size: inherit; | |||||
| line-height: inherit; | |||||
| } | |||||
| button, | |||||
| select { | |||||
| text-transform: none; | |||||
| } | |||||
| [role=button] { | |||||
| cursor: pointer; | |||||
| } | |||||
| select { | |||||
| word-wrap: normal; | |||||
| } | |||||
| select:disabled { | |||||
| opacity: 1; | |||||
| } | |||||
| [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { | |||||
| display: none !important; | |||||
| } | |||||
| button, | |||||
| [type=button], | |||||
| [type=reset], | |||||
| [type=submit] { | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| button:not(:disabled), | |||||
| [type=button]:not(:disabled), | |||||
| [type=reset]:not(:disabled), | |||||
| [type=submit]:not(:disabled) { | |||||
| cursor: pointer; | |||||
| } | |||||
| ::-moz-focus-inner { | |||||
| padding: 0; | |||||
| border-style: none; | |||||
| } | |||||
| textarea { | |||||
| resize: vertical; | |||||
| } | |||||
| fieldset { | |||||
| min-width: 0; | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| border: 0; | |||||
| } | |||||
| legend { | |||||
| float: right; | |||||
| width: 100%; | |||||
| padding: 0; | |||||
| margin-bottom: 0.5rem; | |||||
| font-size: calc(1.275rem + 0.3vw); | |||||
| line-height: inherit; | |||||
| } | |||||
| @media (min-width: 1200px) { | |||||
| legend { | |||||
| font-size: 1.5rem; | |||||
| } | |||||
| } | |||||
| legend + * { | |||||
| clear: right; | |||||
| } | |||||
| ::-webkit-datetime-edit-fields-wrapper, | |||||
| ::-webkit-datetime-edit-text, | |||||
| ::-webkit-datetime-edit-minute, | |||||
| ::-webkit-datetime-edit-hour-field, | |||||
| ::-webkit-datetime-edit-day-field, | |||||
| ::-webkit-datetime-edit-month-field, | |||||
| ::-webkit-datetime-edit-year-field { | |||||
| padding: 0; | |||||
| } | |||||
| ::-webkit-inner-spin-button { | |||||
| height: auto; | |||||
| } | |||||
| [type=search] { | |||||
| outline-offset: -2px; | |||||
| -webkit-appearance: textfield; | |||||
| } | |||||
| [type="tel"], | |||||
| [type="url"], | |||||
| [type="email"], | |||||
| [type="number"] { | |||||
| direction: ltr; | |||||
| } | |||||
| ::-webkit-search-decoration { | |||||
| -webkit-appearance: none; | |||||
| } | |||||
| ::-webkit-color-swatch-wrapper { | |||||
| padding: 0; | |||||
| } | |||||
| ::-webkit-file-upload-button { | |||||
| font: inherit; | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| ::file-selector-button { | |||||
| font: inherit; | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| output { | |||||
| display: inline-block; | |||||
| } | |||||
| iframe { | |||||
| border: 0; | |||||
| } | |||||
| summary { | |||||
| display: list-item; | |||||
| cursor: pointer; | |||||
| } | |||||
| progress { | |||||
| vertical-align: baseline; | |||||
| } | |||||
| [hidden] { | |||||
| display: none !important; | |||||
| } | |||||
| /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ | |||||