32 Commits
Author SHA1 Message Date
Jonas Braus 57dcbc1d0e Edit README.md 2025-03-18 13:10:52 +01:00
Jonas Braus fdb1ed0f99 Add .gitlab-ci.yml 2025-03-18 11:56:56 +01:00
Jonas Braus 78c49501a1 push 2025-03-18 11:55:26 +01:00
Jonas Braus e8a2b9b409 Edit README.md 2025-03-17 17:14:07 +01:00
Jonas Braus 282234c04b new version docker compose 2025-03-09 15:40:29 +01:00
c1ph3r f0e05d7e90 Merge branch 'brokenDateFix' into 'main'
datum in dashboard

See merge request brausjonas/mensaappdashboard!2
2025-03-09 14:28:30 +00:00
c1ph3r 7e357f81a4 fixed error 2025-03-09 15:23:07 +01:00
c1ph3r 0dc944a686 helo
weird bug, apes stronger together
2025-03-09 15:07:39 +01:00
Jonas Braus 3120749c63 Update docker-compose.yml 2025-03-03 16:49:47 +01:00
Jonas Braus d9e5056ba4 Update docker-compose.yml 2025-03-03 16:44:05 +01:00
Jonas Braus d0405f9ec3 puuuuush 2025-03-03 16:36:03 +01:00
Jonas Braus 20116de85b Merge branch 'revert-c28ce0fa' into 'main'
Revert "Stable Supabase (hopefully)"

