| @ -0,0 +1,2 @@ | |||
| ( NAMESPACE=registry; kubectl proxy & kubectl get namespace $NAMESPACE -o json |jq '.spec = {"finalizers":[]}' >temp.json; curl -k -H "Content-Type: application/json" -X PUT --data-binary @temp.json 127.0.0.1:8001/api/v1/namespaces/$NAMESPACE/finalize; ) | |||
| @ -0,0 +1 @@ | |||
| docker run --name registry-browser -p 8080:8080 localhost:30500/registry-browser:1.0 | |||
| @ -0,0 +1,57 @@ | |||
| { | |||
| "apiVersion": "v1", | |||
| "kind": "Namespace", | |||
| "metadata": { | |||
| "creationTimestamp": "2024-10-21T09:26:01Z", | |||
| "deletionTimestamp": "2024-10-21T12:39:45Z", | |||
| "labels": { | |||
| "kubernetes.io/metadata.name": "registry" | |||
| }, | |||
| "name": "registry", | |||
| "resourceVersion": "113656", | |||
| "uid": "9193fabc-d5af-40fc-be94-11967be4e7fe" | |||
| }, | |||
| "spec": { | |||
| "finalizers": [] | |||
| }, | |||
| "status": { | |||
| "conditions": [ | |||
| { | |||
| "lastTransitionTime": "2024-10-21T12:39:50Z", | |||
| "message": "Discovery failed for some groups, 1 failing: unable to retrieve the complete list of server APIs: projectcalico.org/v3: stale GroupVersion discovery: projectcalico.org/v3", | |||
| "reason": "DiscoveryFailed", | |||
| "status": "True", | |||
| "type": "NamespaceDeletionDiscoveryFailure" | |||
| }, | |||
| { | |||
| "lastTransitionTime": "2024-10-21T12:39:50Z", | |||
| "message": "All legacy kube types successfully parsed", | |||
| "reason": "ParsedGroupVersions", | |||
| "status": "False", | |||
| "type": "NamespaceDeletionGroupVersionParsingFailure" | |||
| }, | |||
| { | |||
| "lastTransitionTime": "2024-10-21T12:39:50Z", | |||
| "message": "All content successfully deleted, may be waiting on finalization", | |||
| "reason": "ContentDeleted", | |||
| "status": "False", | |||
| "type": "NamespaceDeletionContentFailure" | |||
| }, | |||
| { | |||
| "lastTransitionTime": "2024-10-21T12:39:50Z", | |||
| "message": "All content successfully removed", | |||
| "reason": "ContentRemoved", | |||
| "status": "False", | |||
| "type": "NamespaceContentRemaining" | |||
| }, | |||
| { | |||
| "lastTransitionTime": "2024-10-21T12:39:50Z", | |||
| "message": "All content-preserving finalizers finished", | |||
| "reason": "ContentHasNoFinalizers", | |||
| "status": "False", | |||
| "type": "NamespaceFinalizersRemaining" | |||
| } | |||
| ], | |||
| "phase": "Terminating" | |||
| } | |||
| } | |||
| @ -1,4 +1,3 @@ | |||
| Dockerfile | |||
| Makefile | |||
| volcadossql/ | |||
| venv/ | |||
| @ -0,0 +1 @@ | |||
| temp.json | |||
| @ -0,0 +1,2 @@ | |||
| ( NAMESPACE=reymota; kubectl proxy & kubectl get namespace $NAMESPACE -o json |jq '.spec = {"finalizers":[]}' >temp.json; curl -k -H "Content-Type: application/json" -X PUT --data-binary @temp.json 127.0.0.1:8001/api/v1/namespaces/$NAMESPACE/finalize; ) | |||
| @ -0,0 +1,7 @@ | |||
| sudo mkdir -p /mnt/Externo/reymota/media | |||
| sudo mkdir -p /mnt/Externo/reymota/migrations/lyrics | |||
| sudo mkdir -p /mnt/Externo/reymota/migrations/libros | |||
| sudo mkdir -p /mnt/Externo/reymota/migrations/repostajes | |||
| sudo mkdir -p /mnt/Externo/reymota/migrations/reymotausers | |||
| sudo mkdir -p /mnt/Externo/reymota/static | |||
| sudo mkdir -p /mnt/Externo/reymota/pg | |||
| @ -0,0 +1,3 @@ | |||
| Dockerfile | |||
| Makefile | |||
| venv/ | |||
| @ -0,0 +1,76 @@ | |||
| # 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 directory for gunicorn logs | |||
| RUN mkdir -p /var/log/gunicorn | |||
| # 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 procps vim | |||
| 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/riesgos | |||
| # run entrypoint.sh | |||
| ENTRYPOINT ["/app/entrypoint.sh"] | |||
| @ -0,0 +1 @@ | |||
| temp.json | |||
| @ -0,0 +1,53 @@ | |||
| export ARQUITECTURA := $(shell lscpu |grep itectur | tr -d ' '| cut -f2 -d':') | |||
| #export REGISTRY=registry.cube.local | |||
| export REGISTRY=registry.reymota.es | |||
| export IMG_VERSION = 0.20 | |||
| export IMG_NGINX_VERSION = 1.0 | |||
| # limpia todo | |||
| all: imagen 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-riesgos.yaml | |||
| -kubectl create -f riesgos-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 < riesgos-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 < riesgos-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 riesgos-prod-persistentvolumeclaim.yaml | |||
| -kubectl delete -f pv-local-riesgos.yaml | |||
| -kubectl delete -f reg-secret.yaml | |||
| -kubectl delete -f namespace.yaml | |||
| nginx: | |||
| cd ../nginx; make | |||
| backup: | |||
| kubectl --kubeconfig /home/creylopez/.kube/config -n riesgos exec -ti deployment.apps/db -- /usr/lib/postgresql/15/bin/pg_dump --username=creylopez --dbname=riesgos > riesgos-$(IMG_VERSION).sql | |||
| @ -0,0 +1,67 @@ | |||
| export ARQUITECTURA := $(shell lscpu |grep itectur | tr -d ' '| cut -f2 -d':') | |||
| export REGISTRY=localhost:30500 | |||
| #export REGISTRY=registry.reymota.es | |||
| export IMG_VERSION = 0.66 | |||
| export IMG_NGINX_VERSION = 1.1 | |||
| # limpia todo | |||
| all: imagen clean install | |||
| imagen: | |||
| cd ../; make | |||
| install: | |||
| -kubectl create -f namespace.yaml | |||
| -kubectl create -f reg-secret.yaml | |||
| -kubectl create -f env-prod-configmap-local.yaml | |||
| -kubectl create -f env-prod-db-configmap.yaml | |||
| -kubectl create -f pv-local-riesgos.yaml | |||
| -kubectl create -f riesgos-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 < riesgos-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 < riesgos-deployment.yaml |kubectl delete -f - | |||
| -kubectl delete -f db-deployment.yaml | |||
| -kubectl delete -f db-service.yaml | |||
| -kubectl delete -f env-prod-configmap-local.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 riesgos-prod-persistentvolumeclaim.yaml | |||
| -kubectl delete -f pv-local-riesgos.yaml | |||
| -kubectl delete -f reg-secret.yaml | |||
| -kubectl delete -f namespace.yaml | |||
| nginx: | |||
| cd ../nginx; make | |||
| verimg: | |||
| docker run -it ${REGISTRY}/riesgos-${ARQUITECTURA}:${IMG_VERSION} bash | |||
| backup: | |||
| kubectl --kubeconfig /home/creylopez/.kube/config -n riesgos exec -ti deployment.apps/db -- /usr/lib/postgresql/15/bin/pg_dump --username=creylopez --dbname=riesgos > riesgos-$(IMG_VERSION).sql | |||
| mrproper: | |||
| -kubectl -n riesgos patch pvc postgres-data -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pvc static-volume -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pvc riesgos-usuarios-migrations -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pv riesgos-pg-folder -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pvc riesgos-usuarios-migrations -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pv riesgos-gestion-migrations-folder -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pvc riesgos-media-folder -p '{"metadata":{"finalizers":null}}' | |||
| -kubectl -n riesgos patch pvc riesgos-media -p '{"metadata":{"finalizers":null}}' | |||
| @ -0,0 +1,2 @@ | |||
| ( NAMESPACE=riesgos; kubectl proxy & kubectl get namespace $NAMESPACE -o json |jq '.spec = {"finalizers":[]}' >temp.json; curl -k -H "Content-Type: application/json" -X PUT --data-binary @temp.json 127.0.0.1:8001/api/v1/namespaces/$NAMESPACE/finalize; ) | |||
| @ -0,0 +1,5 @@ | |||
| sudo mkdir -p /mnt/Externo/riesgos/media | |||
| sudo mkdir -p /mnt/Externo/riesgos/migrations/gestion | |||
| sudo mkdir -p /mnt/Externo/riesgos/migrations/usuarios | |||
| sudo mkdir -p /mnt/Externo/riesgos/static | |||
| sudo mkdir -p /mnt/Externo/riesgos/pg | |||
| @ -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: riesgos | |||
| 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: riesgos | |||
| spec: | |||
| ports: | |||
| - name: "5432" | |||
| port: 5432 | |||
| targetPort: 5432 | |||
| selector: | |||
| io.kompose.service: db | |||
| @ -0,0 +1 @@ | |||
| kubectl -n riesgos exec -ti deployment.apps/riesgos -- /bin/bash | |||
| @ -0,0 +1 @@ | |||
| kubectl -n riesgos exec -ti deployment.apps/db -- psql --username=creylopez --dbname=riesgos | |||
| @ -0,0 +1,20 @@ | |||
| apiVersion: v1 | |||
| data: | |||
| DEBUG: "True" | |||
| ENTORNO: "Pruebas" | |||
| DJANGO_ALLOWED_HOSTS: "riesgos.es vmcluster k8s-server localhost 127.0.0.1 [::1]" | |||
| CSRF_TRUSTED_ORIGINS: "https://riesgos.es http://vmcluster" | |||
| SECRET_KEY: change_me | |||
| SQL_DATABASE: riesgos | |||
| 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: riesgos | |||
| @ -0,0 +1,20 @@ | |||
| apiVersion: v1 | |||
| data: | |||
| DEBUG: "False" | |||
| ENTORNO: "Producción" | |||
| DJANGO_ALLOWED_HOSTS: "riesgos.es vmcluster k8s-server localhost 127.0.0.1 [::1]" | |||
| CSRF_TRUSTED_ORIGINS: "https://riesgos.es http://vmcluster" | |||
| SECRET_KEY: change_me | |||
| SQL_DATABASE: riesgos | |||
| 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: riesgos | |||
| @ -0,0 +1,11 @@ | |||
| apiVersion: v1 | |||
| data: | |||
| POSTGRES_DB: riesgos | |||
| POSTGRES_PASSWORD: Dsa-0213 | |||
| POSTGRES_USER: creylopez | |||
| kind: ConfigMap | |||
| metadata: | |||
| labels: | |||
| io.kompose.service: db-env-prod-db | |||
| name: env-prod-db | |||
| namespace: riesgos | |||
| @ -0,0 +1,7 @@ | |||
| ################################################### | |||
| # Namespace riesgos | |||
| ################################################### | |||
| apiVersion: v1 | |||
| kind: Namespace | |||
| metadata: | |||
| name: riesgos | |||
| @ -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: riesgos | |||
| 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/nginx-riesgos-$ARQUITECTURA:$IMG_NGINX_VERSION | |||
| name: nginx | |||
| ports: | |||
| - containerPort: 80 | |||
| protocol: TCP | |||
| volumeMounts: | |||
| - mountPath: /app/riesgos/staticfiles | |||
| name: static-volume | |||
| - mountPath: /app/riesgos/mediafiles | |||
| name: riesgos-media | |||
| imagePullSecrets: | |||
| - name: myregistrykey | |||
| restartPolicy: Always | |||
| volumes: | |||
| - name: static-volume | |||
| persistentVolumeClaim: | |||
| claimName: static-volume | |||
| - name: riesgos-media | |||
| persistentVolumeClaim: | |||
| claimName: riesgos-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: riesgos | |||
| spec: | |||
| type: NodePort | |||
| ports: | |||
| - name: "1337" | |||
| port: 1337 | |||
| nodePort: 30342 | |||
| 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: riesgos | |||
| spec: | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| resources: | |||
| requests: | |||
| storage: 100Mi | |||
| @ -0,0 +1,74 @@ | |||
| apiVersion: v1 | |||
| kind: PersistentVolume | |||
| metadata: | |||
| name: riesgos-media-folder | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| capacity: | |||
| storage: 100Mi | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| hostPath: | |||
| path: "/mnt/Externo/riesgos/media" | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolume | |||
| metadata: | |||
| name: riesgos-gestion-migrations-folder | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| capacity: | |||
| storage: 52Mi | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| hostPath: | |||
| path: "/mnt/Externo/riesgos/migrations/gestion" | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolume | |||
| metadata: | |||
| name: riesgos-usuarios-migrations-folder | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| capacity: | |||
| storage: 53Mi | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| hostPath: | |||
| path: "/mnt/Externo/riesgos/migrations/usuarios" | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolume | |||
| metadata: | |||
| name: riesgos-static-folder | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| capacity: | |||
| storage: 70Mi | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| hostPath: | |||
| path: "/mnt/Externo/riesgos/static" | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolume | |||
| metadata: | |||
| name: riesgos-pg-folder | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| capacity: | |||
| storage: 200Mi | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| hostPath: | |||
| path: "/mnt/Externo/riesgos/pg" | |||
| @ -0,0 +1,8 @@ | |||
| apiVersion: v1 | |||
| kind: Secret | |||
| metadata: | |||
| name: myregistrykey | |||
| namespace: riesgos | |||
| data: | |||
| .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJyZWdpc3RyeS5yZXltb3RhLmVzIjogewoJCQkiYXV0aCI6ICJZM0psZVd4dmNHVjZPbEpsZVMweE1UYzIiCgkJfQoJfQp9 | |||
| type: kubernetes.io/dockerconfigjson | |||
| @ -0,0 +1,136 @@ | |||
| apiVersion: v1 | |||
| kind: Service | |||
| metadata: | |||
| name: riesgos | |||
| namespace: riesgos | |||
| spec: | |||
| ports: | |||
| - name: "8000" | |||
| port: 8000 | |||
| targetPort: 8000 | |||
| selector: | |||
| app: riesgos | |||
| --- | |||
| apiVersion: apps/v1 | |||
| kind: Deployment | |||
| metadata: | |||
| name: riesgos | |||
| namespace: riesgos | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| replicas: 1 | |||
| selector: | |||
| matchLabels: | |||
| app: riesgos | |||
| strategy: | |||
| type: Recreate | |||
| template: | |||
| metadata: | |||
| labels: | |||
| app: riesgos | |||
| spec: | |||
| containers: | |||
| - args: | |||
| - gunicorn | |||
| - riesgos.wsgi:application | |||
| - --bind | |||
| - 0.0.0.0:8000 | |||
| # - --log-file=/var/log/gunicorn/error.log | |||
| # - --access-logfile=/var/log/gunicorn/access.log | |||
| name: riesgos | |||
| image: $REGISTRY/riesgos-$ARQUITECTURA:$IMG_VERSION | |||
| env: | |||
| - name: VERSION | |||
| value: "$IMG_VERSION" | |||
| - name: ENVIRONMENT | |||
| valueFrom: | |||
| configMapKeyRef: | |||
| key: ENTORNO | |||
| name: env-prod | |||
| - 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/riesgos/mediafiles | |||
| name: riesgos-media | |||
| - mountPath: /app/riesgos/gestion/migrations | |||
| name: riesgos-gestion-migrations | |||
| - mountPath: /app/riesgos/usuarios/migrations | |||
| name: riesgos-usuarios-migrations | |||
| - mountPath: /app/riesgos/staticfiles | |||
| name: static-volume | |||
| imagePullSecrets: | |||
| - name: myregistrykey | |||
| restartPolicy: Always | |||
| volumes: | |||
| - name: riesgos-media | |||
| persistentVolumeClaim: | |||
| claimName: riesgos-media | |||
| - name: riesgos-gestion-migrations | |||
| persistentVolumeClaim: | |||
| claimName: riesgos-gestion-migrations | |||
| - name: riesgos-usuarios-migrations | |||
| persistentVolumeClaim: | |||
| claimName: riesgos-usuarios-migrations | |||
| - name: static-volume | |||
| persistentVolumeClaim: | |||
| claimName: static-volume | |||
| status: {} | |||
| @ -0,0 +1,47 @@ | |||
| apiVersion: v1 | |||
| kind: PersistentVolumeClaim | |||
| metadata: | |||
| creationTimestamp: null | |||
| labels: | |||
| io.kompose.service: riesgos-media | |||
| name: riesgos-media | |||
| namespace: riesgos | |||
| spec: | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| resources: | |||
| requests: | |||
| storage: 100Mi | |||
| status: {} | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolumeClaim | |||
| metadata: | |||
| creationTimestamp: null | |||
| labels: | |||
| io.kompose.service: riesgos-gestion-migrations | |||
| name: riesgos-gestion-migrations | |||
| namespace: riesgos | |||
| spec: | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| resources: | |||
| requests: | |||
| storage: 52Mi | |||
| status: {} | |||
| --- | |||
| apiVersion: v1 | |||
| kind: PersistentVolumeClaim | |||
| metadata: | |||
| creationTimestamp: null | |||
| labels: | |||
| io.kompose.service: riesgos-usuarios-migrations | |||
| name: riesgos-usuarios-migrations | |||
| namespace: riesgos | |||
| spec: | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| resources: | |||
| requests: | |||
| storage: 53Mi | |||
| status: {} | |||
| @ -0,0 +1,13 @@ | |||
| apiVersion: v1 | |||
| kind: PersistentVolumeClaim | |||
| metadata: | |||
| labels: | |||
| io.kompose.service: static-volume | |||
| name: static-volume | |||
| namespace: riesgos | |||
| spec: | |||
| accessModes: | |||
| - ReadWriteOnce | |||
| resources: | |||
| requests: | |||
| storage: 70Mi | |||
| @ -0,0 +1 @@ | |||
| docker run -it registry.riesgos.es/riesgos-x86_64:0.1 bash | |||
| @ -0,0 +1,8 @@ | |||
| install: | |||
| echo "Creando imagen con version '${IMG_VERSION}' para la arquitectura '${ARQUITECTURA}' en el registry '${REGISTRY}'" | |||
| docker build --no-cache -t ${REGISTRY}/riesgos-${ARQUITECTURA}:${IMG_VERSION} . | |||
| docker push ${REGISTRY}/riesgos-${ARQUITECTURA}:${IMG_VERSION} | |||
| @ -0,0 +1,49 @@ | |||
| # Instalación | |||
| Desde el directorio K8S ejecutar make (esto hace todo: la imagen, para los pods y los lanza otra vez) | |||
| La primera vez, hay que entrar en el pod de vehículos con 'entra.sh' y | |||
| python manage.py createsuperuser | |||
| python manage.py makemigrations | |||
| python manage.py migrate | |||
| ## Comprobar la base de datos | |||
| Con la shell entraPsql.sh: | |||
| \l para listar las BD | |||
| \c reymota para usar nuestra db | |||
| \dt para ver las tablas | |||
| # De dónde cogí ideas | |||
| 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/ | |||
| ## Cambiar la secuencia de lo sid | |||
| ALTER SEQUENCE tablename_id_seq RESTART WITH nn; | |||
| esto se hace cuando restauro un volcado de la bd sobre una instalación nueva. Si hay índices ya creados, hay que reinciar a partir del último. | |||
| @ -0,0 +1,20 @@ | |||
| #!/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 collectstatic --noinput | |||
| #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}' en el registry '${REGISTRY}'" | |||
| docker build --no-cache -t ${REGISTRY}/nginx-riesgos-${ARQUITECTURA}:${IMG_NGINX_VERSION} . | |||
| docker push ${REGISTRY}/nginx-riesgos-${ARQUITECTURA}:${IMG_NGINX_VERSION} | |||
| @ -0,0 +1,25 @@ | |||
| upstream riesgos { | |||
| server riesgos:8000; | |||
| } | |||
| server { | |||
| listen 80; | |||
| location / { | |||
| proxy_pass http://riesgos; | |||
| 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/riesgos/staticfiles/; | |||
| } | |||
| location /media/ { | |||
| alias /app/riesgos/mediafiles/; | |||
| } | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| asgiref==3.8.1 | |||
| Django==4.2 | |||
| flake8==7.1.1 | |||
| gunicorn==22.0.0 | |||
| mccabe==0.7.0 | |||
| packaging==24.1 | |||
| pillow==10.4.0 | |||
| psycopg2-binary==2.9.6 | |||
| pycodestyle==2.12.1 | |||
| pyflakes==3.2.0 | |||
| sqlparse==0.5.1 | |||
| typing_extensions==4.12.2 | |||
| django-calculation==1.0.0 | |||
| pandas==2.2.3 | |||
| @ -0,0 +1 @@ | |||
| migrations/ | |||
| @ -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,6 @@ | |||
| export CSRF_TRUSTED_ORIGINS="http://localhost" | |||
| export DEBUG="True" | |||
| export SECRET_KEY="hola" | |||
| export DJANGO_ALLOWED_HOSTS="localhost" | |||
| @ -0,0 +1 @@ | |||
| migrations/ | |||
| @ -0,0 +1,10 @@ | |||
| from django.contrib import admin | |||
| # Register your models here. | |||
| from .models import Proyecto, Accion, Riesgo | |||
| admin.site.register(Proyecto) | |||
| admin.site.register(Riesgo) | |||
| admin.site.register(Accion) | |||
| @ -0,0 +1,6 @@ | |||
| from django.apps import AppConfig | |||
| class GestionConfig(AppConfig): | |||
| default_auto_field = 'django.db.models.BigAutoField' | |||
| name = 'gestion' | |||
| @ -0,0 +1,55 @@ | |||
| from django import forms | |||
| from .models import Proyecto, Riesgo, Accion | |||
| class ProyectoForm(forms.ModelForm): | |||
| class Meta: | |||
| model = Proyecto | |||
| fields = ['referencia', 'titulo', 'tr'] | |||
| referencia = forms.CharField( | |||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||
| titulo = forms.CharField( | |||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||
| tr = forms.CharField( | |||
| widget=forms.TextInput(attrs={'class': 'form-control'})) | |||
| class RiesgoForm(forms.ModelForm): | |||
| class Meta: | |||
| model = Riesgo | |||
| fields = ['fecha', | |||
| 'proyecto', | |||
| 'titulo', | |||
| 'descripcion', | |||
| 'responsable', | |||
| 'importe', | |||
| 'probabilidad', | |||
| 'valor', | |||
| 'fecha_liberacion', | |||
| 'a_liberar', | |||
| 'a_ejecutar', | |||
| 'accion'] | |||
| exclude = ['valor'] | |||
| fecha = forms.DateField( | |||
| widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'})) | |||
| proyecto = forms.ModelChoiceField( | |||
| queryset=Proyecto.objects.all(), | |||
| widget=forms.Select(attrs={'class': 'form-control'})) | |||
| importe = forms.DecimalField( | |||
| widget=forms.NumberInput(attrs={'class': 'form-control'})) | |||
| probabilidad = forms.DecimalField( | |||
| widget=forms.NumberInput(attrs={'class': 'form-control'})) | |||
| a_liberar = forms.BooleanField(initial=False, required=False) | |||
| a_ejecutar = forms.BooleanField(initial=False, required=False) | |||
| fecha_liberacion = forms.DateField( | |||
| widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'})) | |||
| @ -0,0 +1,40 @@ | |||
| from django.db import models | |||
| from django.core.validators import MaxValueValidator | |||
| from usuarios.models import Usuario | |||
| class Proyecto(models.Model): | |||
| referencia = models.CharField(max_length=200) | |||
| titulo = models.CharField(max_length=200) | |||
| tr = models.CharField(max_length=200) | |||
| def __str__(self): | |||
| return self.referencia | |||
| class Accion(models.Model): | |||
| descripcion = models.CharField(max_length=200) | |||
| valor = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) | |||
| fecha = models.DateField() | |||
| def __str__(self): | |||
| return str(self.descripcion) | |||
| class Riesgo(models.Model): | |||
| proyecto = models.ForeignKey(Proyecto, on_delete=models.CASCADE) | |||
| fecha = models.DateField() | |||
| titulo = models.CharField(max_length=200) | |||
| descripcion = models.CharField(max_length=200) | |||
| responsable = models.ForeignKey(Usuario, on_delete=models.DO_NOTHING) | |||
| importe = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) | |||
| probabilidad = models.DecimalField(max_digits=4, decimal_places=0, blank=True, null=True) | |||
| valor = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) | |||
| fecha_liberacion = models.DateField() | |||
| a_liberar = models.BooleanField(default=False) | |||
| a_ejecutar = models.BooleanField(default=False) | |||
| accion = models.ForeignKey(Accion, on_delete=models.CASCADE) | |||
| def __str__(self): | |||
| return str(self.titulo) | |||
| @ -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 */ | |||