See merge request brausjonas/mensaappdashboard!1
2025-01-29 21:09:58 +00:00
Jonas Braus 98e6570808 Revert "Stable Supabase (hopefully)"
This reverts commit c28ce0fa99
2025-01-29 21:09:48 +00:00
Ali c28ce0fa99 Stable Supabase (hopefully)
This Push could be a stable version of the needed supabase backend lol
2025-01-29 22:07:31 +01:00
Jonas Braus 283374bfb3 test 2025-01-29 21:50:10 +01:00
Jonas Braus 991452d9f3 test§ 2025-01-29 21:49:08 +01:00
Jonas Braus c166a42ce2 supabase test 2025-01-29 21:41:09 +01:00
Jonas Braus 9c36596b4f supabase 2025-01-29 21:11:05 +01:00
Jonas Braus c9e63523bf fixed supabase docker 2025-01-29 19:47:40 +01:00
Jonas Braus 3c3bc65ca0 fixed view on mobile or other devices 2025-01-24 14:06:19 +01:00
Jonas Braus e027546ef0 ....add volume files.... 2025-01-23 22:43:20 +01:00
Jonas Braus 805309d625 combined compose including dashboard 2025-01-23 22:41:56 +01:00
Jonas Braus 6cec223555 supabase - preconfigured 2025-01-23 22:33:11 +01:00
Jonas Braus 2f82d1c047 dockerfile created 2025-01-23 19:36:57 +01:00
Jonas Braus ef09f5b165 aliAproved 2025-01-15 22:07:16 +01:00
Jonas Braus 0b85bb9ba9 Es geht haha 2025-01-15 21:46:26 +01:00
Ali 28f93e926d average PUUUUSH 2025-01-15 21:16:41 +01:00
Jonas Braus e2138439c2 Time Select for view all 2025-01-15 20:25:47 +01:00
Jonas Braus 5b844d3499 Brrrrrrrrrrr 2025-01-15 19:44:21 +01:00
Ali a45527845f more filter
puuuuush
2025-01-15 19:24:41 +01:00
Ali 61e4416879 Merge branch 'main' of https://gitlab.com/brausjonas/mensaappdashboard 2025-01-15 18:46:09 +01:00
Ali 6696b1f4d0 Start Filter
First step of filters for the dashboard
2025-01-15 18:45:50 +01:00
360 changed files with 221935 additions and 790 deletions
+15
View File
@@ -0,0 +1,15 @@
# The Docker image that will be used to build your app
image: busybox
# Functions that should be executed before the build script is run
before_script: []
pages:
script:
- echo "Copying public dic..."
artifacts:
paths:
# The folder that contains the files to be exposed at the Page URL
- public
rules:
# This ensures that only pushes to the default branch will trigger
# a pages deploy
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
+7
View File
@@ -0,0 +1,7 @@
FROM nginx:stable-perl
WORKDIR /usr/share/nginx/html
COPY ./build/web/ ./
COPY ./default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8051
+18 -12
View File
@@ -1,16 +1,22 @@
# mensaappbackend
# Use GitLab hosted instance
visit [mensaapp on gitlab.io](https://mensaappdashboard-c25a86.gitlab.io/)
A new Flutter project.
# Download Docker
You have to download docker on your system (including compose plugin). F.e. on ubuntu `$ snap install docker`
## Getting Started
# Create compose file
Create a file called `docker-compose.yml` and paste the content:
```
name: supabase
This project is a starting point for a Flutter application.
services:
dashboard:
container_name: mensa-dashboard
image: registry.gitlab.com/brausjonas/mensaappdashboard:0.3
restart: unless-stopped
ports:
- 8051:8051
```
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
open terminal, locate the docker-compose.yml file and run `docker compose up -d` or `docker-compose up -d` depending on your compose plugin verison.
(You might have to run as sudo)
+44
View File
@@ -0,0 +1,44 @@
server {
listen 8051;
listen [::]:8051;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
+4 -481
View File
@@ -1,486 +1,9 @@
# Usage
# Start: docker compose up
# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
# Stop: docker compose down
# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans
name: supabase
services:
studio:
container_name: supabase-studio
image: supabase/studio:20241106-f29003e
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
]
timeout: 5s
interval: 5s
retries: 3
depends_on:
analytics:
condition: service_healthy
environment:
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
AUTH_JWT_SECRET: ${JWT_SECRET}
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
LOGFLARE_URL: http://analytics:4000
NEXT_PUBLIC_ENABLE_LOGS: true
# Comment to use Big Query backend for analytics
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
# Uncomment to use Big Query backend for analytics
# NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery
kong:
container_name: supabase-kong
image: kong:2.8.1
restart: unless-stopped
# https://unix.stackexchange.com/a/294837
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
ports:
- ${KONG_HTTP_PORT}:8000/tcp
- ${KONG_HTTPS_PORT}:8443/tcp
depends_on:
analytics:
condition: service_healthy
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
# https://github.com/supabase/cli/issues/14
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
volumes:
# https://github.com/supabase/supabase/issues/12661
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro
auth:
container_name: supabase-auth
image: supabase/gotrue:v2.164.0
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:9999/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
API_EXTERNAL_URL: ${API_EXTERNAL_URL}
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
GOTRUE_SITE_URL: ${SITE_URL}
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
# Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile.
# GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true
# GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true
# GOTRUE_SMTP_MAX_FREQUENCY: 1s
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
GOTRUE_SMTP_HOST: ${SMTP_HOST}
GOTRUE_SMTP_PORT: ${SMTP_PORT}
GOTRUE_SMTP_USER: ${SMTP_USER}
GOTRUE_SMTP_PASS: ${SMTP_PASS}
GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}
GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE}
GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION}
GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY}
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE}
GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
# Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true"
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook"
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "<standard-base64-secret>"
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true"
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt"
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true"
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt"
# GOTRUE_HOOK_SEND_SMS_ENABLED: "false"
# GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook"
# GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
# GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false"
# GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender"
# GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
rest:
container_name: supabase-rest
image: postgrest/postgrest:v12.2.0
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
restart: unless-stopped
environment:
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}
command: "postgrest"
realtime:
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
container_name: realtime-dev.supabase-realtime
image: supabase/realtime:v2.30.34
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"curl",
"-sSfL",
"--head",
"-o",
"/dev/null",
"-H",
"Authorization: Bearer ${ANON_KEY}",
"http://localhost:4000/api/tenants/realtime-dev/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
PORT: 4000
DB_HOST: ${POSTGRES_HOST}
DB_PORT: ${POSTGRES_PORT}
DB_USER: supabase_admin
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_NAME: ${POSTGRES_DB}
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: ${JWT_SECRET}
SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
ERL_AFLAGS: -proto_dist inet_tcp
DNS_NODES: "''"
RLIMIT_NOFILE: "10000"
APP_NAME: realtime
SEED_SELF_HOST: true
# To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up
storage:
container_name: supabase-storage
image: supabase/storage-api:v1.11.13
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:5000/status"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
ANON_KEY: ${ANON_KEY}
SERVICE_KEY: ${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
FILE_SIZE_LIMIT: 52428800
STORAGE_BACKEND: file
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
TENANT_ID: stub
# TODO: https://github.com/supabase/storage-api/issues/55
REGION: stub
GLOBAL_S3_BUCKET: stub
ENABLE_IMAGE_TRANSFORMATION: "true"
IMGPROXY_URL: http://imgproxy:5001
volumes:
- ./volumes/storage:/var/lib/storage:z
imgproxy:
container_name: supabase-imgproxy
image: darthsim/imgproxy:v3.8.0
healthcheck:
test: [ "CMD", "imgproxy", "health" ]
timeout: 5s
interval: 5s
retries: 3
environment:
IMGPROXY_BIND: ":5001"
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
IMGPROXY_USE_ETAG: "true"
IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION}
volumes:
- ./volumes/storage:/var/lib/storage:z
meta:
container_name: supabase-meta
image: supabase/postgres-meta:v0.84.2
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
restart: unless-stopped
environment:
PG_META_PORT: 8080
PG_META_DB_HOST: ${POSTGRES_HOST}
PG_META_DB_PORT: ${POSTGRES_PORT}
PG_META_DB_NAME: ${POSTGRES_DB}
PG_META_DB_USER: supabase_admin
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
functions:
container_name: supabase-edge-functions
image: supabase/edge-runtime:v1.62.2
restart: unless-stopped
depends_on:
analytics:
condition: service_healthy
environment:
JWT_SECRET: ${JWT_SECRET}
SUPABASE_URL: http://kong:8000
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
# TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786
VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}"
volumes:
- ./volumes/functions:/home/deno/functions:Z
command:
- start
- --main-service
- /home/deno/functions/main
analytics:
container_name: supabase-analytics
image: supabase/logflare:1.4.0
healthcheck:
test: [ "CMD", "curl", "http://localhost:4000/health" ]
timeout: 5s
interval: 5s
retries: 10
restart: unless-stopped
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
# Uncomment to use Big Query backend for analytics
# volumes:
# - type: bind
# source: ${PWD}/gcloud.json
# target: /opt/app/rel/logflare/bin/gcloud.json
# read_only: true
environment:
LOGFLARE_NODE_HOST: 127.0.0.1
DB_USERNAME: supabase_admin
DB_DATABASE: _supabase
DB_HOSTNAME: ${POSTGRES_HOST}
DB_PORT: ${POSTGRES_PORT}
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_SCHEMA: _analytics
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
LOGFLARE_SINGLE_TENANT: true
LOGFLARE_SUPABASE_MODE: true
LOGFLARE_MIN_CLUSTER_SIZE: 1
# Comment variables to use Big Query backend for analytics
POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase
POSTGRES_BACKEND_SCHEMA: _analytics
LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
# Uncomment to use Big Query backend for analytics
# GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
# GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER}
ports:
- 4000:4000
# Comment out everything below this point if you are using an external Postgres database
db:
container_name: supabase-db
image: supabase/postgres:15.6.1.139
healthcheck:
test: pg_isready -U postgres -h localhost
interval: 5s
timeout: 5s
retries: 10
depends_on:
vector:
condition: service_healthy
command:
- postgres
- -c
- config_file=/etc/postgresql/postgresql.conf
- -c
- log_min_messages=fatal # prevents Realtime polling queries from appearing in logs
restart: unless-stopped
environment:
POSTGRES_HOST: /var/run/postgresql
PGPORT: ${POSTGRES_PORT}
POSTGRES_PORT: ${POSTGRES_PORT}
PGPASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PGDATABASE: ${POSTGRES_DB}
POSTGRES_DB: ${POSTGRES_DB}
JWT_SECRET: ${JWT_SECRET}
JWT_EXP: ${JWT_EXPIRY}
volumes:
- ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
# Must be superuser to create event trigger
- ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
# Must be superuser to alter reserved role
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
# Initialize the database settings with JWT_SECRET and JWT_EXP
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
# PGDATA directory is persisted between restarts
- ./volumes/db/data:/var/lib/postgresql/data:Z
# Changes required for internal supabase data such as _analytics
- ./volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z
# Changes required for Analytics support
- ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
# Changes required for Pooler support
- ./volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z
# Use named volume to persist pgsodium decryption key between restarts
- db-config:/etc/postgresql-custom
vector:
container_name: supabase-vector
image: timberio/vector:0.28.1-alpine
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://vector:9001/health"
]
timeout: 5s
interval: 5s
retries: 3
volumes:
- ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro
environment:
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
command: [ "--config", "etc/vector/vector.yml" ]
# Update the DATABASE_URL if you are using an external Postgres database
supavisor:
container_name: supabase-pooler
image: supabase/supavisor:1.1.56
healthcheck:
test: curl -sSfL --head -o /dev/null "http://127.0.0.1:4000/api/health"
interval: 10s
timeout: 5s
retries: 5
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
command:
- /bin/sh
- -c
- /app/bin/migrate && /app/bin/supavisor eval "$$(cat /etc/pooler/pooler.exs)" && /app/bin/server
dashboard:
container_name: mensa-dashboard
image: registry.gitlab.com/brausjonas/mensaappdashboard:0.3
restart: unless-stopped
ports:
- ${POSTGRES_PORT}:5432
- ${POOLER_PROXY_PORT_TRANSACTION}:6543
environment:
- PORT=4000
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DATABASE_URL=ecto://postgres:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase
- CLUSTER_POSTGRES=true
- SECRET_KEY_BASE=UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
- VAULT_ENC_KEY=your-encryption-key-32-chars-min
- API_JWT_SECRET=${JWT_SECRET}
- METRICS_JWT_SECRET=${JWT_SECRET}
- REGION=local
- ERL_AFLAGS=-proto_dist inet_tcp
- POOLER_TENANT_ID=${POOLER_TENANT_ID}
- POOLER_DEFAULT_POOL_SIZE=${POOLER_DEFAULT_POOL_SIZE}
- POOLER_MAX_CLIENT_CONN=${POOLER_MAX_CLIENT_CONN}
- POOLER_POOL_MODE=transaction
volumes:
- ./volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro
volumes:
db-config:
- 8051:8051
+10 -2
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:mensaappbackend/providers/pageProvider.dart';
import 'package:mensaappbackend/providers/timeProvider.dart';
import 'package:mensaappbackend/providers/viewProvider.dart';
import 'package:provider/provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
@@ -7,9 +8,13 @@ import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
String currentURL = Uri.base.toString();
List<String> url_parts = currentURL.split(":");
currentURL = "${url_parts[0]}:${url_parts[1]}:8000";
await Supabase.initialize(
url: "http://192.168.188.29:8000",
anonKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE"
url: "https://ieyivhvzkecsblqaylsd.supabase.co/",
anonKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlleWl2aHZ6a2Vjc2JscWF5bHNkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDEwMTUzMTYsImV4cCI6MjA1NjU5MTMxNn0.hf18LiI2zZRTypUCkm-Ksbj3y1krGyVIw5OzUbgCG0Y"
);
runApp(const MyApp());
@@ -28,6 +33,9 @@ class MyApp extends StatelessWidget {
),
ChangeNotifierProvider(
create: (context) => ViewProvider(),
),
ChangeNotifierProvider(
create: (context) => TimeProvider(),
)
],
child: MaterialApp(
+164
View File
@@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:mensaappbackend/modules/ratingCard.dart';
import 'package:mensaappbackend/providers/timeProvider.dart';
import 'package:provider/provider.dart';
class BuilderAverage {
static Widget build(var context, var snapshot, var ratingNames) {
var data = snapshot.data;
Map<String, Map> dic = {
"page1": {},
"page2": {"q1": 0, "q2": 0, "avg_q1": 0, "avg_q2": 0, "counter": 0},
"page3": {"q1": 0, "q2": 0, "avg_q1": 0, "avg_q2": 0, "counter": 0}
};
for (Map bewertung in data) {
for (var page in bewertung["ratings"].keys) {
var tmp_dic = bewertung["ratings"][page];
switch (page) {
case "page1":
if (tmp_dic["food"].length == 0) {
break;
}
var curKey = tmp_dic["food"].split(": ")[1];
if (!dic["page1"]!.containsKey(curKey)) {
dic["page1"]![curKey] = {
"q1": 0,
"q2": 0,
"avg_q1": 0,
"avg_q2": 0,
"counter": 0
};
}
dic["page1"]![curKey]["q1"] += tmp_dic["q1"];
dic["page1"]![curKey]["q2"] += tmp_dic["q2"];
dic["page1"]![curKey]["counter"] += tmp_dic["q1"] == 0 ? 0 : 1;
break;
case "page2":
dic["page2"]!["q1"] += tmp_dic["q1"];
dic["page2"]!["q2"] += tmp_dic["q2"];
dic["page2"]!["counter"] += tmp_dic["q1"] == 0 ? 0 : 1;
break;
case "page3":
dic["page3"]!["q1"] += tmp_dic["q1"];
dic["page3"]!["q2"] += tmp_dic["q2"];
dic["page3"]!["counter"] += tmp_dic["q1"] == 0 ? 0 : 1;
break;
}
}
}
Map finalDic = {};
if (dic["page2"]!["q1"] != 0) {
finalDic["Service"] = {"org": "page2", "q1": 0, "q2": 0};
finalDic["Service"]!["q1"] = (dic["page2"]!["q1"] / dic["page2"]!["counter"]).round().toInt();
finalDic["Service"]!["q2"] = (dic["page2"]!["q2"] / dic["page2"]!["counter"]).round().toInt();
}
if (dic["page3"]!["q2"] != 0) {
finalDic["Essenswünsche"] = {"org": "page3", "q1": 0, "q2": 0};
finalDic["Essenswünsche"]!["q1"] = (dic["page3"]!["q1"] / dic["page3"]!["counter"]).round().toInt();
finalDic["Essenswünsche"]!["q2"] = (dic["page3"]!["q2"] / dic["page3"]!["counter"]).round().toInt();
}
for (var food in dic["page1"]!.keys) {
var foodData = dic["page1"]![food];
finalDic[food] = {"q1": 0, "q2": 0, "org": "page1"};
finalDic[food]["q1"] = (foodData["q1"] / foodData["counter"]).round().toInt();
finalDic[food]?["q2"] = (foodData["q2"] / foodData["counter"]).round().toInt();
}
List<Widget> cards = [];
if (data == null) {
return const Center();
}
for (var title in finalDic.keys) {
List<Widget> children = [];
var rating = finalDic[title];
if (rating["q1"] != 0) {
Widget card = RatingCard(
rating1: rating["q1"],
rating2: rating["q2"],
text: "",
title: title,
rating1Title: ratingNames[rating["org"]]!["q1"]!,
rating2Title: ratingNames[rating["org"]]!["q2"]!,
);
children.add(card);
children.add(const SizedBox(
width: 10,
));
}
cards.add(Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.8),
spreadRadius: 0,
blurRadius: 0,
offset: const Offset(0, 0))
],
borderRadius: BorderRadius.circular(15),
color: Theme.of(context).colorScheme.surface.withOpacity(0.8)),
child: Column(
children: [
Wrap(
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 10,
children: children,
)
],
),
),
const SizedBox(
height: 15,
),
],
));
}
return Center(
child: IntrinsicWidth(
stepWidth: 2,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
Consumer<TimeProvider>(
builder: (context, timeProvider, child) {
// Dynamischer Text basierend auf TimeProvider
String text =
timeProvider.timeList[timeProvider.selectedTime]!;
return Text(
text + " - Durchschnitt",
style: const TextStyle(fontSize: 25),
);
},
),
const SizedBox(
height: 20,
),
for (var card in cards) card,
],
),
),
),
);
}
}
+119
View File
@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:mensaappbackend/modules/ratingCard.dart';
import 'package:mensaappbackend/providers/timeProvider.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl_browser.dart';
class BuilderToday {
static Widget build(var context, var snapshot, var ratingNames) {
var data = snapshot.data;
List<Widget> cards = [];
if (data == null) {
return const Center();
}
var i = 0;
for (var map in data) {
var ratings = map["ratings"];
var createdAt = map["created_at"];
// Datum in ein lesbares Format umwandeln
var formattedDate = DateFormat('dd.MM.yyyy').format(DateTime.parse(createdAt));
List<Widget> children = [];
for (var key in ratings.keys) {
var rating = ratings[key];
if (rating["q1"] != 0) {
Widget card = RatingCard(
rating1: rating["q1"],
rating2: rating["q2"],
text: rating["t1"],
title: "$formattedDate\n${ratingNames[key]!["title"]! +
(key == "page1" ? "${rating["food"]}" : "")}",
rating1Title: ratingNames[key]!["q1"]!,
rating2Title: ratingNames[key]!["q2"]!,
);
children.add(card);
children.add(const SizedBox(
width: 10,
));
}
}
cards.add(Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.8),
spreadRadius: 0,
blurRadius: 0,
offset: const Offset(0, 0))
],
borderRadius: BorderRadius.circular(15),
color: Theme.of(context).colorScheme.surface.withOpacity(0.8)),
child: Column(
children: [
Text(
"Bewertung $i",
style: const TextStyle(fontSize: 20),
),
const SizedBox(
height: 10,
),
Wrap(
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 10,
children: children,
)
],
),
),
const SizedBox(
height: 15,
),
],
));
i++;
}
return Center(
child: IntrinsicWidth(
stepWidth: 2,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
Consumer<TimeProvider>(
builder: (context, timeProvider, child) {
// Dynamischer Text basierend auf TimeProvider
String text = timeProvider.timeList[timeProvider.selectedTime]!;
return Text(
text + " - Alle",
style: const TextStyle(fontSize: 25),
);
},
),
const SizedBox(
height: 20,
),
for (var card in cards) card,
],
),
),
),
);
}
}
-96
View File
@@ -1,96 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mensaappbackend/modules/ratingCard.dart';
class BuilderToday {
static Widget build (var context, var snapshot, var ratingNames) {
var data = snapshot.data;
List<Widget> cards = [];
if (data == null) {
return const Center();
}
var i = 0;
for (var map in data) {
var ratings = map["ratings"];
List<Widget> children = [
];
for (var key in ratings.keys) {
var rating = ratings[key];
if (rating["q1"] != 0) {
Widget card = RatingCard(
rating1: rating["q1"],
rating2: rating["q2"],
text: rating["t1"],
title: ratingNames[key]!["title"]! + (key == "page1" ? "${rating["food"]}" : ""),
rating1Title: ratingNames[key]!["q1"]!,
rating2Title: ratingNames[key]!["q2"]!,
);
children.add(card);
children.add(const SizedBox(
width: 10,
));
}
}
cards.add(Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3))
],
borderRadius: BorderRadius.circular(15),
color: Theme.of(context).colorScheme.surface.withOpacity(0.8)),
child: Column(
children: [
Text("Bewertung $i", style: const TextStyle(fontSize: 20),),
const SizedBox(height: 10,),
Wrap(
runSpacing: 10,
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.center,
children: children,
)
],
),
),
const SizedBox(
height: 15,
),
],
));
i++;
}
return SingleChildScrollView(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
const Text(
"Heute - Alle",
style: TextStyle(fontSize: 25),
),
const SizedBox(
height: 20,
),
for (var card in cards) card
],
),
),
);
}
}
-138
View File
@@ -1,138 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mensaappbackend/modules/ratingCard.dart';
class BuilderTodayAverage {
static Widget _getRatingWidget(
var q1, var q2, var page, var essen, var ratingNames, var context) {
Widget card = Column(
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3))
],
borderRadius: BorderRadius.circular(15),
color: Theme.of(context).colorScheme.surface.withOpacity(0.8)),
child: RatingCard(
rating1: q1,
rating2: q2,
title: ratingNames[page]["title"] + (essen == -1 ? "" : "$essen"),
rating1Title: ratingNames[page]["q1"],
rating2Title: ratingNames[page]["q2"],
text: "",
),
),
const SizedBox(
height: 20,
)
],
);
if (q1 != 0) {
return card;
} else {
return Container();
}
}
static Widget build(var context, var snapshot, var ratingNames) {
var data = snapshot.data;
if (data == null) {
return const Center();
}
var avgP1F1 = [0.0, 0.0, 0];
var avgP1F2 = [0.0, 0.0, 0];
var avgP1F3 = [0.0, 0.0, 0];
var avgP2 = [0.0, 0.0, 0];
var avgP3 = [0.0, 0.0, 0];
var foodIdx = 1;
var foodNameMap = {};
var foodMap = {1: avgP1F1, 2: avgP1F2, 3: avgP1F3};
for (var map in data) {
var ratings = map["ratings"];
ratings["page1"]["food"] = ratings["page1"]["food"].toString();
print(ratings["page1"]["food"]);
ratings["page1"]["food"] = ratings["page1"]["food"];
if (!foodNameMap.containsKey(ratings["page1"]["food"])) {
foodNameMap[ratings["page1"]["food"]] = foodIdx;
foodIdx++;
}
var food = foodNameMap[ratings["page1"]["food"]];
var page1 = ratings["page1"],
page2 = ratings["page2"],
page3 = ratings["page3"];
foodMap[food]?[0] += page1["q1"];
foodMap[food]?[1] += page1["q2"];
foodMap[food]?[2] += page1["q1"] == 0 ? 0 : 1;
avgP2[0] += page2["q1"];
avgP2[1] += page2["q2"];
avgP2[2] += page2["q1"] == 0 ? 0 : 1;
avgP3[0] += page3["q1"];
avgP3[1] += page3["q2"];
avgP3[2] += page3["q1"] == 0 ? 0 : 1;
}
for (var i in [1, 2, 3]) {
foodMap[i]?[0] =
(foodMap[i]![0] / (foodMap[i]![2] == 0 ? 1 : foodMap[i]![2])).toInt();
foodMap[i]?[1] =
(foodMap[i]![1] / (foodMap[i]![2] == 0 ? 1 : foodMap[i]![2])).toInt();
}
avgP2[0] = (avgP2[0] / avgP2[2] == 0 ? 1 : avgP2[2]).toInt();
avgP2[1] = (avgP2[1] / avgP2[2] == 0 ? 1 : avgP2[2]).toInt();
avgP3[0] = (avgP3[0] / avgP3[2] == 0 ? 1 : avgP3[2]).toInt();
avgP3[1] = (avgP3[1] / avgP3[2] == 0 ? 1 : avgP3[2]).toInt();
return SingleChildScrollView(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
const Text(
"Heute - Durchschnitt",
style: TextStyle(fontSize: 25),
),
const SizedBox(
height: 20,
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_getRatingWidget(avgP1F1[0], avgP1F1[1], "page1",
foodNameMap.keys.elementAtOrNull(0), ratingNames, context),
_getRatingWidget(avgP1F2[0], avgP1F2[1], "page1",
foodNameMap.keys.elementAtOrNull(1), ratingNames, context),
_getRatingWidget(avgP1F3[0], avgP1F3[1], "page1",
foodNameMap.keys.elementAtOrNull(2), ratingNames, context),
_getRatingWidget(
avgP2[0], avgP2[1], "page2", -1, ratingNames, context),
_getRatingWidget(
avgP3[0], avgP3[1], "page3", -1, ratingNames, context)
],
)
],
),
),
);
}
}
+2 -1
View File
@@ -22,6 +22,7 @@ class RatingCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(10),
@@ -31,7 +32,7 @@ class RatingCard extends StatelessWidget {
),
child: Column(
children: [
Text(title, style: TextStyle(fontSize: 22),),
Text(title, style: TextStyle(fontSize: 22), textAlign: TextAlign.center,),
SizedBox(height: 10,),
Text("$rating1Title:", style: TextStyle(fontSize: 18),),
RatingStars(initRating: rating1.toDouble()),
+86 -19
View File
@@ -2,48 +2,112 @@ import 'package:flutter/material.dart';
import 'package:mensaappbackend/modules/ratingCard.dart';
import 'package:mensaappbackend/providers/pageProvider.dart';
import 'package:mensaappbackend/providers/viewProvider.dart';
import 'package:mensaappbackend/providers/timeProvider.dart';
import 'package:provider/provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'dart:html' as html;
class PageDashboard extends StatelessWidget {
const PageDashboard({super.key});
Future<List<dynamic>> requestSupabaseToday() async {
bool isMobileBrowser() {
String userAgent = html.window.navigator.userAgent;
return userAgent.contains('Mobile') ||
userAgent.contains('Android') ||
userAgent.contains('iPhone') ||
userAgent.contains('iPad');
}
Future<List<dynamic>> requestSupabaseTimeSpan(int timeId) async {
var supabase = Supabase.instance.client;
var now = DateTime.now();
var morning = DateTime(now.year, now.month, now.day, 0, 1);
var result = await supabase.from("ratings_database").select("ratings").gte("created_at", morning);
var nowMorning = DateTime(now.year, now.month, now.day, 0, 1);
var morningDict = {
1: nowMorning,
2: nowMorning.subtract(const Duration(days: 7)),
3: nowMorning.subtract(const Duration(days: 30)),
4: nowMorning.subtract(const Duration(days: 365)),
};
var result = await supabase.from("ratings_database").select("ratings, created_at").gte("created_at", morningDict[timeId]!);
return result;
}
final ratingNames = const {
"page1": {"title": "", "q1": "Geschmack", "q2": "Qualität"},
"page2": {"title": "Service", "q1": "Undefined", "q2": "Undefined"},
"page3": {"title": "Wünsche", "q1": "Undefined", "q2": "Undefined"}
"page2": {"title": "Service", "q1": "Freundlichkeit", "q2": "Sauberkeit"},
"page3": {"title": "Wünsche", "q1": "Vielfalt", "q2": "Vegan"}
};
@override
Widget build(BuildContext context) {
var viewDatabaseFunctions = {
0: requestSupabaseToday,
1: requestSupabaseToday,
};
bool browser_mobile = isMobileBrowser();
String dashboard_text = browser_mobile ? "" : "Dashboard";
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary,
title: Text(
"Dashboard",
style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
title: Consumer<TimeProvider>(
builder: (context, timeProvider, child) {
return Row(
children: [
Text(
dashboard_text,
style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
),
SizedBox(width: browser_mobile ? 0 : 32),
Icon(Icons.watch_later, ),
SizedBox(width: 8),
SizedBox(
width: 125,
child: Center( // Add Center widget here
child: DropdownButton(
value: Provider.of<TimeProvider>(context, listen: false).selectedTime,
dropdownColor: Theme.of(context).colorScheme.secondary,
//style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
iconEnabledColor: Theme.of(context).colorScheme.onPrimary,
focusColor: Colors.transparent,
isDense: true,
isExpanded: true,
items: [
for (var key in Provider.of<TimeProvider>(context, listen: false).timeList.entries)
DropdownMenuItem(
value: key.key,
child: Center(
child: Text(key.value, style: TextStyle(color: Theme.of(context).colorScheme.onSecondary)),
),
),
],
selectedItemBuilder: (BuildContext context) {
return [
for (var key in Provider.of<TimeProvider>(context, listen: false).timeList.entries)
Center(
child: Text(
key.value,
style: TextStyle(color: Theme.of(context).colorScheme.onPrimary), // Benutzerdefinierte Farbe für angezeigten Text
),
),
];
},
onChanged: (value) {
Provider.of<TimeProvider>(context, listen: false).setSelectedTime(value);
},
),
),
),
],
);
},
),
centerTitle: false,
actions: [
TextButton.icon(
onPressed: () {
var supabase = Supabase.instance.client;
supabase.auth.signOut();
Provider.of<PageProvider>(context, listen: false)
.setSelectedPage(0);
Provider.of<PageProvider>(context, listen: false).setSelectedPage(0);
},
label: Text(
"Logout",
@@ -53,11 +117,14 @@ class PageDashboard extends StatelessWidget {
Icons.logout,
color: Theme.of(context).colorScheme.onPrimary,
),
)
),
],
),
body: FutureBuilder(
future: viewDatabaseFunctions[Provider.of<ViewProvider>(context, listen: false).selectedView]!(),
future: requestSupabaseTimeSpan(
Provider.of<TimeProvider>(context).selectedTime
),
builder: (context, snapshot) {
return Consumer<ViewProvider> (
builder: (context, viewProvider, child) {
@@ -77,7 +144,7 @@ class PageDashboard extends StatelessWidget {
FilledButton.icon(
style: const ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.symmetric(vertical: 20))),
icon: const Icon(Icons.today),
label: const Text("Alle - Heute"),
label: const Text("Alle"),
onPressed: () {
Provider.of<ViewProvider>(context, listen: false).setSelectedView(0);
Navigator.pop(context);
@@ -87,7 +154,7 @@ class PageDashboard extends StatelessWidget {
FilledButton.icon(
style: const ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.symmetric(vertical: 20))),
icon: const Icon(Icons.today),
label: const Text("Durchschnitt - Heute"),
label: const Text("Durchschnitt"),
onPressed: () {
Provider.of<ViewProvider>(context, listen: false).setSelectedView(1);
Navigator.pop(context);
+20
View File
@@ -0,0 +1,20 @@
import 'package:flutter/cupertino.dart';
import 'package:mensaappbackend/pages/pageDashboard.dart';
import 'package:mensaappbackend/pages/pageLogin.dart';
import 'package:mensaappbackend/pages/wrongLogin.dart';
class TimeProvider extends ChangeNotifier {
var selectedTime = 1;
Map<int, String> timeList = {
1: "Heute",
2: "7 Tage",
3: "30 Tage",
4: "365 Tage"
};
void setSelectedTime(var index) {
selectedTime = index;
notifyListeners();
print(selectedTime);
}
}
+4 -3
View File
@@ -1,13 +1,14 @@
import 'package:flutter/cupertino.dart';
import 'package:mensaappbackend/modules/builderToday.dart';
import 'package:mensaappbackend/modules/builderTodayAverage.dart';
import 'package:mensaappbackend/modules/builderNormal.dart';
import 'package:mensaappbackend/modules/builderAverage.dart';
class ViewProvider extends ChangeNotifier {
var selectedView = 0;
var views = {
0: (context, snapshot, ratingNames) => BuilderToday.build(context, snapshot, ratingNames),
1: (context, snapshot, ratingNames) => BuilderTodayAverage.build(context, snapshot, ratingNames)
1: (context, snapshot, ratingNames) => BuilderAverage.build(context, snapshot, ratingNames)
};
void setSelectedView(var index) {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]},{"family":"packages/font_awesome_flutter/FontAwesomeBrands","fonts":[{"weight":400,"asset":"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"}]},{"family":"packages/font_awesome_flutter/FontAwesomeRegular","fonts":[{"weight":400,"asset":"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"}]},{"family":"packages/font_awesome_flutter/FontAwesomeSolid","fonts":[{"weight":900,"asset":"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"}]}]
+36255
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Some files were not shown because too many files have changed in this diff Show More