diff --git a/appwrite/compose.env b/appwrite/compose.env new file mode 100644 index 0000000..f0a3fdf --- /dev/null +++ b/appwrite/compose.env @@ -0,0 +1,138 @@ +_APP_ENV=production +_APP_LOCALE=en +_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_FORCE_HTTPS=disabled +_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled +_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled +_APP_OPTIONS_ROUTER_PROTECTION=disabled +_APP_OPENSSL_KEY_V1=7b9983b6b031a2291255fcb9e6d1eb85 +_APP_DOMAIN=localhost +_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com +_APP_DOMAIN_FUNCTIONS=functions.localhost +_APP_DOMAIN_SITES=sites.localhost +_APP_DOMAIN_TARGET=localhost +_APP_DOMAIN_TARGET_CNAME=localhost +_APP_DOMAIN_TARGET_AAAA=::1 +_APP_DOMAIN_TARGET_A=127.0.0.1 +_APP_CONSOLE_WHITELIST_ROOT=enabled +_APP_CONSOLE_WHITELIST_EMAILS= +_APP_CONSOLE_WHITELIST_IPS= +_APP_CONSOLE_HOSTNAMES= +_APP_SYSTEM_EMAIL_NAME=Appwrite +_APP_SYSTEM_EMAIL_ADDRESS=noreply@delmar.bzh +_APP_SYSTEM_TEAM_EMAIL=team@delmar.bzh +_APP_SYSTEM_RESPONSE_FORMAT= +_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@delmar.bzh +_APP_EMAIL_SECURITY= +_APP_EMAIL_CERTIFICATES=admin@delmar.bzh +_APP_USAGE_STATS=enabled +_APP_LOGGING_PROVIDER= +_APP_LOGGING_CONFIG= +_APP_USAGE_AGGREGATION_INTERVAL=30 +_APP_USAGE_TIMESERIES_INTERVAL=30 +_APP_USAGE_DATABASE_INTERVAL=900 +_APP_WORKER_PER_CORE=6 +_APP_CONSOLE_SESSION_ALERTS=disabled +_APP_COMPRESSION_ENABLED=enabled +_APP_COMPRESSION_MIN_SIZE_BYTES=1024 +_APP_REDIS_HOST=redis +_APP_REDIS_PORT=6379 +_APP_REDIS_USER= +_APP_REDIS_PASS= +_APP_DB_HOST=mariadb +_APP_DB_PORT=3306 +_APP_DB_SCHEMA=appwrite +_APP_DB_USER=user +_APP_DB_PASS=password +_APP_DB_ROOT_PASS=rootsecretpassword +_APP_INFLUXDB_HOST=influxdb +_APP_INFLUXDB_PORT=8086 +_APP_STATSD_HOST=telegraf +_APP_STATSD_PORT=8125 +_APP_SMTP_HOST=pro1.mail.ovh.net +_APP_SMTP_PORT=587 +_APP_SMTP_SECURE=tls +_APP_SMTP_USERNAME=admin@delmar.bzh +_APP_SMTP_PASSWORD=sxS4GA8rBfmFkCFL +_APP_SMS_PROVIDER= +_APP_SMS_FROM= +_APP_STORAGE_LIMIT=30000000 +_APP_STORAGE_PREVIEW_LIMIT=20000000 +_APP_STORAGE_ANTIVIRUS=disabled +_APP_STORAGE_ANTIVIRUS_HOST=clamav +_APP_STORAGE_ANTIVIRUS_PORT=3310 +_APP_STORAGE_DEVICE=local +_APP_STORAGE_S3_ACCESS_KEY= +_APP_STORAGE_S3_SECRET= +_APP_STORAGE_S3_REGION=us-east-1 +_APP_STORAGE_S3_BUCKET= +_APP_STORAGE_S3_ENDPOINT= +_APP_STORAGE_DO_SPACES_ACCESS_KEY= +_APP_STORAGE_DO_SPACES_SECRET= +_APP_STORAGE_DO_SPACES_REGION=us-east-1 +_APP_STORAGE_DO_SPACES_BUCKET= +_APP_STORAGE_BACKBLAZE_ACCESS_KEY= +_APP_STORAGE_BACKBLAZE_SECRET= +_APP_STORAGE_BACKBLAZE_REGION=us-west-004 +_APP_STORAGE_BACKBLAZE_BUCKET= +_APP_STORAGE_LINODE_ACCESS_KEY= +_APP_STORAGE_LINODE_SECRET= +_APP_STORAGE_LINODE_REGION=eu-central-1 +_APP_STORAGE_LINODE_BUCKET= +_APP_STORAGE_WASABI_ACCESS_KEY= +_APP_STORAGE_WASABI_SECRET= +_APP_STORAGE_WASABI_REGION=eu-central-1 +_APP_STORAGE_WASABI_BUCKET= +_APP_FUNCTIONS_SIZE_LIMIT=30000000 +_APP_COMPUTE_SIZE_LIMIT=30000000 +_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000 +_APP_FUNCTIONS_TIMEOUT=900 +_APP_FUNCTIONS_BUILD_TIMEOUT=900 +_APP_COMPUTE_BUILD_TIMEOUT=900 +_APP_FUNCTIONS_CONTAINERS=10 +_APP_FUNCTIONS_CPUS=0 +_APP_COMPUTE_CPUS=0 +_APP_FUNCTIONS_MEMORY=0 +_APP_COMPUTE_MEMORY=0 +_APP_FUNCTIONS_MEMORY_SWAP=0 +_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0 +_APP_EXECUTOR_SECRET=your-secret-key +_APP_EXECUTOR_HOST=http://exc1/v1 +_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes +_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0 +_APP_FUNCTIONS_INACTIVE_THRESHOLD=60 +_APP_COMPUTE_INACTIVE_THRESHOLD=60 +DOCKERHUB_PULL_USERNAME= +DOCKERHUB_PULL_PASSWORD= +DOCKERHUB_PULL_EMAIL= +OPEN_RUNTIMES_NETWORK=appwrite_runtimes +_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes +_APP_COMPUTE_RUNTIMES_NETWORK=runtimes +_APP_DOCKER_HUB_USERNAME= +_APP_DOCKER_HUB_PASSWORD= +_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600 +_APP_COMPUTE_MAINTENANCE_INTERVAL=3600 +_APP_SITES_TIMEOUT=900 +_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29 +_APP_VCS_GITHUB_APP_NAME= +_APP_VCS_GITHUB_PRIVATE_KEY= +_APP_VCS_GITHUB_APP_ID= +_APP_VCS_GITHUB_CLIENT_ID= +_APP_VCS_GITHUB_CLIENT_SECRET= +_APP_VCS_GITHUB_WEBHOOK_SECRET= +_APP_MAINTENANCE_INTERVAL=86400 +_APP_MAINTENANCE_DELAY=0 +_APP_MAINTENANCE_START_TIME=00:00 +_APP_MAINTENANCE_RETENTION_CACHE=2592000 +_APP_MAINTENANCE_RETENTION_EXECUTION=1209600 +_APP_MAINTENANCE_RETENTION_AUDIT=1209600 +_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800 +_APP_MAINTENANCE_RETENTION_ABUSE=86400 +_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 +_APP_MAINTENANCE_RETENTION_SCHEDULES=86400 +_APP_GRAPHQL_MAX_BATCH_SIZE=10 +_APP_GRAPHQL_MAX_COMPLEXITY=250 +_APP_GRAPHQL_MAX_DEPTH=3 +_APP_MIGRATIONS_FIREBASE_CLIENT_ID= +_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET= +_APP_ASSISTANT_OPENAI_API_KEY= diff --git a/appwrite/docker-compose.yaml b/appwrite/docker-compose.yaml new file mode 100644 index 0000000..09e8e18 --- /dev/null +++ b/appwrite/docker-compose.yaml @@ -0,0 +1,965 @@ +x-logging: &x-logging + logging: + driver: 'json-file' + options: + max-file: '5' + max-size: '10m' +services: + traefik: + image: traefik:2.11 + container_name: appwrite-traefik + <<: *x-logging + command: + - --providers.file.directory=/storage/config + - --providers.file.watch=true + - --providers.docker=true + - --providers.docker.exposedByDefault=false + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`) + - --entrypoints.appwrite_web.address=:80 + - --entrypoints.appwrite_websecure.address=:443 + restart: unless-stopped + ports: + - 50154:80 + - 50155:443 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-config:/storage/config:ro + - appwrite-certificates:/storage/certificates:ro + depends_on: + - appwrite + networks: + - gateway + - appwrite + + appwrite: + image: appwrite/appwrite:1.7.3 + container_name: appwrite + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + labels: + - traefik.enable=true + - traefik.constraint-label-stack=appwrite + - traefik.docker.network=appwrite + - traefik.http.services.appwrite_api.loadbalancer.server.port=80 + #http + - traefik.http.routers.appwrite_api_http.entrypoints=appwrite_web + - traefik.http.routers.appwrite_api_http.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite_api_http.service=appwrite_api + # https + - traefik.http.routers.appwrite_api_https.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_api_https.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite_api_https.service=appwrite_api + - traefik.http.routers.appwrite_api_https.tls=true + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-imports:/storage/imports:rw + - appwrite-cache:/storage/cache:rw + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + - appwrite-builds:/storage/builds:rw + depends_on: + - mariadb + - redis +# - clamav + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_LOCALE + - _APP_COMPRESSION_MIN_SIZE_BYTES + - _APP_CONSOLE_WHITELIST_ROOT + - _APP_CONSOLE_WHITELIST_EMAILS + - _APP_CONSOLE_SESSION_ALERTS + - _APP_CONSOLE_WHITELIST_IPS + - _APP_CONSOLE_HOSTNAMES + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_EMAIL_SECURITY + - _APP_SYSTEM_RESPONSE_FORMAT + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_ROUTER_FORCE_HTTPS + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_FUNCTIONS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_USAGE_STATS + - _APP_STORAGE_LIMIT + - _APP_STORAGE_PREVIEW_LIMIT + - _APP_STORAGE_ANTIVIRUS + - _APP_STORAGE_ANTIVIRUS_HOST + - _APP_STORAGE_ANTIVIRUS_PORT + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_COMPUTE_SIZE_LIMIT + - _APP_FUNCTIONS_TIMEOUT + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY + - _APP_FUNCTIONS_RUNTIMES + - _APP_SITES_RUNTIMES + - _APP_DOMAIN_SITES + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_LOGGING_CONFIG + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_DELAY + - _APP_MAINTENANCE_START_TIME + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_SMS_PROVIDER + - _APP_SMS_FROM + - _APP_GRAPHQL_MAX_BATCH_SIZE + - _APP_GRAPHQL_MAX_COMPLEXITY + - _APP_GRAPHQL_MAX_DEPTH + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_VCS_GITHUB_WEBHOOK_SECRET + - _APP_VCS_GITHUB_CLIENT_SECRET + - _APP_VCS_GITHUB_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_ASSISTANT_OPENAI_API_KEY + appwrite-console: + <<: *x-logging + container_name: appwrite-console + image: appwrite/console:6.0.11 + restart: unless-stopped + networks: + - appwrite + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + - "traefik.docker.network=appwrite" + - "traefik.http.services.appwrite_console.loadbalancer.server.port=80" + #ws + - traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web + - traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`) + - traefik.http.routers.appwrite_console_http.service=appwrite_console + # wss + - traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`) + - traefik.http.routers.appwrite_console_https.service=appwrite_console + - traefik.http.routers.appwrite_console_https.tls=true + + appwrite-realtime: + image: appwrite/appwrite:1.7.3 + entrypoint: realtime + container_name: appwrite-realtime + <<: *x-logging + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + - "traefik.docker.network=appwrite" + - "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80" + #ws + - traefik.http.routers.appwrite_realtime_ws.entrypoints=appwrite_web + - traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`) + - traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime + # wss + - traefik.http.routers.appwrite_realtime_wss.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) + - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime + - traefik.http.routers.appwrite_realtime_wss.tls=true + networks: + - appwrite + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + + appwrite-worker-audits: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-audits + <<: *x-logging + container_name: appwrite-worker-audits + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-webhooks: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-webhooks + <<: *x-logging + container_name: appwrite-worker-webhooks + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_EMAIL_SECURITY + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-deletes: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-deletes + <<: *x-logging + container_name: appwrite-worker-deletes + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + - appwrite-builds:/storage/builds:rw + - appwrite-certificates:/storage/certificates:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_LOGGING_CONFIG + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_EMAIL_CERTIFICATES + + appwrite-worker-databases: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-databases + <<: *x-logging + container_name: appwrite-worker-databases + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-builds: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-builds + <<: *x-logging + container_name: appwrite-worker-builds + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + volumes: + - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + - appwrite-builds:/storage/builds:rw + - appwrite-uploads:/storage/uploads:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_FUNCTIONS_TIMEOUT + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY + - _APP_COMPUTE_SIZE_LIMIT + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_ROUTER_FORCE_HTTPS + - _APP_DOMAIN + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_DOMAIN_SITES + + appwrite-worker-certificates: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-certificates + <<: *x-logging + container_name: appwrite-worker-certificates + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + volumes: + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_FUNCTIONS + - _APP_EMAIL_CERTIFICATES + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-functions: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-functions + <<: *x-logging + container_name: appwrite-worker-functions + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + - openruntimes-executor + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_FUNCTIONS_TIMEOUT + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_USAGE_STATS + - _APP_DOCKER_HUB_USERNAME + - _APP_DOCKER_HUB_PASSWORD + - _APP_LOGGING_CONFIG + + appwrite-worker-mails: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-mails + <<: *x-logging + container_name: appwrite-worker-mails + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_LOGGING_CONFIG + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS + + appwrite-worker-messaging: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-messaging + <<: *x-logging + container_name: appwrite-worker-messaging + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-uploads:/storage/uploads:rw + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_SMS_FROM + - _APP_SMS_PROVIDER + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-worker-migrations: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-migrations + <<: *x-logging + container_name: appwrite-worker-migrations + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-imports:/storage/imports:rw + depends_on: + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_EMAIL_SECURITY + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + + appwrite-task-maintenance: + image: appwrite/appwrite:1.7.3 + entrypoint: maintenance + <<: *x-logging + container_name: appwrite-task-maintenance + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_FUNCTIONS + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + + appwrite-task-stats-resources: + image: appwrite/appwrite:1.7.3 + container_name: appwrite-task-stats-resources + entrypoint: stats-resources + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-resources: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-stats-resources + container_name: appwrite-worker-stats-resources + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-usage: + image: appwrite/appwrite:1.7.3 + entrypoint: worker-stats-usage + container_name: appwrite-worker-stats-usage + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + + appwrite-task-scheduler-functions: + image: appwrite/appwrite:1.7.3 + entrypoint: schedule-functions + container_name: appwrite-task-scheduler-functions + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-task-scheduler-executions: + image: appwrite/appwrite:1.7.3 + entrypoint: schedule-executions + container_name: appwrite-task-scheduler-executions + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-task-scheduler-messages: + image: appwrite/appwrite:1.7.3 + entrypoint: schedule-messages + container_name: appwrite-task-scheduler-messages + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-assistant: + image: appwrite/assistant:0.4.0 + container_name: appwrite-assistant + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + environment: + - _APP_ASSISTANT_OPENAI_API_KEY + + appwrite-browser: + image: appwrite/browser:0.2.4 + container_name: appwrite-browser + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + + openruntimes-executor: + container_name: openruntimes-executor + hostname: exc1 + <<: *x-logging + restart: unless-stopped + stop_signal: SIGINT + image: openruntimes/executor:0.7.14 + networks: + - appwrite + - runtimes + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-builds:/storage/builds:rw + - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + # Host mount nessessary to share files between executor and runtimes. + # It's not possible to share mount file between 2 containers without host mount (copying is too slow) + - /tmp:/tmp:rw + environment: + - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD + - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL + - OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK + - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME + - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD + - OPR_EXECUTOR_ENV=$_APP_ENV + - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES + - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET + - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 + - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG + - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE + - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET + - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION + - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT + - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET + - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION + - OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=$_APP_STORAGE_DO_SPACES_BUCKET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=$_APP_STORAGE_BACKBLAZE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=$_APP_STORAGE_BACKBLAZE_SECRET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=$_APP_STORAGE_BACKBLAZE_REGION + - OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=$_APP_STORAGE_BACKBLAZE_BUCKET + - OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=$_APP_STORAGE_LINODE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_LINODE_SECRET=$_APP_STORAGE_LINODE_SECRET + - OPR_EXECUTOR_STORAGE_LINODE_REGION=$_APP_STORAGE_LINODE_REGION + - OPR_EXECUTOR_STORAGE_LINODE_BUCKET=$_APP_STORAGE_LINODE_BUCKET + - OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=$_APP_STORAGE_WASABI_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET + - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION + - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + + mariadb: + image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p + container_name: appwrite-mariadb + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-mariadb:/var/lib/mysql:rw + environment: + - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MYSQL_DATABASE=${_APP_DB_SCHEMA} + - MYSQL_USER=${_APP_DB_USER} + - MYSQL_PASSWORD=${_APP_DB_PASS} + - MARIADB_AUTO_UPGRADE=1 + command: 'mysqld --innodb-flush-method=fsync' + + redis: + image: redis:7.2.4-alpine + container_name: appwrite-redis + <<: *x-logging + restart: unless-stopped + command: > + redis-server + --maxmemory 512mb + --maxmemory-policy allkeys-lru + --maxmemory-samples 5 + networks: + - appwrite + volumes: + - appwrite-redis:/data:rw + + # clamav: + # image: appwrite/clamav:1.2.0 + # container_name: appwrite-clamav + # restart: unless-stopped + # networks: + # - appwrite + # volumes: + # - appwrite-uploads:/storage/uploads + +networks: + gateway: + name: gateway + appwrite: + name: appwrite + runtimes: + name: runtimes + +volumes: + appwrite-mariadb: + appwrite-redis: + appwrite-cache: + appwrite-uploads: + appwrite-imports: + appwrite-certificates: + appwrite-functions: + appwrite-sites: + appwrite-builds: + appwrite-config: diff --git a/arcane/agent-compose.yaml b/arcane/agent-compose.yaml new file mode 100644 index 0000000..06774f7 --- /dev/null +++ b/arcane/agent-compose.yaml @@ -0,0 +1,25 @@ +# carlo: fEpbsshKL67xntGZXvig9eKGtQPeYS8N +# gary: rbJ75M7coTZCxmocSQ9DDFhbRE5BzgB5 +# sandy: y2R7MyVcN2KgG7Dkjh8bZZP7YJEFgmAq +# krabs: D5RQ5andUN6mN9QViEzjidqkyDHSDMqv +# sheldon: nr4qbG58vppNuAEg648DYa2Xt45DmTYP +# bernie: ro4fTDt37QuCSkMEGLzNLcQNqapgKbjS +--- +name: arcane-agent + +volumes: + agent-data: {} + +services: + arcane-agent: + image: ghcr.io/ofkm/arcane-headless:latest + container_name: arcane-agent + ports: + - '3553:3553' + environment: + - AGENT_MODE=true + - AGENT_BOOTSTRAP_TOKEN=${TOKEN:-xxxxxxxxxxxxxxxxxxxx} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - agent-data:/app/data + restart: unless-stopped diff --git a/arcane/docker-compose.yaml b/arcane/docker-compose.yaml new file mode 100644 index 0000000..66e153c --- /dev/null +++ b/arcane/docker-compose.yaml @@ -0,0 +1,24 @@ +# bob (dkr.delmar.bzh) +--- +name: arcane + +volumes: + arcane-data: + +services: + arcane: + image: ghcr.io/ofkm/arcane:latest + container_name: arcane + ports: + - '32519:3552' + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - arcane-data:/app/data + - /mnt/data/docker:/app/data/projects + environment: + - APP_URL=https://dkr.delmar.bzh + - PUID=1000 + - PGID=1000 + - ENCRYPTION_KEY=wRixZ38z76kQZA9AR7ECJveevp3BbR5d + - JWT_SECRET=hG9JUXuuocrDBdKRU6NnwaaARCVfVYbF + restart: unless-stopped diff --git a/arr-suite/LICENSE b/arr-suite/LICENSE new file mode 100644 index 0000000..f71fa13 --- /dev/null +++ b/arr-suite/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 pvd-nerd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/arr-suite/README.md b/arr-suite/README.md new file mode 100644 index 0000000..606b76d --- /dev/null +++ b/arr-suite/README.md @@ -0,0 +1,38 @@ +# Automated Media Management Setup + +After years of trial, error, and endless Reddit dives, I built the guide I wish I had from the start—a clear, step-by-step walkthrough to fully automate your seedbox in a secure, reliable way. Whether you're new or a seasoned tweaker, this will streamline your media setup from start to finish. + +Below is a quick overview of the Docker containers used and what each one does. + +- 🔒 Gluetun: A sleek, all-in-one VPN client supporting multiple providers—your digital Swiss Army knife, ensuring your privacy remains rock-solid while torrenting and streaming. +- 🌐 Traefik: A modern, lightweight reverse proxy and load balancer—your traffic’s front door with smart routing, automatic HTTPS via Let's Encrypt, and seamless Docker integration. Simple to use, powerful to scale. +- ⬇️ qBittorrent: The reliable, open-source alternative to µTorrent, built on Qt and libtorrent-rasterbar, providing a lightweight yet robust solution for downloading torrents with ease. +- 🔄 qBittorrent Port Forwarder: Automatically syncs qBittorrent's ports through Gluetun, ensuring maximum connectivity and optimal speeds without manual port configuration headaches. +- 🎬 Radarr: Automates movie downloads and management—think CouchPotato, but smarter, slicker, and fully integrated into your workflow, making movie management a breeze. +- 📺 Sonarr: Your personal TV assistant, automatically fetching, sorting, renaming, and even upgrading episodes. It monitors RSS feeds and ensures your TV shows are always ready and waiting. +- 🔍 Prowlarr: Centralized management for torrent and Usenet indexers—effortlessly integrated across Sonarr, Radarr, Lidarr, and Readarr, eliminating the hassle of configuring indexers individually for each app. +- 📦 Unpackerr: Watches completed downloads, swiftly unpacking files so they're instantly ready for import by your media apps, removing yet another manual step from your workflow. +- 📝 Overseerr & Jellyseerr: Easy, user-friendly media request tools for Sonarr, Radarr, Plex, and Jellyfin—making content requests and approvals a breeze. +- 📡 Plex & Jellyfin: Stream and organize your media anywhere. Plex offers sleek, remote access; Jellyfin is open-source and privacy-focused—both keep your library beautifully managed. +- 🚢 Watchtower: Automatically keeps your Docker containers up to date with the latest images—set it and forget it for a smoother, more secure stack. + +By the end of this guide, you'll have a powerful, fully-automated media system that's secure, efficient, and hassle-free. + +Check out the full guide here: https://passthebits.com/ + +## Quick Start +Carefully read the entire compose file before deploying. Comments are included with details and additional supported variables. Confirm that all uncommented service variables and volumes are correctly configured before deploying. The compose file is available on GitHub. +``` +git clone https://github.com/pvd-nerd/docker-arr-suite $HOME/media_stack +cd $HOME/media_stack +chmod +x media.sh + +# Pull all container images before launch. +sudo docker compose pull + +# Start stack services. Initial startup may take a while. +# If startup fails, consider increasing the `start_period` in the compose file. +sudo docker compose up -d +``` + +Some containers won't start until environment variables are set. Allow them to restart continuously initially. \ No newline at end of file diff --git a/arr-suite/arr-compose.yml b/arr-suite/arr-compose.yml new file mode 100644 index 0000000..b5ae82e --- /dev/null +++ b/arr-suite/arr-compose.yml @@ -0,0 +1,139 @@ +--- + +networks: + default: + name: media_network + driver: bridge + attachable: true + +volumes: + data: # Sonarr, Radarr, Prowlarr, and Unpackerr data mount + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/data" + + sonarr: # Sonarr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/sonarr" + + radarr: # Radarr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/radarr" + + prowlarr: # Prowlarr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/prowlarr" + + unpackerr: # unPackerr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/unpackerr" + +services: + sonarr: + # See more: https://docs.linuxserver.io/images/docker-sonarr + image: lscr.io/linuxserver/sonarr + container_name: sonarr + restart: always + network_mode: service:gluetun + env_file: ./env/arr.env + volumes: + - sonarr:/config + - data:/media + healthcheck: + test: curl -f http://localhost:8989/ping || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + depends_on: + gluetun: + condition: service_healthy + restart: true + bittorrent: + condition: service_healthy + restart: true + + radarr: + # See more: https://docs.linuxserver.io/images/docker-radarr + image: lscr.io/linuxserver/radarr:latest + container_name: radarr + restart: always + network_mode: service:gluetun + env_file: ./env/arr.env + volumes: + - radarr:/config + - data:/media + healthcheck: + test: curl -f http://localhost:7878/ping || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + depends_on: + gluetun: + condition: service_healthy + restart: true + bittorrent: + condition: service_healthy + restart: true + + prowlarr: + # See more: https://docs.linuxserver.io/images/docker-prowlarr + image: lscr.io/linuxserver/prowlarr:latest + container_name: prowlarr + restart: always + network_mode: service:gluetun + env_file: ./env/arr.env + volumes: + - prowlarr:/config + - data:/media + healthcheck: + test: curl -f http://localhost:9696/ping || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + depends_on: + gluetun: + condition: service_healthy + restart: true + qbittorrent: + condition: service_healthy + restart: true + sonarr: + condition: service_healthy + restart: true + radarr: + condition: service_healthy + restart: true + + unpackerr: + # See more: https://unpackerr.zip/docs/install/compose + image: golift/unpackerr + container_name: unpackerr + restart: always + secrets: + - radarr-api + - sonarr-api + env_file: ./env/arr.env + volumes: + - data:/media + - unpackerr:/config + depends_on: + sonarr: + condition: service_healthy + restart: true + radarr: + condition: service_healthy + restart: true diff --git a/arr-suite/bittorrent-compose.yml b/arr-suite/bittorrent-compose.yml new file mode 100644 index 0000000..077f276 --- /dev/null +++ b/arr-suite/bittorrent-compose.yml @@ -0,0 +1,63 @@ +--- + +networks: + default: + name: media_network + driver: bridge + attachable: true + +volumes: + torrent: # torrent data mount + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/data/torrents" + + bittorrent: # qBittorrent app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/bittorrent" + +secrets: + bittorrent-user: + file: ./secrets/bittorrent-user.secret + bittorrent-password: + file: ./secrets/bittorrent-password.secret + +services: + bittorrent: + # See more: https://docs.linuxserver.io/images/docker-qbittorrent + image: lscr.io/linuxserver/qbittorrent:latest + container_name: bittorrent + restart: always + network_mode: service:gluetun + env_file: ./env/bittorrent.env + volumes: + - bittorrent:/config + - torrent:/media + healthcheck: + test: curl -f http://localhost:9080 || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + depends_on: + gluetun: + condition: service_healthy + restart: true + + bittorrent_port_forwarder: + # See more: https://github.com/mjmeli/qbittorrent-port-forward-gluetun-server + image: mjmeli/qbittorrent-port-forward-gluetun-server + container_name: bittorrent_port_forwarder + restart: always + secrets: + - bittorrent-user + - bittorrent-password + env_file: ./env/bittorrent.env + depends_on: + bittorrent: + condition: service_healthy + restart: true diff --git a/arr-suite/docker-compose.yml b/arr-suite/docker-compose.yml new file mode 100644 index 0000000..c7ef402 --- /dev/null +++ b/arr-suite/docker-compose.yml @@ -0,0 +1,48 @@ +--- + +networks: + default: + name: media_network + driver: bridge + attachable: true + +volumes: + gluetun: # Gluetun app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/gluetun" + +secrets: + wireguard_private_key: + file: ./secrets/wireguard_private_key.secret + +services: + gluetun: + # See more: https://github.com/qdm12/gluetun-wiki + image: qmcgaw/gluetun + container_name: gluetun + restart: always + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + secrets: + - wireguard_private_key + env_file: ./env/gluetun.env + volumes: + - gluetun:/gluetun + + watchtower: + # See more: https://github.com/containrrr/watchtower + image: containrrr/watchtower + container_name: watchtower + env_file: ./env/watchtower.env + volumes: + - /var/run/docker.sock:/var/run/docker.sock + +include: + - bittorrent-compose.yml + - arr-compose.yml + - jellyfin-compose.yml diff --git a/arr-suite/env/arr.env b/arr-suite/env/arr.env new file mode 100644 index 0000000..793904a --- /dev/null +++ b/arr-suite/env/arr.env @@ -0,0 +1,12 @@ +# arr.env +PUID: 1000 +PGID: 1000 +TZ: Etc/UTC +# unpackerr.env +UN_LOG_FILE: /config/unpackerr.log +UN_SONARR_0_URL: http://gluetun:8989 +# Update with Sonarr API key +UN_SONARR_0_API_KEY: /run/secrets/sonarr-api +UN_RADARR_0_URL: http://gluetun:7878 +# Update with Radarr API key +UN_RADARR_0_API_KEY: /run/secrets/radarr-api \ No newline at end of file diff --git a/arr-suite/env/bittorrent.env b/arr-suite/env/bittorrent.env new file mode 100644 index 0000000..2a23177 --- /dev/null +++ b/arr-suite/env/bittorrent.env @@ -0,0 +1,10 @@ +# qBittorrent +PUID: 1000 +PGID: 1000 +TZ: Europe/Paris +WEBUI_PORT: 9080 +# Bittorrent Port Forwarder +QBT_USERNAME: /run/secrets/bittorrent-user +QBT_PASSWORD: /run/secrets/bittorrent-passowrd +QBT_ADDR: http://gluetun:9080 +GTN_ADDR: http://gluetun:8000 diff --git a/arr-suite/env/gluetun.env b/arr-suite/env/gluetun.env new file mode 100644 index 0000000..fa9ffea --- /dev/null +++ b/arr-suite/env/gluetun.env @@ -0,0 +1,10 @@ +# gluetun.env +PUID: 1000 +PGID: 1000 +VPN_SERVICE_PROVIDER: protonvpn +VPN_TYPE: wireguard +TZ: Europe/Paris +# Wireguard Settings: +WIREGUARD_PRIVATE_KEY: /run/secrets/wireguard_private_key +WIREGUARD_ADDRESSES: 10.2.0.2/32 +SERVER_COUNTRIES: France diff --git a/arr-suite/env/jellyfin.env b/arr-suite/env/jellyfin.env new file mode 100644 index 0000000..52efda5 --- /dev/null +++ b/arr-suite/env/jellyfin.env @@ -0,0 +1,5 @@ +# jellyfin.env +JELLYFIN_PublishedServerUrl: https://jellyfin.${DOMAIN} +# jellyseerr.env +LOG_LEVEL: debug +TZ: Europe/Paris diff --git a/arr-suite/env/plex.env b/arr-suite/env/plex.env new file mode 100644 index 0000000..921292d --- /dev/null +++ b/arr-suite/env/plex.env @@ -0,0 +1,9 @@ +# plex.env +PUID: 1000 +PGID: 1000 +TZ: Etc/UTC +VERSION: docker +# Get claim from https://account.plex.tv/en/claim +PLEX_CLAIM: /run/secrets/plex-claim +LOG_LEVEL: debug +PORT: 5055 \ No newline at end of file diff --git a/arr-suite/env/traefik.env b/arr-suite/env/traefik.env new file mode 100644 index 0000000..63ef538 --- /dev/null +++ b/arr-suite/env/traefik.env @@ -0,0 +1,3 @@ +# traefik.env +CF_DNS_API_TOKEN_FILE: /run/secrets/cloudflare-token +CF_API_EMAIL_FILE: /run/secrets/cloudflare-email \ No newline at end of file diff --git a/arr-suite/env/watchtower.env b/arr-suite/env/watchtower.env new file mode 100644 index 0000000..d9849d5 --- /dev/null +++ b/arr-suite/env/watchtower.env @@ -0,0 +1,4 @@ +# watchtower.env +WATCHTOWER_CLEANUP: true +WATCHTOWER_INCLUDE_RESTARTING: true +WATCHTOWER_POLL_INTERVAL: 86400 \ No newline at end of file diff --git a/arr-suite/jellyfin-compose.yml b/arr-suite/jellyfin-compose.yml new file mode 100644 index 0000000..97ea842 --- /dev/null +++ b/arr-suite/jellyfin-compose.yml @@ -0,0 +1,67 @@ +--- + +networks: + default: + name: media_network + driver: bridge + attachable: true + +volumes: + media: # Media library + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/data/media" + + jellyseerr: # Jellyseerr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/jellyseerr" + + jellyfin: # Jellyfin app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/jellyfin" + + cache: # Jellyfin cache data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/jellyfin_cache" + +services: + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + restart: always + user: 1000:1000 + # Network mode of 'host' exposes the ports on the host. This is needed for DLNA access. + # network_mode: 'host' + volumes: + - jellyfin:/config + - cache:/cache + - media:/media + env_file: ./env/jellyfin.env + healthcheck: + test: curl -i http://localhost:8096/health || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + + jellyseerr: + image: fallenbagel/jellyseerr:latest + container_name: jellyseerr + restart: always + env_file: ./env/jellyfin.env + volumes: + - jellyseerr:/app/config + depends_on: + jellyfin: + condition: service_healthy + restart: true diff --git a/arr-suite/plex-compose.yml b/arr-suite/plex-compose.yml new file mode 100644 index 0000000..b281708 --- /dev/null +++ b/arr-suite/plex-compose.yml @@ -0,0 +1,68 @@ +--- +networks: + default: + name: media_network + driver: bridge + attachable: true + +volumes: + media: # Media library + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/data/media" + + overseerr: # Overseerr app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/overseerr" + + plex: # Plex app data. + driver: local + driver_opts: + type: "nfs" + o: "addr=${NFS_SERVER},rw,tcp,nolock,hard,wsize=65536,rsize=65536" + device: ":${NFS_VOLUME}/docker_data/plex" + +secrets: + plex-claim: + file: ./secrets/plex-claim.secret + +services: + overseerr: + # See more: https://docs.overseerr.dev/getting-started/installation + image: sctx/overseerr:latest + container_name: overseerr + restart: always + env_file: ./env/plex.env + volumes: + - overseerr:/app/config + healthcheck: + test: wget http://localhost:5055/api/v1/status -qO /dev/null || exit 1 + interval: 10s + timeout: 3s + start_period: 60s + depends_on: + plex: + condition: service_healthy + restart: true + + plex: + # See more: https://docs.linuxserver.io/images/docker-plex/ + image: lscr.io/linuxserver/plex:latest + container_name: plex + restart: always + secrets: + - plex-claim + env_file: ./env/plex.env + volumes: + - plex:/config + - media:/media + healthcheck: + test: curl -f http://localhost:32400/identity || exit 1 + interval: 10s + timeout: 3s + start_period: 60s diff --git a/arr-suite/secrets/bittorrent-password.secret b/arr-suite/secrets/bittorrent-password.secret new file mode 100644 index 0000000..d4cbab3 --- /dev/null +++ b/arr-suite/secrets/bittorrent-password.secret @@ -0,0 +1 @@ +BITTORRENTPASS \ No newline at end of file diff --git a/arr-suite/secrets/bittorrent-user.secret b/arr-suite/secrets/bittorrent-user.secret new file mode 100644 index 0000000..c30f33e --- /dev/null +++ b/arr-suite/secrets/bittorrent-user.secret @@ -0,0 +1 @@ +BITTORRENTUSER \ No newline at end of file diff --git a/arr-suite/secrets/cloudflare-email.secret b/arr-suite/secrets/cloudflare-email.secret new file mode 100644 index 0000000..00d6e3c --- /dev/null +++ b/arr-suite/secrets/cloudflare-email.secret @@ -0,0 +1 @@ +CLOUDFLAREEMAIL \ No newline at end of file diff --git a/arr-suite/secrets/cloudflare-token.secret b/arr-suite/secrets/cloudflare-token.secret new file mode 100644 index 0000000..bb16481 --- /dev/null +++ b/arr-suite/secrets/cloudflare-token.secret @@ -0,0 +1 @@ +CLOUDFLAREAPI \ No newline at end of file diff --git a/arr-suite/secrets/openvpn_password.secret b/arr-suite/secrets/openvpn_password.secret new file mode 100644 index 0000000..0af7eb3 --- /dev/null +++ b/arr-suite/secrets/openvpn_password.secret @@ -0,0 +1 @@ +OPENVPNPASS diff --git a/arr-suite/secrets/openvpn_user.secret b/arr-suite/secrets/openvpn_user.secret new file mode 100644 index 0000000..084f5e7 --- /dev/null +++ b/arr-suite/secrets/openvpn_user.secret @@ -0,0 +1 @@ +OPENVPNUSER \ No newline at end of file diff --git a/arr-suite/secrets/plex-claim.secret b/arr-suite/secrets/plex-claim.secret new file mode 100644 index 0000000..8714329 --- /dev/null +++ b/arr-suite/secrets/plex-claim.secret @@ -0,0 +1 @@ +PLEXCLAIM \ No newline at end of file diff --git a/arr-suite/secrets/radarr-api.secret b/arr-suite/secrets/radarr-api.secret new file mode 100644 index 0000000..00fcc0c --- /dev/null +++ b/arr-suite/secrets/radarr-api.secret @@ -0,0 +1 @@ +RADARRAPI \ No newline at end of file diff --git a/arr-suite/secrets/sonarr-api.secret b/arr-suite/secrets/sonarr-api.secret new file mode 100644 index 0000000..3fae682 --- /dev/null +++ b/arr-suite/secrets/sonarr-api.secret @@ -0,0 +1 @@ +SONARRAPI \ No newline at end of file diff --git a/arr-suite/secrets/wireguard_private_key.secret b/arr-suite/secrets/wireguard_private_key.secret new file mode 100644 index 0000000..8cb9da4 --- /dev/null +++ b/arr-suite/secrets/wireguard_private_key.secret @@ -0,0 +1 @@ +QHpbGPtMm2M30HrWPzoseJAxJd9lJeTjZs28wsVS+Vc= diff --git a/arr-suite/temp.env b/arr-suite/temp.env new file mode 100644 index 0000000..b538f1d --- /dev/null +++ b/arr-suite/temp.env @@ -0,0 +1,182 @@ +################################################################################# +################################################################################# +################################################################################# +## +## Docker Compose Environment Variable file for Jellyfin / *ARR Media Stack +## +## Update any of the environment variables below as required. +## +## It is highly recommended Linux users set up a "docker" user, so the +## applications can access the local filesystem with this user's access +## privileges. Use PUID / PGID to map user access between the Docker apps +## and local filesystem. +## +## The MediaStack Guide is located at https://MediaStack.Guide +## +################################################################################# +################################################################################# +################################################################################# + +# Name of the project in Docker +COMPOSE_PROJECT_NAME=mediastack +COMPOSE_BAKE=true + +# This is the network subnet which will be used inside the docker "media_network", change as required. +# LOCAL_SUBNET is your home network and is needed so the VPN client allows access to your home computers. +DOCKER_SUBNET=172.28.10.0/24 +DOCKER_GATEWAY=172.28.10.1 +LOCAL_SUBNET=192.168.1.0/24 # This is the IP Subnet used on your home network +LOCAL_DOCKER_IP=192.168.1.10 # This is the IP Address of your Docker computer + +# Each of the "*ARR" applications have been configured so the theme can be changed to your needs. +# Refer to Theme Park for more info / options: https://docs.theme-park.dev/theme-options/aquamarine/ +TP_THEME=nord + +# If you intend to use Plex as your Media Server, then enter your Plex Claim +# information below, to link this Plex Media Server to your Plex account +# Access Plex claim at: https://account.plex.tv/en/claim +PLEX_CLAIM=claim-1234567890abcdef + +# These are the folders on your local host computer / NAS running docker, they MUST exist +# and have correct permissions for PUID and PGUI prior to running the docker compose. +# +# Use the commands in the Guide to create all the sub-folders in each of these folders. + +# Host Data Folders - Will accept Linux, Windows, NAS folders. +# Make sure these folders exists before running the "docker compose" command. +FOLDER_FOR_MEDIA=/your-media-folder # <-- Update for your folders - Synology Example: /volume1/media +FOLDER_FOR_DATA=/your-app-configs # <-- Update for your folders - Synology Example: /volume1/docker/appdata + +# File access, date and time details for the containers / applications to use. +# Run "sudo id docker" on host computer to find PUID / PGID and update these to suit. +PUID=1000 +PGID=1000 +UMASK=0002 +TIMEZONE=Europe/Zurich + +# Update your own Internet VPN provide details below +# Online documentation: https://github.com/qdm12/gluetun-wiki/tree/main/setup/providers +VPN_TYPE=openvpn +VPN_SERVICE_PROVIDER=VPN provider name +VPN_USERNAME= +VPN_PASSWORD= + +# You MUST provide at least one entry to the SERVER variables below, that supports your VPN provider's settings. +# If you want to add more than one entry per line, use comma separated values: "one,two,three" etc... +SERVER_COUNTRIES= +SERVER_REGIONS=Europe +SERVER_CITIES= +SERVER_HOSTNAMES= +SERVER_CATEGORIES= + +# Fill in this item ONLY if you're using a custom OpenVPN configuration +# Should be inside gluetun data folder - Example: /gluetun/custom-openvpn.conf +# You can then edit it inside the FOLDER_FOR_DATA location for gluetun. +OPENVPN_CUSTOM_CONFIG= +GLUETUN_CONTROL_PORT=8320 + +# Fill in these items ONLY if you change VPN_TYPE to "wireguard" +VPN_ENDPOINT_IP= +VPN_ENDPOINT_PORT= +WIREGUARD_PUBLIC_KEY= +WIREGUARD_PRIVATE_KEY= +WIREGUARD_PRESHARED_KEY= +WIREGUARD_ADDRESSES= + +# These are the ports used to access each of the applications in your web browser. +# You can safely change these if you need, but they can't conflict with other active ports. +QBIT_PORT=6881 +FLARESOLVERR_PORT=8191 +TDARR_SERVER_PORT=8266 + +# WebUI ports for internal access to applications +WEBUI_PORT_AUTHENTIK=6080 +WEBUI_PORT_BAZARR=6767 +WEBUI_PORT_CHROMIUM=3650 +WEBUI_PORT_DDNS_UPDATER=8310 +WEBUI_PORT_FILEBOT=5454 +WEBUI_PORT_GUACAMOLE=9200 +WEBUI_PORT_GRAFANA=3800 +WEBUI_PORT_HEADPLANE=3500 +WEBUI_PORT_HEIMDALL=2080 +WEBUI_PORT_HOMARR=3200 +WEBUI_PORT_HOMEPAGE=3000 +WEBUI_PORT_HUNTARR=9705 +WEBUI_PORT_JELLYFIN=8096 +WEBUI_PORT_JELLYSEERR=5055 +WEBUI_PORT_LIDARR=8686 +WEBUI_PORT_MYLAR=8090 +WEBUI_PORT_PLEX=32400 +WEBUI_PORT_PORTAINER=9000 +WEBUI_PORT_PROMETHEUS=9090 +WEBUI_PORT_PROWLARR=9696 +WEBUI_PORT_QBITTORRENT=8200 +WEBUI_PORT_RADARR=7878 +WEBUI_PORT_READARR=8787 +WEBUI_PORT_SABNZBD=8100 +WEBUI_PORT_SONARR=8989 +WEBUI_PORT_TDARR=8265 +WEBUI_PORT_TRAEFIK=8080 +WEBUI_PORT_WHISPARR=6969 + +CHROMIUM_START_PAGE="https://github.com/geekau/mediastack/" + +# Traefik is configured for Reverse Proxy. Set your Internet gateway to redirect incoming ports 80 and 443 +# to the ports used below (using Docker IP Address), and they will be translated back to 80 and 443 by Traefik. +# Change these port numbers if you have conflicting services running on the Docker host computer. +# If ports 80 and 443 are already used, then adjust and redirect incoming ports to 5080 and 5443, or similar. + +REVERSE_PROXY_PORT_HTTP=80 +REVERSE_PROXY_PORT_HTTPS=443 + +# Traefik Configuration +CLOUDFLARE_EMAIL=email@example.com # Your CloudFlare Account Email Address +CLOUDFLARE_DNS_ZONE=example.com # Your CloudFlare Registered Domain Name +CLOUDFLARE_DNS_API_TOKEN=1234567890abcdef1234567890... # Your CloudFlare Read / Write API Token + +# Headscale / Headplane / Tailscale VPN Wireguard Mesh Networking +# These port settings are only to change the internal port due to conflicts, Headscale, Tailscale and Headplane will +# all function normally using the default ports as they are routed through Traefik reverse proxy. +CONNECT_PORT_HEADSCALE=4080 +METRICS_PORT_HEADSCALE=4090 + +CROWDSEC_PORT=9080 +METRICS_PORT_TRAEFIK=8082 +METRICS_PORT_UNPACKERR=5656 + +# The Tailscale Docker container is configured as an exit node inside your home network, so traffic can route securely +# across the Internet, and break out behind your home gateway / router. +# sudo docker exec -it headscale headscale users create exit-node +# sudo docker exec -it headscale headscale --user exit-node preauthkeys create +# NOTE: Headscale must be running before the commands can be executed, then update authkey below and restart Tailscale. +TAILSCALE_AUTHKEY=1234567890abcdef1234567890abcdef1234567890abcdef + +# Connect to the following address to complete the initial setup of Authentik after first deployment: +# http://:6080/if/flow/initial-setup/ + +# echo AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n') +AUTHENTIK_SECRET_KEY=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef +AUTHENTIK_VERSION=2025.4.1 +AUTHENTIK_ERROR_REPORTING__ENABLED=true +POSTGRESQL_PORT=5432 +VALKEY_PORT=6379 + +# echo POSTGRESQL_PASSWORD=$(openssl rand -base64 60 | tr -d '\n') +POSTGRESQL_PASSWORD=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef +POSTGRESQL_USERNAME=mediastack-postgresql +AUTHENTIK_DATABASE=mediastack-authentik +GUACAMOLE_DATABASE=mediastack-guacamole +GUACD_PORT=4822 + +# SMTP Host Emails are sent to +EMAIL_SERVER_HOST=mail.example.com +EMAIL_SERVER_PORT=25 +# Optionally authenticate (don't add quotation marks to your password) +EMAIL_ADDRESS=email.address@example.com +EMAIL_PASSWORD=email-password-here +# Use StartTLS +EMAIL_TLS=true +# Use SSL - StartTLS and SSL can't both be true +EMAIL_SSL=false +# Email address authentik will send from, should have a correct @domain.name +EMAIL_SENDER=authentik@example.com diff --git a/arr-suite/temp.yaml b/arr-suite/temp.yaml new file mode 100644 index 0000000..a549077 --- /dev/null +++ b/arr-suite/temp.yaml @@ -0,0 +1,1768 @@ +########################################################################### +########################################################################### +networks: + mediastack: + name: mediastack + driver: bridge + ipam: + driver: default + config: + - subnet: ${DOCKER_SUBNET:?err} + gateway: ${DOCKER_GATEWAY:?err} + +########################################################################### +########################################################################### +services: + +########################################################################### +########################################################################### +## +## Docker Compose File: Postgresql +## Function: Postgresql Database Server +## +## Documentation: https://hub.docker.com/_/postgres +## +########################################################################### +########################################################################### + postgresql: + image: docker.io/library/postgres:latest + container_name: postgresql + restart: unless-stopped + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + ports: + - ${POSTGRESQL_PORT:?err}:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - ${FOLDER_FOR_DATA:?err}/postgresql:/var/lib/postgresql/data + environment: + - TZ=${TIMEZONE:?err} + - POSTGRES_DB=${AUTHENTIK_DATABASE:?err} + - POSTGRES_USER=${POSTGRESQL_USERNAME:?err} + - POSTGRES_PASSWORD=${POSTGRESQL_PASSWORD:?err} + +########################################################################### +########################################################################### +## +## Docker Compose File: Guacamole / Guacd +## Function: Clientless Remote Desktop Gateway +## +## Documentation: https://hub.docker.com/r/guacamole/guacamole +## +########################################################################### +########################################################################### + guacamole: + image: guacamole/guacamole + container_name: guacamole + restart: unless-stopped + user: ${PUID:?err}:${PGID:?err} + depends_on: + postgresql: + condition: service_healthy + restart: true + networks: + - mediastack + ports: + - ${WEBUI_PORT_GUACAMOLE:?err}:8080 + environment: + - TZ=${TIMEZONE:?err} + - WEBAPP_CONTEXT=ROOT + - GUACD_HOSTNAME=guacd + - POSTGRESQL_HOSTNAME=postgresql + - POSTGRESQL_PORT=${POSTGRESQL_PORT:?err} + - POSTGRESQL_DATABASE=${GUACAMOLE_DATABASE:?err} + - POSTGRESQL_USER=${POSTGRESQL_USERNAME:?err} + - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD:?err} + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.guacamole.service=guacamole + - traefik.http.routers.guacamole.rule=Host(`guacamole.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.guacamole.entrypoints=secureweb + - traefik.http.routers.guacamole.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.guacamole.loadbalancer.server.scheme=http + - traefik.http.services.guacamole.loadbalancer.server.port=8080 + # MIDDLEWARES + + guacd: + image: guacamole/guacd + container_name: guacd + restart: unless-stopped + user: ${PUID:?err}:${PGID:?err} + depends_on: + postgresql: + condition: service_healthy + restart: true + networks: + - mediastack + ports: + - ${GUACD_PORT:?err}:4822 + environment: + - TZ=${TIMEZONE:?err} + - POSTGRESQL_HOSTNAME=postgresql + - POSTGRESQL_PORT=${POSTGRESQL_PORT:?err} + - POSTGRESQL_DATABASE=${GUACAMOLE_DATABASE:?err} + - POSTGRESQL_USER=${POSTGRESQL_USERNAME:?err} + - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD:?err} + +########################################################################### +########################################################################### +## +## Docker Compose File: Valkey (same as Redis) +## Function: High Performance Data Structure Server +## +## Documentation: https://hub.docker.com/r/valkey/valkey +## +########################################################################### +########################################################################### + valkey: + image: valkey/valkey:alpine + container_name: valkey + command: --save 60 1 --loglevel warning + restart: unless-stopped + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + ports: + - ${VALKEY_PORT:?err}:6379 + healthcheck: + test: ["CMD-SHELL", "valkey-cli ping | grep PONG"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + volumes: + - ${FOLDER_FOR_DATA:?err}/valkey:/data + +########################################################################### +########################################################################### +## +## Docker Compose File: Authentik Server & Worker +## Function: Authentication & Authorisation Identity Manager +## +## Documentation: https://docs.goauthentik.io/docs/install-config/install/docker-compose +## +########################################################################### +########################################################################### + authentik: + image: ghcr.io/goauthentik/server:${AUTHENTIK_VERSION:?err} + container_name: authentik + restart: unless-stopped + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + command: server + environment: + - TZ=${TIMEZONE:?err} + - AUTHENTIK_LOG_LEVEL=info # Options are: # info, warning, error, debug and trace + - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY:?err} + - AUTHENTIK_REDIS__HOST=valkey + - AUTHENTIK_POSTGRESQL__HOST=postgresql + - AUTHENTIK_POSTGRESQL__NAME=${AUTHENTIK_DATABASE:?err} + - AUTHENTIK_POSTGRESQL__USER=${POSTGRESQL_USERNAME:?err} + - AUTHENTIK_POSTGRESQL__PASSWORD=${POSTGRESQL_PASSWORD:?err} + - AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED:?err} + - AUTHENTIK_EMAIL__HOST=${EMAIL_SERVER_HOST} + - AUTHENTIK_EMAIL__PORT=${EMAIL_SERVER_PORT} + - AUTHENTIK_EMAIL__USERNAME=${EMAIL_ADDRESS} + - AUTHENTIK_EMAIL__PASSWORD=${EMAIL_PASSWORD} + - AUTHENTIK_EMAIL__USE_TLS=${EMAIL_TLS} + - AUTHENTIK_EMAIL__USE_SSL=${EMAIL_SSL} + - AUTHENTIK_EMAIL__FROM=${EMAIL_SENDER} + - AUTHENTIK_EMAIL__TIMEOUT=10 + volumes: + - ${FOLDER_FOR_DATA:?err}/authentik/media:/media + - ${FOLDER_FOR_DATA:?err}/authentik/templates:/templates + ports: + - ${WEBUI_PORT_AUTHENTIK:?err}:9000 + depends_on: + postgresql: + condition: service_healthy + restart: true + valkey: + condition: service_healthy + restart: true + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.authentik.service=authentik + - traefik.http.routers.authentik.rule=Host(`auth.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.authentik.entrypoints=secureweb + - traefik.http.routers.authentik.middlewares=security-headers@file,traefik-bouncer@file + # Do not add authentik-forwardauth@file to middlewares, otherwise other applications can't authenticate + # SERVICES + - traefik.http.services.authentik.loadbalancer.server.scheme=http + - traefik.http.services.authentik.loadbalancer.server.port=9000 + # MIDDLEWARES + + authentic-worker: + image: ghcr.io/goauthentik/server:${AUTHENTIK_VERSION:?err} + container_name: authentik-worker + restart: unless-stopped + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + command: worker + environment: + - TZ=${TIMEZONE:?err} + - AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY:?err} + - AUTHENTIK_REDIS__HOST=valkey + - AUTHENTIK_POSTGRESQL__HOST=postgresql + - AUTHENTIK_POSTGRESQL__NAME=${AUTHENTIK_DATABASE:?err} + - AUTHENTIK_POSTGRESQL__USER=${POSTGRESQL_USERNAME:?err} + - AUTHENTIK_POSTGRESQL__PASSWORD=${POSTGRESQL_PASSWORD:?err} + - AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED:?err} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${FOLDER_FOR_DATA:?err}/authentik/certs:/certs + - ${FOLDER_FOR_DATA:?err}/authentik/media:/media + - ${FOLDER_FOR_DATA:?err}/authentik/templates:/templates + depends_on: + postgresql: + condition: service_healthy + restart: true + valkey: + condition: service_healthy + restart: true + +########################################################################### +########################################################################### +## +## Docker Compose File: Traefik +## Function: Reverse Proxy Routing Server +## +## Documentation: https://doc.traefik.io/traefik/ +## +########################################################################### +########################################################################### + traefik: + image: traefik:latest + container_name: traefik + restart: unless-stopped + networks: + - mediastack + user: root + environment: + - TZ=${TIMEZONE:?err} + - CF_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN:?err} + ports: + - ${REVERSE_PROXY_PORT_HTTP:?err}:80 + - ${REVERSE_PROXY_PORT_HTTPS:?err}:443 + - ${WEBUI_PORT_TRAEFIK:?err}:8080 + - ${METRICS_PORT_TRAEFIK:?err}:8082 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${FOLDER_FOR_DATA:?err}/logs/traefik:/var/log + - ${FOLDER_FOR_DATA:?err}/traefik:/etc/traefik + - ${FOLDER_FOR_DATA:?err}/traefik/letsencrypt:/letsencrypt + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.traefik.service=api@internal + - traefik.http.routers.traefik.rule=Host(`traefik.${CLOUDFLARE_DNS_ZONE:?err}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) + - traefik.http.routers.traefik.entrypoints=secureweb + - traefik.http.routers.traefik.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.traefik.loadbalancer.server.scheme=http + - traefik.http.services.traefik.loadbalancer.server.port=8080 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Traefik Certificate Dumper +## Function: Dump SSL / TLS Certificates from Traefik +## +## Documentation: https://hub.docker.com/r/ldez/traefik-certs-dumper +## +########################################################################### +########################################################################### + traefik-certs-dumper: + image: ldez/traefik-certs-dumper:latest + container_name: traefik-certs-dumper + restart: always + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + entrypoint: sh -c ' + while ! [ -e /data/acme.json ] + || ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do + sleep 1 + ; done + && traefik-certs-dumper file --version v2 --watch + --source /data/acme.json --dest /certs' + volumes: + - ${FOLDER_FOR_DATA:?err}/traefik/letsencrypt:/data:ro + - ${FOLDER_FOR_DATA:?err}/traefik-certs-dumper:/certs + +########################################################################### +########################################################################### +## +## Docker Compose File: CrowdSec Security Engine +## Function: Cyber Security Threat Intelligence +## +## Documentation: https://docs.crowdsec.net/u/getting_started/installation/docker/ +## +########################################################################### +########################################################################### + crowdsec: + image: crowdsecurity/crowdsec:latest + container_name: crowdsec + restart: always + networks: + - mediastack + user: ${PUID:?err}:${PGID:?err} + environment: + - TZ=${TIMEZONE:?err} + ports: + - 127.0.0.1:${CROWDSEC_PORT:?err}:8080 + - 6060:6060 # Provides Metrics for Prometheus + - 7422:7422 # Provides WAF AppSec + depends_on: + - traefik + volumes: + - ${FOLDER_FOR_DATA:?err}/crowdsec:/etc/crowdsec + - ${FOLDER_FOR_DATA:?err}/crowdsec/data:/var/lib/crowdsec/data/ + - ${FOLDER_FOR_DATA:?err}/logs:/logs:ro + +########################################################################### +########################################################################### +## +## Docker Compose File: Prometheus +## Function: Systems and Service Monitoring +## +## Documentation: https://prometheus.io/docs/introduction/overview/ +## +########################################################################### +########################################################################### + prometheus: + image: prom/prometheus + container_name: prometheus + restart: unless-stopped + user: ${PUID:?err}:${PGID:?err} + networks: + - mediastack + depends_on: + - crowdsec + ports: + - 127.0.0.1:${WEBUI_PORT_PROMETHEUS:?err}:9090 + volumes: + - ${FOLDER_FOR_DATA:?err}/prometheus:/prometheus + environment: + - TZ=${TIMEZONE:?err} + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.prometheus.service=prometheus + - traefik.http.routers.prometheus.rule=Host(`prometheus.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.prometheus.entrypoints=secureweb + - traefik.http.routers.prometheus.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.prometheus.loadbalancer.server.scheme=http + - traefik.http.services.prometheus.loadbalancer.server.port=9090 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Grafana +## Function: Visual Monitoring Dashboard +## +## Documentation: http://docs.grafana.org/installation/docker/ +## +########################################################################### +########################################################################### + grafana: + image: grafana/grafana-enterprise + container_name: grafana + restart: unless-stopped + user: ${PUID:?err}:${PGID:?err} + depends_on: + - crowdsec + networks: + - mediastack + ports: + - ${WEBUI_PORT_GRAFANA:?err}:3000 + volumes: + - /var/log:/var/dockerhost:ro + - ${FOLDER_FOR_DATA:?err}/grafana:/var/lib/grafana + environment: + - TZ=${TIMEZONE:?err} + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.grafana.service=grafana + - traefik.http.routers.grafana.rule=Host(`grafana.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.grafana.entrypoints=secureweb + - traefik.http.routers.grafana.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.grafana.loadbalancer.server.scheme=http + - traefik.http.services.grafana.loadbalancer.server.port=3000 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Headscale +## Function: OpenSource Tailscale Coordination Server +## +## Documentation: https://headscale.net/stable/ +## +########################################################################### +########################################################################### + headscale: + image: headscale/headscale:latest + container_name: headscale + restart: unless-stopped + networks: + - mediastack + command: serve + ports: + - ${CONNECT_PORT_HEADSCALE:?err}:8080 + - ${METRICS_PORT_HEADSCALE:?err}:9090 + volumes: + - ${FOLDER_FOR_DATA:?err}/headscale:/etc/headscale + - ${FOLDER_FOR_DATA:?err}/headscale/data:/var/lib/headscale + environment: + - TZ=${TIMEZONE:?err} + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.headscale.service=headscale + - traefik.http.routers.headscale.rule=Host(`headscale.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.headscale.entrypoints=secureweb + - traefik.http.routers.headscale.middlewares=security-headers@file,traefik-bouncer@file + # Do not add authentik-forwardauth@file to middlewares, otherwise Tailscale clients can't authenticate and connect + # SERVICES + - traefik.http.services.headscale.loadbalancer.server.scheme=http + - traefik.http.services.headscale.loadbalancer.server.port=8080 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Tailscale +## Function: Tailscale Client - Operating as Tailnet Exit-Node +## +## Documentation: https://headscale.net/stable/ +## +########################################################################### +########################################################################### + tailscale: + image: tailscale/tailscale:latest + hostname: tailscale + container_name: tailscale + restart: unless-stopped + networks: + - mediastack + cap_add: + - net_admin + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - ${FOLDER_FOR_DATA:?err}/tailscale:/var/lib/tailscale + environment: + - TS_USERSPACE=false + - TS_STATE_DIR=/var/lib/tailscale + - TS_AUTHKEY=${TAILSCALE_AUTHKEY:?err} + - TS_EXTRA_ARGS=--hostname=exit-node --advertise-exit-node --advertise-routes=${LOCAL_SUBNET:?err},${DOCKER_SUBNET:?err} --login-server=https://headscale.${CLOUDFLARE_DNS_ZONE:?err} + +########################################################################### +########################################################################### +## +## Docker Compose File: Headplane +## Function: WebUI Management for Headscale Coordination Server +## +## Documentation: https://github.com/tale/headplane +## +########################################################################### +########################################################################### + headplane: + image: ghcr.io/tale/headplane:latest + container_name: headplane + restart: unless-stopped + networks: + - mediastack + ports: + - ${WEBUI_PORT_HEADPLANE:?err}:3000 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${FOLDER_FOR_DATA:?err}/headscale:/etc/headscale + - ${FOLDER_FOR_DATA:?err}/headplane:/etc/headplane + - ${FOLDER_FOR_DATA:?err}/headplane/data:/var/lib/headplane + environment: + - TZ=${TIMEZONE:?err} + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.headplane.service=headplane + - traefik.http.routers.headplane.rule=Host(`headplane.${CLOUDFLARE_DNS_ZONE:?err}`) && PathPrefix(`/admin/`) + - traefik.http.routers.headplane.entrypoints=secureweb + - traefik.http.routers.headplane.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.headplane.loadbalancer.server.scheme=http + - traefik.http.services.headplane.loadbalancer.server.port=3000 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Gluetun (qmcgaw) +## Function: VPN Client +## +## Documentation: https://github.com/qdm12/gluetun-wiki +## +########################################################################### +########################################################################### + gluetun: + image: qmcgaw/gluetun:latest + container_name: gluetun + restart: always + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + ports: + - 8888:8888/tcp # Gluetun Local Network HTTP proxy + - 8388:8388/tcp # Gluetun Local Network Shadowsocks + - 8388:8388/udp # Gluetun Local Network Shadowsocks + - ${GLUETUN_CONTROL_PORT:?err}:${GLUETUN_CONTROL_PORT:?err} # Gluetun Status Port + +# Comment out lines below if they are not going to use Gluetun VPN for Internet connection: + + - ${WEBUI_PORT_BAZARR:?err}:6767 # WebUI Portal: Bazarr + - ${WEBUI_PORT_FILEBOT:?err}:5454 # WebUI Portal: Filebot + - ${WEBUI_PORT_HUNTARR:?err}:9705 # WebUI Portal: Huntarr + - ${WEBUI_PORT_JELLYFIN:?err}:8096 # WebUI Portal: Jellyfin + - ${WEBUI_PORT_JELLYSEERR:?err}:5055 # WebUI Portal: Jellyseerr + - ${WEBUI_PORT_LIDARR:?err}:8686 # WebUI Portal: Lidarr + - ${WEBUI_PORT_MYLAR:?err}:8090 # WebUI Portal: Mylar3 + - ${WEBUI_PORT_PROWLARR:?err}:9696 # WebUI Portal: Prowlarr + - ${WEBUI_PORT_RADARR:?err}:7878 # WebUI Portal: Radarr + - ${WEBUI_PORT_READARR:?err}:8787 # WebUI Portal: Readarr + - ${WEBUI_PORT_SABNZBD:?err}:8080 # WebUI Portal: SABnzbd + - ${WEBUI_PORT_SONARR:?err}:8989 # WebUI Portal: Sonarr + - ${WEBUI_PORT_WHISPARR:?err}:6969 # WebUI Portal: Whisparr + + - ${WEBUI_PORT_QBITTORRENT:?err}:${WEBUI_PORT_QBITTORRENT:?err} # WebUI Portal: qBittorrent + + - ${QBIT_PORT:?err}:6881 # Transmission Torrent Port + - ${FLARESOLVERR_PORT:?err}:8191 # Service Port: FlareSolverr + + - ${TDARR_SERVER_PORT:?err}:${TDARR_SERVER_PORT:?err} # Tdarr: Server Port + - ${WEBUI_PORT_TDARR:?err}:${WEBUI_PORT_TDARR:?err} # Tdarr: WebUI Portal + + - ${WEBUI_PORT_PLEX:?err}:32400 # WebUI Portal: Plex +# - 1900:1900/udp # DNLA Service (Clashes with Synology: SSPD "File Services" --> "Advanced") +# - 5353:5353/udp # Plex Network Port (Clashes with Synology: Bonjour "File Services" --> "Advanced") + - 8324:8324 # Plex Network Port + - 32410:32410/udp # Plex Network Port + - 32412:32412/udp # Plex Network Port + - 32413:32413/udp # Plex Network Port + - 32414:32414/udp # Plex Network Port + - 32469:32469 # Plex Network Port + + volumes: + - ${FOLDER_FOR_DATA:?err}/gluetun:/gluetun + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER:?err} + - OPENVPN_USER=${VPN_USERNAME:?err} + - OPENVPN_PASSWORD=${VPN_PASSWORD:?err} + - SERVER_COUNTRIES=${SERVER_COUNTRIES} + - SERVER_REGIONS=${SERVER_REGIONS} + - SERVER_CITIES=${SERVER_CITIES} + - SERVER_HOSTNAMES=${SERVER_HOSTNAMES} + - SERVER_CATEGORIES=${SERVER_CATEGORIES} + - FIREWALL_OUTBOUND_SUBNETS=${LOCAL_SUBNET:?err} + - OPENVPN_CUSTOM_CONFIG=${OPENVPN_CUSTOM_CONFIG} + - HTTP_CONTROL_SERVER_ADDRESS=:${GLUETUN_CONTROL_PORT:?err} + - VPN_TYPE=${VPN_TYPE} + - VPN_ENDPOINT_IP=${VPN_ENDPOINT_IP} + - VPN_ENDPOINT_PORT=${VPN_ENDPOINT_PORT} + - WIREGUARD_PUBLIC_KEY=${WIREGUARD_PUBLIC_KEY} + - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY} + - WIREGUARD_PRESHARED_KEY=${WIREGUARD_PRESHARED_KEY} + - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES} + - HTTPPROXY=on + - SHADOWSOCKS=on + networks: + - mediastack + +########################################################################### +########################################################################### +## +## Docker Compose File: Bazarr (LinuxServer.io) +## Function: Download subtitles for Radarr and Sonarr +## +## Documentation: https://docs.linuxserver.io/images/docker-bazarr +## +########################################################################### +########################################################################### + bazarr: + image: lscr.io/linuxserver/bazarr:latest + container_name: bazarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/bazarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:bazarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_BAZARR:?err}:6767 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.bazarr.service=bazarr + - traefik.http.routers.bazarr.rule=Host(`bazarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.bazarr.entrypoints=secureweb + - traefik.http.routers.bazarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.bazarr.loadbalancer.server.scheme=http + - traefik.http.services.bazarr.loadbalancer.server.port=6767 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Chromium (LinuxServer.io) +## Function: OpenSource Web Browser +## +## Documentation: https://docs.linuxserver.io/images/docker-chromium/ +## +########################################################################### +########################################################################### + chromium: + image: lscr.io/linuxserver/chromium:latest + container_name: chromium + restart: unless-stopped + shm_size: 1gb + volumes: + - ${FOLDER_FOR_DATA:?err}/chromium:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - TITLE=MediaStack Chromium + - CUSTOM_PORT=${WEBUI_PORT_CHROMIUM:?err} + - CHROME_CLI=${CHROMIUM_START_PAGE:?err} + ports: + - ${WEBUI_PORT_CHROMIUM:?err}:${WEBUI_PORT_CHROMIUM:?err} + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.chromium.service=chromium + - traefik.http.routers.chromium.rule=Host(`chromium.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.chromium.entrypoints=secureweb + - traefik.http.routers.chromium.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.chromium.loadbalancer.server.scheme=http + - traefik.http.services.chromium.loadbalancer.server.port=${WEBUI_PORT_CHROMIUM:?err} + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: DDNS-Updater (qmcgaw) +## Function: Update Dynamic IP addresses for DNS A and/or AAAA records +## +## Documentation: https://hub.docker.com/r/qmcgaw/ddns-updater +## +########################################################################### +########################################################################### + ddns-updater: + image: qmcgaw/ddns-updater:latest + container_name: ddns-updater + restart: always + user: ${PUID:?err}:${PGID:?err} + volumes: + - ${FOLDER_FOR_DATA:?err}/ddns-updater:/updater/data + ports: + - ${WEBUI_PORT_DDNS_UPDATER:?err}:${WEBUI_PORT_DDNS_UPDATER:?err}/tcp + environment: + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - CONFIG= + - PERIOD=5m + - UPDATE_COOLDOWN_PERIOD=5m + - PUBLICIP_FETCHERS=all + - PUBLICIP_HTTP_PROVIDERS=all + - PUBLICIPV4_HTTP_PROVIDERS=all + - PUBLICIPV6_HTTP_PROVIDERS=all + - PUBLICIP_DNS_PROVIDERS=all + - PUBLICIP_DNS_TIMEOUT=3s + - HTTP_TIMEOUT=10s + # Web UI + - LISTENING_ADDRESS=:${WEBUI_PORT_DDNS_UPDATER:?err} + - ROOT_URL=/ + # Backup + - BACKUP_PERIOD=0 # 0 to disable + - BACKUP_DIRECTORY=/updater/data + # Other + - LOG_LEVEL=info + - LOG_CALLER=hidden + - SHOUTRRR_ADDRESSES= +# NOTE: DDNS-Update MUST NOT connect to the Gluetun VPN container + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.ddns-updater.service=ddns-updater + - traefik.http.routers.ddns-updater.rule=Host(`ddns-updater.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.ddns-updater.entrypoints=secureweb + - traefik.http.routers.ddns-updater.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.ddns-updater.loadbalancer.server.scheme=http + - traefik.http.services.ddns-updater.loadbalancer.server.port=${WEBUI_PORT_DDNS_UPDATER:?err} + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Filebot (GitHub) +## Function: Lookup and Rename Media from Internet Databases +## +## Docker Page: https://github.com/filebot/filebot-docker#filebot-xpra +## Homepage: https://www.filebot.net/ +## User Forum: https://www.filebot.net/forums +## +########################################################################### +########################################################################### + filebot: + image: rednoah/filebot:xpra + container_name: filebot + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}/filebot:/filebot + - ${FOLDER_FOR_DATA:?err}/filebot:/data/filebot + environment: +# - XPRA_AUTH=password:value=YOUR_PASSWORD + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - DARK_MODE=1 + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_FILEBOT:?err}:5454 # Configured in Gluetun VPN service + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.filebot.service=filebot + - traefik.http.routers.filebot.rule=Host(`filebot.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.filebot.entrypoints=secureweb + - traefik.http.routers.filebot.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.filebot.loadbalancer.server.scheme=http + - traefik.http.services.filebot.loadbalancer.server.port=5454 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Flaresolverr (Flaresolverr) +## Function: Cloudflare Proxy Server +## +## Documentation: https://github.com/FlareSolverr/FlareSolverr +## +########################################################################### +########################################################################### + flaresolverr: + image: ghcr.io/flaresolverr/flaresolverr:latest + container_name: flaresolverr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + environment: + - LOG_LEVEL=info + - LOG_HTML=false + - CAPTCHA_SOLVER=none + - TZ=${TIMEZONE:?err} + network_mode: "service:gluetun" +# ports: +# - ${FLARESOLVERR_PORT:?err}:8191 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.flaresolverr.service=flaresolverr + - traefik.http.routers.flaresolverr.rule=Host(`flaresolverr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.flaresolverr.entrypoints=secureweb + - traefik.http.routers.flaresolverr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.flaresolverr.loadbalancer.server.scheme=http + - traefik.http.services.flaresolverr.loadbalancer.server.port=8191 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Heimdall (LinuxServer.io) +## Function: Organise links to web sites and web applications +## +## Documentation: https://docs.linuxserver.io/images/docker-heimdall +## +########################################################################### +########################################################################### + heimdall: + image: lscr.io/linuxserver/heimdall:latest + container_name: heimdall + restart: unless-stopped + volumes: + - ${FOLDER_FOR_DATA:?err}/heimdall:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + ports: + - ${WEBUI_PORT_HEIMDALL:?err}:80 +# NOTE: Heimdall MUST NOT connect to the Gluetun VPN container + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.heimdall.service=heimdall + - traefik.http.routers.heimdall.rule=Host(`heimdall.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.heimdall.entrypoints=secureweb + - traefik.http.routers.heimdall.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.heimdall.loadbalancer.server.scheme=http + - traefik.http.services.heimdall.loadbalancer.server.port=80 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Homarr (https://ghcr.io/) +## Function: Application Dashboard +## +## Documentation: https://homarr.dev/docs/getting-started/after-the-installation +## +########################################################################### +########################################################################### + homarr: + image: ghcr.io/ajnart/homarr:latest + container_name: homarr + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${FOLDER_FOR_DATA:?err}/homarr/configs:/app/data/configs + - ${FOLDER_FOR_DATA:?err}/homarr/icons:/app/public/icons + - ${FOLDER_FOR_DATA:?err}/homarr/data:/data + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + ports: + - ${WEBUI_PORT_HOMARR:?err}:7575 +# NOTE: Heimdall MUST NOT connect to the Gluetun VPN container + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.homarr.service=homarr + - traefik.http.routers.homarr.rule=Host(`homarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.homarr.entrypoints=secureweb + - traefik.http.routers.homarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.homarr.loadbalancer.server.scheme=http + - traefik.http.services.homarr.loadbalancer.server.port=7575 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Homepage (https://ghcr.io/) +## Function: Application Dashboard +## +## Documentation: https://gethomepage.dev/latest/configs/ +## +########################################################################### +########################################################################### + homepage: + image: ghcr.io/gethomepage/homepage:latest + container_name: homepage + restart: unless-stopped + ports: + - ${WEBUI_PORT_HOMEPAGE:?err}:3000 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${FOLDER_FOR_DATA:?err}/homepage:/app/config + environment: + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - HOMEPAGE_ALLOWED_HOSTS=homepage,homepage.${CLOUDFLARE_DNS_ZONE:?err},${CLOUDFLARE_DNS_ZONE:?err},localhost,${LOCAL_DOCKER_IP:?err} +# NOTE: Homepage MUST NOT connect to the Gluetun VPN container + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.homepage.service=homepage + - traefik.http.routers.homepage.rule=Host(`homepage.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.homepage.entrypoints=secureweb + - traefik.http.routers.homepage.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.homepage.loadbalancer.server.scheme=http + - traefik.http.services.homepage.loadbalancer.server.port=3000 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Huntarr +## Function: ARR Missing Content Manager +## +## Documentation: https://github.com/plexguide/Huntarr.io +## +########################################################################### +########################################################################### + huntarr: + image: huntarr/huntarr:latest + container_name: huntarr + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_DATA:?err}/huntarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_HUNTARR:?err}:9705 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.huntarr.service=huntarr + - traefik.http.routers.huntarr.rule=Host(`huntarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.huntarr.entrypoints=secureweb + - traefik.http.routers.huntarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.huntarr.loadbalancer.server.scheme=http + - traefik.http.services.huntarr.loadbalancer.server.port=9705 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Jellyfin (LinuxServer.io) +## Function: Media Server +## +## Documentation: https://jellyfin.org/docs/general/administration/installing#docker +## https://jellyfin.org/docs/general/administration/hardware-acceleration/ +## +########################################################################### +########################################################################### + jellyfin: + image: lscr.io/linuxserver/jellyfin:latest + container_name: jellyfin + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true +# Add Configurations for GPU Hardware Rendering Here: +# devices: +# - /dev/dri/renderD128:/dev/dri/renderD128 +# - /dev/dri/card0:/dev/dri/card0 + volumes: + - ${FOLDER_FOR_MEDIA:?err}/media:/data/media + - ${FOLDER_FOR_DATA:?err}/jellyfin:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} +# - JELLYFIN_PublishedServerUrl=${LOCAL_DOCKER_IP:?err} # Enable for DLNA - Only works on HOST Network Mode + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_JELLYFIN:?err}:8096 # Configured in Gluetun VPN container +# - 7359:7359/udp # Enable for DLNA - Only works on HOST Network Mode +# - 1900:1900/udp # Enable for DLNA - Only works on HOST Network Mode + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.jellyfin.service=jellyfin + - traefik.http.routers.jellyfin.rule=Host(`jellyfin.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.jellyfin.entrypoints=secureweb + - traefik.http.routers.jellyfin.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.jellyfin.loadbalancer.server.scheme=http + - traefik.http.services.jellyfin.loadbalancer.server.port=8096 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Jellyseerr (fallenbagel) +## Function: Media Request Manager +## +## Documentation: https://hub.docker.com/r/fallenbagel/jellyseerr +## +########################################################################### +########################################################################### + jellyseerr: + image: fallenbagel/jellyseerr:latest + container_name: jellyseerr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_DATA:?err}/jellyseerr:/app/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_JELLYSEERR:?err}:5055 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.jellyseerr.service=jellyseerr + - traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.jellyseerr.entrypoints=secureweb + - traefik.http.routers.jellyseerr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.jellyseerr.loadbalancer.server.scheme=http + - traefik.http.services.jellyseerr.loadbalancer.server.port=5055 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Lidarr (LinuxServer.io) +## Function: Music Library Manager +## +## Documentation: https://docs.linuxserver.io/images/docker-lidarr +## +########################################################################### +########################################################################### + lidarr: + image: lscr.io/linuxserver/lidarr:latest + container_name: lidarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/lidarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:lidarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_LIDARR:?err}:8686 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.lidarr.service=lidarr + - traefik.http.routers.lidarr.rule=Host(`lidarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.lidarr.entrypoints=secureweb + - traefik.http.routers.lidarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.lidarr.loadbalancer.server.scheme=http + - traefik.http.services.lidarr.loadbalancer.server.port=8686 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Mylar3 (LinuxServer.io) +## Function: Comic Library Manager +## +## Documentation: https://github.com/mylar3/mylar3/wiki +## +########################################################################### +########################################################################### + mylar: + image: lscr.io/linuxserver/mylar3:latest + container_name: mylar + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/mylar:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:mylar3 + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_MYLAR:?err}:8090 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.mylar.service=mylar + - traefik.http.routers.mylar.rule=Host(`mylar.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.mylar.entrypoints=secureweb + - traefik.http.routers.mylar.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.mylar.loadbalancer.server.scheme=http + - traefik.http.services.mylar.loadbalancer.server.port=8090 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Plex (LinuxServer.io) +## Function: Media Server +## +## Documentation: https://hub.docker.com/r/linuxserver/plex +## +########################################################################### +########################################################################### + plex: + image: lscr.io/linuxserver/plex:latest + container_name: plex + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true +# Add Configurations for GPU Hardware Rendering Here: +# devices: +# - /dev/dri/renderD128:/dev/dri/renderD128 +# - /dev/dri/card0:/dev/dri/card0 + network_mode: "service:gluetun" +# Ports are disabled in containers when they are connected to VPN, as the +# Gluetun container manages the ports for containers that connect via VPN +# ports: +# - ${WEBUI_PORT_PLEX:?err}:32400 # Configured in Gluetun VPN container +# - 1900:1900/udp +# - 5353:5353/udp +# - 8324:8324 +# - 32410:32410/udp +# - 32412:32412/udp +# - 32413:32413/udp +# - 32414:32414/udp +# - 32469:32469 + volumes: + - ${FOLDER_FOR_MEDIA:?err}/media:/data/media + - ${FOLDER_FOR_DATA:?err}/plex:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - VERSION=docker + - PLEX_CLAIM=${PLEX_CLAIM} + - ADVERTISE_IP=https://plex.${CLOUDFLARE_DNS_ZONE:?err}:443/ + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.plex.service=plex + - traefik.http.routers.plex.rule=Host(`plex.${CLOUDFLARE_DNS_ZONE:?err}`) && PathPrefix(`/web/`) + - traefik.http.routers.plex.entrypoints=secureweb + - traefik.http.routers.plex.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.plex.loadbalancer.server.scheme=http + - traefik.http.services.plex.loadbalancer.server.port=32400 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Portainer (Portainer.io) +## Function: Alternate GUI Manager for Docker +## +## Documentation: https://docs.portainer.io/start/install/server/docker +## +########################################################################### +########################################################################### + portainer: + image: portainer/portainer-ce:latest + container_name: portainer + restart: always + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${FOLDER_FOR_DATA:?err}/portainer:/data + ports: + - ${WEBUI_PORT_PORTAINER:?err}:9000 +# NOTE: Portainer MUST NOT connect to the Gluetun VPN container + networks: + - mediastack + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.portainer.service=portainer + - traefik.http.routers.portainer.rule=Host(`portainer.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.portainer.entrypoints=secureweb + - traefik.http.routers.portainer.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.portainer.loadbalancer.server.scheme=http + - traefik.http.services.portainer.loadbalancer.server.port=9000 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Prowlarr (LinuxServer.io) +## Function: Indexer and Search Manager +## +## Documentation: https://docs.linuxserver.io/images/docker-prowlarr +## +########################################################################### +########################################################################### + prowlarr: + image: lscr.io/linuxserver/prowlarr:develop + container_name: prowlarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_DATA:?err}/prowlarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:prowlarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_PROWLARR:?err}:9696 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.prowlarr.service=prowlarr + - traefik.http.routers.prowlarr.rule=Host(`prowlarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.prowlarr.entrypoints=secureweb + - traefik.http.routers.prowlarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.prowlarr.loadbalancer.server.scheme=http + - traefik.http.services.prowlarr.loadbalancer.server.port=9696 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: qBittorrent (LinuxServer.io) +## Function: Torrent Download Client +## +## Documentation: https://docs.linuxserver.io/images/docker-qbittorrent +## +########################################################################### +########################################################################### + qbittorrent: + image: lscr.io/linuxserver/qbittorrent:latest + container_name: qbittorrent + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/qbittorrent:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - WEBUI_PORT=${WEBUI_PORT_QBITTORRENT:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:qbittorrent + - TP_THEME=${TP_THEME:?err} +## Do Not Change Network for qBittorrent +## qBittorrent MUST always use a VPN / Secure Internet connection + network_mode: "service:gluetun" + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.qbittorrent.service=qbittorrent + - traefik.http.routers.qbittorrent.rule=Host(`qbittorrent.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.qbittorrent.entrypoints=secureweb + - traefik.http.routers.qbittorrent.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.qbittorrent.loadbalancer.server.scheme=http + - traefik.http.services.qbittorrent.loadbalancer.server.port=${WEBUI_PORT_QBITTORRENT:?err} + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Radarr (LinuxServer.io) +## Function: Movie Library Manager +## +## Documentation: https://docs.linuxserver.io/images/docker-radarr +## +########################################################################### +########################################################################### + radarr: + image: lscr.io/linuxserver/radarr:latest + container_name: radarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/radarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:radarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_RADARR:?err}:7878 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.radarr.service=radarr + - traefik.http.routers.radarr.rule=Host(`radarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.radarr.entrypoints=secureweb + - traefik.http.routers.radarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.radarr.loadbalancer.server.scheme=http + - traefik.http.services.radarr.loadbalancer.server.port=7878 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Readarr (LinuxServer.io) +## Function: Book Library Manager +## +## Documentation: https://docs.linuxserver.io/images/docker-readarr +## +########################################################################### +########################################################################### + readarr: + image: lscr.io/linuxserver/readarr:develop + container_name: readarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/readarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:readarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_READARR:?err}:8787 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.readarr.service=readarr + - traefik.http.routers.readarr.rule=Host(`readarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.readarr.entrypoints=secureweb + - traefik.http.routers.readarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.readarr.loadbalancer.server.scheme=http + - traefik.http.services.readarr.loadbalancer.server.port=8787 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: SABnzbd (LinuxServer.io) +## Function: Usenet Download Client +## +## Documentation: https://docs.linuxserver.io/images/docker-sabnzbd +## +########################################################################### +########################################################################### + sabnzbd: + image: lscr.io/linuxserver/sabnzbd:latest + container_name: sabnzbd + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/sabnzbd:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:sabnzbd + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_SABNZBD:?err}:8080 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.sabnzbd.service=sabnzbd + - traefik.http.routers.sabnzbd.rule=Host(`sabnzbd.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.sabnzbd.entrypoints=secureweb + - traefik.http.routers.sabnzbd.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.sabnzbd.loadbalancer.server.scheme=http + - traefik.http.services.sabnzbd.loadbalancer.server.port=8080 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Sonarr (LinuxServer.io) +## Function: Series Library Manager (TV Shows) +## +## Documentation: https://docs.linuxserver.io/images/docker-sonarr +## +########################################################################### +########################################################################### + sonarr: + image: lscr.io/linuxserver/sonarr:latest + container_name: sonarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/sonarr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - TZ=${TIMEZONE:?err} + - DOCKER_MODS=ghcr.io/themepark-dev/theme.park:sonarr + - TP_THEME=${TP_THEME:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_SONARR:?err}:8989 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.sonarr.service=sonarr + - traefik.http.routers.sonarr.rule=Host(`sonarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.sonarr.entrypoints=secureweb + - traefik.http.routers.sonarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.sonarr.loadbalancer.server.scheme=http + - traefik.http.services.sonarr.loadbalancer.server.port=8989 + # MIDDLEWARES + +########################################################################### +########################################################################### +## +## Docker Compose File: Tdarr V2 (haveagitgat/tdarr) +## Function: Tdarr V2 - Audio/Video library transcoding automation +## (Contains Tdarr_Server and WebUI ) +## +## Documentation: https://docs.tdarr.io/docs/installation/docker/run-compose/ +## https://docs.tdarr.io/docs/installation/docker/hardware-transcoding +## +########################################################################### +########################################################################### + tdarr: + image: ghcr.io/haveagitgat/tdarr:latest + container_name: tdarr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}/media:/data + - ${FOLDER_FOR_DATA:?err}/tdarr/server:/app/server + - ${FOLDER_FOR_DATA:?err}/tdarr/configs:/app/configs + - ${FOLDER_FOR_DATA:?err}/tdarr/logs:/app/logs + - ${FOLDER_FOR_DATA:?err}/tdarr-node:/temp + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - serverIP=0.0.0.0 + - serverPort=${TDARR_SERVER_PORT:?err} + - webUIPort=${WEBUI_PORT_TDARR:?err} + - internalNode=true + - nodeID=Tdarr_Server + network_mode: "service:gluetun" +# ports: +# - ${TDARR_SERVER_PORT:?err}:${TDARR_SERVER_PORT:?err} # Configured in Gluetun VPN container +# - ${WEBUI_PORT_TDARR:?err}:${WEBUI_PORT_TDARR:?err} # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.tdarr.service=tdarr + - traefik.http.routers.tdarr.rule=Host(`tdarr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.tdarr.entrypoints=secureweb + - traefik.http.routers.tdarr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.tdarr.loadbalancer.server.scheme=http + - traefik.http.services.tdarr.loadbalancer.server.port=${WEBUI_PORT_TDARR:?err} + # MIDDLEWARES + + tdarr-node: + image: ghcr.io/haveagitgat/tdarr_node:latest + container_name: tdarr-node + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}/media:/data + - ${FOLDER_FOR_DATA:?err}/tdarr/configs:/app/configs + - ${FOLDER_FOR_DATA:?err}/tdarr/logs:/app/logs + - ${FOLDER_FOR_DATA:?err}/tdarr-node:/temp + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + - nodeID=Tdarr_Node_1 + - serverIP=0.0.0.0 + - serverPort=${TDARR_SERVER_PORT:?err} + network_mode: "service:gluetun" + +########################################################################### +########################################################################### +## +## Docker Compose File: Unpackerr (Hotio.Dev) +## Function: Archive Media Extraction +## +## Documentation: https://github.com/davidnewhall/unpackerr +## https://github.com/davidnewhall/unpackerr/blob/master/examples/docker-compose.yml +## +########################################################################### +########################################################################### + unpackerr: + image: golift/unpackerr + container_name: unpackerr + restart: unless-stopped + user: ${PUID:?err}:${PGID:?err} + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/unpackerr:/config + - ${FOLDER_FOR_DATA:?err}/logs/unpackerr:/var/log + networks: + - mediastack + ports: + - ${METRICS_PORT_UNPACKERR:?err}:5656 + environment: + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + # Remove all lines that begin with UN_CMDHOOK, UN_WEBHOOK, UN_FOLDER, UN_WEBSERVER, and other apps you do not use. + ## Global Settings + - UN_DEBUG=false + - UN_QUIET=false + - UN_ERROR_STDERR=false + - UN_ACTIVITY=false + - UN_LOG_QUEUES=1m + - UN_LOG_FILE=/var/log/unpackerr.log + - UN_LOG_FILES=10 + - UN_LOG_FILE_MB=10 + - UN_LOG_FILE_MODE=0644 + - UN_INTERVAL=2m + - UN_START_DELAY=1m + - UN_RETRY_DELAY=5m + - UN_MAX_RETRIES=3 + - UN_PARALLEL=1 + - UN_FILE_MODE=0644 + - UN_DIR_MODE=2755 + ## Web Server + - UN_WEBSERVER_METRICS=true + - UN_WEBSERVER_LISTEN_ADDR=0.0.0.0:5656 + - UN_WEBSERVER_LOG_FILE=/var/log/server.log + - UN_WEBSERVER_LOG_FILES=10 + - UN_WEBSERVER_LOG_FILE_MB=10 + - UN_WEBSERVER_SSL_CERT_FILE= + - UN_WEBSERVER_SSL_KEY_FILE= + - UN_WEBSERVER_URLBASE=/ + - UN_WEBSERVER_UPSTREAMS= + ## Folder Settings + - UN_FOLDERS_INTERVAL=1s + - UN_FOLDERS_BUFFER=20000 + ## Mylar Settings + ## Mylar Config - Copy API Key from: http://mylar:8090/general/settings + - UN_MYLAR_0_URL=http://mylar:8090 + - UN_MYLAR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_MYLAR_0_PATHS_0=/data/torrents/comics + - UN_MYLAR_0_PROTOCOLS=torrent + - UN_MYLAR_0_TIMEOUT=10s + - UN_MYLAR_0_DELETE_DELAY=5m + - UN_MYLAR_0_DELETE_ORIG=false + - UN_MYLAR_0_SYNCTHING=false + ## Sonarr Settings + ## Sonarr Config - Copy API Key from: http://sonarr:8989/general/settings + - UN_SONARR_0_URL=http://sonarr:8989 + - UN_SONARR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_SONARR_0_PATHS_0=/data/torrents/anime + - UN_SONARR_0_PATHS_1=/data/torrents/tv + - UN_SONARR_0_PROTOCOLS=torrent + - UN_SONARR_0_TIMEOUT=10s + - UN_SONARR_0_DELETE_DELAY=5m + - UN_SONARR_0_DELETE_ORIG=false + - UN_SONARR_0_SYNCTHING=false + ## Radarr Settings + ## Radarr Config - Copy API Key from: http://radarr:7878/general/settings + - UN_RADARR_0_URL=http://radarr:7878 + - UN_RADARR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_RADARR_0_PATHS_0=/data/torrents/movies + - UN_RADARR_0_PROTOCOLS=torrent + - UN_RADARR_0_TIMEOUT=10s + - UN_RADARR_0_DELETE_DELAY=5m + - UN_RADARR_0_DELETE_ORIG=false + - UN_RADARR_0_SYNCTHING=false + ## Lidarr Settings + ## Lidarr Config - Copy API Key from: http://lidarr:8686/general/settings + - UN_LIDARR_0_URL=http://lidarr:8686 + - UN_LIDARR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_LIDARR_0_PATHS_0=/data/torrents/music + - UN_LIDARR_0_PROTOCOLS=torrent + - UN_LIDARR_0_TIMEOUT=10s + - UN_LIDARR_0_DELETE_DELAY=5m + - UN_LIDARR_0_DELETE_ORIG=false + - UN_LIDARR_0_SYNCTHING=false + ## Readarr Settings + ## Readarr Config - Copy API Key from: http://readarr:8787/general/settings + - UN_READARR_0_URL=http://readarr:8787 + - UN_READARR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_READARR_0_PATHS_0=/data/torrents/books + - UN_READARR_0_PROTOCOLS=torrent + - UN_READARR_0_TIMEOUT=10s + - UN_READARR_0_DELETE_DELAY=5m + - UN_READARR_0_DELETE_ORIG=false + - UN_READARR_0_SYNCTHING=false + ## Whisparr Settings + ## Whisparr Config - Copy API Key from: http://readarr:6969/general/settings + - UN_WHISPARR_0_URL=http://whisparr:6969 + - UN_WHISPARR_0_API_KEY=0123456789abcdef0123456789abcdef + - UN_WHISPARR_0_PATHS_0=/data/torrents/xxx + - UN_WHISPARR_0_PROTOCOLS=torrent + - UN_WHISPARR_0_TIMEOUT=10s + - UN_WHISPARR_0_DELETE_DELAY=5m + - UN_WHISPARR_0_DELETE_ORIG=false + - UN_WHISPARR_0_SYNCTHING=false + ## Watch Folders + - UN_FOLDER_0_PATH=/data/torrents/complete + - UN_FOLDER_0_EXTRACT_PATH= + - UN_FOLDER_0_DELETE_AFTER=10m + - UN_FOLDER_0_DISABLE_RECURSION=false + - UN_FOLDER_0_DELETE_FILES=false + - UN_FOLDER_0_DELETE_ORIGINAL=false + - UN_FOLDER_0_DISABLE_LOG=false + - UN_FOLDER_0_MOVE_BACK=false + - UN_FOLDER_0_EXTRACT_ISOS=false + ## Web Hooks + - UN_WEBHOOK_0_URL=https://notifiarr.com/api/v1/notification/unpackerr/api_key_from_notifiarr_com + - UN_WEBHOOK_0_NAME= + - UN_WEBHOOK_0_SILENT=false + - UN_WEBHOOK_0_EVENTS_0=1 + - UN_WEBHOOK_0_EVENTS_1=4 + - UN_WEBHOOK_0_EVENTS_2=6 + - UN_WEBHOOK_0_NICKNAME=Unpackerr + - UN_WEBHOOK_0_CHANNEL= + - UN_WEBHOOK_0_EXCLUDE_0=readarr + - UN_WEBHOOK_0_EXCLUDE_1=lidarr + - UN_WEBHOOK_0_TEMPLATE_PATH= + - UN_WEBHOOK_0_TEMPLATE= + - UN_WEBHOOK_0_IGNORE_SSL=false + - UN_WEBHOOK_0_TIMEOUT=10s + - UN_WEBHOOK_0_CONTENT_TYPE=application/json + ## Command Hooks + - UN_CMDHOOK_0_COMMAND=/data/torrents/unpackerr.sh + - UN_CMDHOOK_0_NAME= + - UN_CMDHOOK_0_SHELL=false + - UN_CMDHOOK_0_SILENT=false + - UN_CMDHOOK_0_EVENTS_0=1 + - UN_CMDHOOK_0_EVENTS_1=4 + - UN_CMDHOOK_0_EVENTS_2=7 + - UN_CMDHOOK_0_EXCLUDE_0=readarr + - UN_CMDHOOK_0_EXCLUDE_1=lidarr + - UN_CMDHOOK_0_TIMEOUT=10s + +########################################################################### +########################################################################### +## +## Docker Compose File: Whisparr (Hotio.Dev) +## Function: Adult Media Library Manager +## +## Documentation: https://wiki.servarr.com/whisparr +## +########################################################################### +########################################################################### + whisparr: + image: hotio/whisparr:nightly + container_name: whisparr + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + restart: true + volumes: + - ${FOLDER_FOR_MEDIA:?err}:/data + - ${FOLDER_FOR_DATA:?err}/whisparr:/config + environment: + - PUID=${PUID:?err} + - PGID=${PGID:?err} + - UMASK=${UMASK:?err} + - TZ=${TIMEZONE:?err} + network_mode: "service:gluetun" +# ports: +# - ${WEBUI_PORT_WHISPARR:?err}:6969 # Configured in Gluetun VPN container + labels: + - traefik.enable=true + # ROUTERS + - traefik.http.routers.whisparr.service=whisparr + - traefik.http.routers.whisparr.rule=Host(`whisparr.${CLOUDFLARE_DNS_ZONE:?err}`) + - traefik.http.routers.whisparr.entrypoints=secureweb + - traefik.http.routers.whisparr.middlewares=authentik-forwardauth@file,security-headers@file,traefik-bouncer@file + # SERVICES + - traefik.http.services.whisparr.loadbalancer.server.scheme=http + - traefik.http.services.whisparr.loadbalancer.server.port=6969 + # MIDDLEWARES diff --git a/authelia/compose.yaml b/authelia/compose.yaml new file mode 100644 index 0000000..a13fc7f --- /dev/null +++ b/authelia/compose.yaml @@ -0,0 +1,20 @@ +networks: + net: + driver: bridge + +volumes: + config: + +services: + authelia: + container_name: authelia + image: docker.io/authelia/authelia:latest + restart: unless-stopped + networks: + net: {} + ports: + - 9091:9091 + environment: + TZ: ${TZ} + volumes: + - config:/config diff --git a/authelia/configuration.yml b/authelia/configuration.yml new file mode 100644 index 0000000..fe831b0 --- /dev/null +++ b/authelia/configuration.yml @@ -0,0 +1,56 @@ +--- +############################################################### +# Authelia configuration # +############################################################### + +server: + address: 'tcp://:9091' + +log: + level: 'debug' + +totp: + issuer: 'authelia.com' + +identity_validation: + reset_password: + jwt_secret: 'S3SorNdAWR786SeTP7a9MBNQQbSqRTGFitqRtemT7VD6PQtmQHsXhPGgs7bfKTvp' + +authentication_backend: + file: + path: '/config/users_database.yml' + +access_control: + default_policy: 'deny' + rules: + - domain: 'www.delmar.bzh' + policy: 'bypass' + - domain: 'dkr.delmar.bzh' + policy: 'one_factor' + - domain: 'cloud.delmar.bzh' + policy: 'two_factor' + +session: + secret: 'M7SQ7ornWrDas6ienGHmRJaNrX93KNLCTi8ggM3QMiKKcCdjAwyJrLFK3oSVgsfG' + + cookies: + - name: 'authelia_session' + domain: 'delmar.bzh' # Should match whatever your root protected domain is + authelia_url: 'https://auth.delmar.bzh' + expiration: '1 hour' # 1 hour + inactivity: '5 minutes' # 5 minutes + default_redirection_url: 'https://public.example.com' + +regulation: + max_retries: 3 + find_time: '2 minutes' + ban_time: '5 minutes' + +storage: + encryption_key: 'yxmVn7chQz6PWEwPyXYSnHo9y42sWMfEXvLxRA6wPsZdj5Jb2ZGM9dVjgUTUSnbx' + local: + path: '/config/db.sqlite3' + +notifier: + filesystem: + filename: '/config/notification.txt' diff --git a/authelia/users_database.yml b/authelia/users_database.yml new file mode 100644 index 0000000..6c8d325 --- /dev/null +++ b/authelia/users_database.yml @@ -0,0 +1,17 @@ +--- +############################################################### +# Users Database # +############################################################### + +# This file can be used if you do not have an LDAP set up. + +# List of users +users: + admin: + disabled: false + displayname: 'Admin' + password: 'X5r53JMPVg97EKfL' + email: 'admin@delmar.bzh' + groups: + - 'admins' + - 'dev' diff --git a/authentik/authentik Self-signed Certificate_certificate.pem b/authentik/authentik Self-signed Certificate_certificate.pem new file mode 100644 index 0000000..b3b7a62 --- /dev/null +++ b/authentik/authentik Self-signed Certificate_certificate.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFUzCCAzugAwIBAgIRAO/ljk7+ck+qhez8D5V3f1AwDQYJKoZIhvcNAQELBQAw +HTEbMBkGA1UEAwwSYXV0aGVudGlrIDIwMjUuMi40MB4XDTI1MDUyNDIyMjAxMVoX +DTI2MDUyNTIyMjAxMVowVjEqMCgGA1UEAwwhYXV0aGVudGlrIFNlbGYtc2lnbmVk +IENlcnRpZmljYXRlMRIwEAYDVQQKDAlhdXRoZW50aWsxFDASBgNVBAsMC1NlbGYt +c2lnbmVkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyz/EnjizgNvT +uKX/mDn+IwWJWFKAphVuNtCmg0dWv79JofaI8R16QXpKL4WGTByK9CzPpE/IsvRs +iS04WEV+FUVTNG+CCSZUuZKqgl20XiFk5QTDa1RUXrHWKS4owMtppnLy/gO6/cpJ +PvYOwO0RVmAQBohCukPoG/vjUh/Jyd3sSdq5A9K6TgcGUZk8SrFgW1RWA5dn+F7p +ssEVjLst96bMFknD8zfQ8V9cQOKsHf6RmCTtiz0tZ6P/QxSoWRnB19oNnQESuau7 +XMTJSYka4LnharSuZFeYqfnUYFlDScsrEivouOdo05TtbBRbLHDaVjXCWfe7UOsl +zzLYPu08RfPAlgLQXN1vhEroJNbY/8jMD0bQ5cogMMzhllBm6Z0pJgWgX+ZiKTHg +KDxKhz/z9FUpw99W9tEKH4lknKz5aFINr6ZthTnTV3fOxhTU49feak6jFj18ssxd +yzpfoMg8xjfqqA5aA+yKfOmxxGJYJhuZGtXZXc1yqAwa9RjGbCd6En0yqk96QfFL +TvQYX9EKCDfnYFS8mqLOIotsnqDpYr8Zehr8aCUWlTegRe7H8/KcMe/uwhfOpZ4i ++gkwC36gIwRXi10kH8gkQyw61YKd9uIJFYUZjaZQNxZFNQqMixownKTqRPru6YUN +rD3Kgu+EI4gVh8gUJ/U3jBgV36c9moECAwEAAaNVMFMwUQYDVR0RAQH/BEcwRYJD +T3QzN2U3NGIwRDJyOFU4ZlpFeUdyR3I2Wms4WGFWYXM4QnE5S1NNZS5zZWxmLXNp +Z25lZC5nb2F1dGhlbnRpay5pbzANBgkqhkiG9w0BAQsFAAOCAgEAMNfTfHxWxi+I +3wUnIyW6w8+m41Uz5OIBtYjGNQrZN1TeP0lW7Ciw9C2Q4WF0gD5EFEilSvLt87Im +L5KI5VpdOprdD8uJ1+Hpv2UJOLiWf9Ii40WrP0R/pD9BuNRf96XAHLwj/nGnnT4k +OFFLdMap3L7ivAA5scj2G4P5q6dEJgVg6T2iM7IAcmUi33uVHzK+lXfNqlDp/fVE +0I/W6KZGEV2fCAf+B2eqjqyBVVe9WEGnvxxMR1Bsmajkh3lwyomXdYCYWMHe11QT +rYy7If3RJPTVD/gSuzm7ybw7eL55Jmzdn4boOLUl4WU/X33fliC8vFlPE+beWaRD +eUziXSdq9lqLgvARE53EcSTUASraqYsxgsHhrNkMmOEOQUw60vcpmKPxic5B2/Lr +etJQ41fNquVu8EqZax67Yfo/saxcuC8EgX0gEITjO33j4ERPoTRI8b0Vy0MpaegU +du8+vJMvW2x+1G6TXbZkSBzS0TbfzzC0tkfZJl9rugLPGfu9Btb65oXTKo2IDklp +CmapiK4lWBMfnSWNgDS5jBcYV6/1xFH3ZnzbOWIIWMTVIC2OqhiC8dvWeMrrq4nc +ypkYJsTVUyxAzaSw5VU2Da7sy1IDhz+HTDYf54Z900M4B2LUUlRQsF7bzN0H/w3G +jGcUd5es8VCJBMmaHx2iCDz+Dic3hqA= +-----END CERTIFICATE----- diff --git a/authentik/authentik Self-signed Certificate_private_key.pem b/authentik/authentik Self-signed Certificate_private_key.pem new file mode 100644 index 0000000..331d009 --- /dev/null +++ b/authentik/authentik Self-signed Certificate_private_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAyz/EnjizgNvTuKX/mDn+IwWJWFKAphVuNtCmg0dWv79JofaI +8R16QXpKL4WGTByK9CzPpE/IsvRsiS04WEV+FUVTNG+CCSZUuZKqgl20XiFk5QTD +a1RUXrHWKS4owMtppnLy/gO6/cpJPvYOwO0RVmAQBohCukPoG/vjUh/Jyd3sSdq5 +A9K6TgcGUZk8SrFgW1RWA5dn+F7pssEVjLst96bMFknD8zfQ8V9cQOKsHf6RmCTt +iz0tZ6P/QxSoWRnB19oNnQESuau7XMTJSYka4LnharSuZFeYqfnUYFlDScsrEivo +uOdo05TtbBRbLHDaVjXCWfe7UOslzzLYPu08RfPAlgLQXN1vhEroJNbY/8jMD0bQ +5cogMMzhllBm6Z0pJgWgX+ZiKTHgKDxKhz/z9FUpw99W9tEKH4lknKz5aFINr6Zt +hTnTV3fOxhTU49feak6jFj18ssxdyzpfoMg8xjfqqA5aA+yKfOmxxGJYJhuZGtXZ +Xc1yqAwa9RjGbCd6En0yqk96QfFLTvQYX9EKCDfnYFS8mqLOIotsnqDpYr8Zehr8 +aCUWlTegRe7H8/KcMe/uwhfOpZ4i+gkwC36gIwRXi10kH8gkQyw61YKd9uIJFYUZ +jaZQNxZFNQqMixownKTqRPru6YUNrD3Kgu+EI4gVh8gUJ/U3jBgV36c9moECAwEA +AQKCAgAUzH0/3biMxrIXUrFhsAmQJuhjp+mvVUEWjeqEQ6AediJAVzyzQ9t3aN6/ +ly/jiQY1L4K+I0X7/iQ/M2fxLIdXAOWfV4vciNYhuV8GgTVMbqlnAEJJMPODTzqF +s0oLVKWGmXvBP7t6biFptn9TAc+E4cayDG6bpghSGMgt5r/dDv83nZDSUdWr128v +mXlAgX9FSU+WBX/nvDq5BqZAZQUysxhubXUGZh9VG/eSUj/fv+XBRoIifguv7+nW +wP3rREYAYzmCaBI0+txtBVw90LB177+gDkZMUWR3fG/dvjSzD/3j2adyiIfDIxna +tgiQdyn4qX6ueznpcW7IkcDsS5Wfui/RXwpF8v+jpMAp74/+tufrWW09cntWPO9R +cYGUWuGEAaGVRxLMLhKoYu+xh1vXhpjzEoHis4gGu3Eyi/T2bfH1Uy6BFfaIaRbx +Si6QMhF8pDiTCjxuNqn6fT1TopVgbRFYIWEv2cUMiXKetWnwwtMMzHE4Wp9fWa/y +Ifteq34wy7DJj0tXW05alfhDjlxhtFPv6b06Fd9YL9+OaIA1T1bcx+gQjgYXBcYp +u+mNTSvtWVhEkyALcho8d7tVnxd7UvhrqTbuim63sfDdYrapchDgWp9vRRlR4MKw +KtxWO1s35u+wO6IIJ7JH6/tjyuQKP+xirsN1bPGW4BKy9witOQKCAQEA7+LT/ycm +WR22esY04a2ijzVCUbMoOpPrmRQdUK64r08+K7YfPpaUmKJHYMtCDzYFyoKIhU4I +zF2S3HN0/CVBjSCLqgIeVvLSuoZ+B0Vu2lvVxcgtGBaHI9/KMa78DKx4KDwksQ2v +8nATNqVvN8KvERR/Xu+9RgTqFx7yHCPeK7kr1VM1PqIofBywzFxtEPseW8toY1+B +oZ3nXGe9jkQgf2SeS7gogebsvqBfuSFZ0mNYyzQ//Y34g/wwR0puKbi0baah2k6V +KaAN+4u6y2UwNjl+St1KOwEyn2q+Q4KZ2X/pyg57i6j/MJUJ4kmf/iHP4m6vNR8e +088xMEn6Fv7BdQKCAQEA2ObquuZYf6PeQA9cDUvz0SVEf61Fjw9YJPOcbd0b0lCp +rsIeRpGHxxyJZuXgs2jLMlzPJ7AOvBm68O3L3L7CqrnAN+XdxiEIrQu57ySZcH77 +6lGicJlSpa3ihri9n8sNGEKTEEsUItYhdYwWkCdVE7mCRXXNWU7wNUEvobWYu+qS +33eU+XLS1YUel2SNebDRNjpOVpcbSonWZsdpYfF567i/YWAieIEiYT/k6KqLcLLm +84og3Ri+4niH1fHasiKQQhLbNeUk4ItDxAtfXWunNXJSGSEbrlDoZ6Orwh1PpP0l +q49hGTk2UlEkDjtJ4cJS4pGoEijij7417gQCDManXQKCAQA6OYEhRjxdlwOqqx88 +w6tYPSIauHSEO1EM97/hzHbRPlelvm+4/k2U+pZ+nPcsv/0J2UMPsBAcon25+mAj +r1tIyOR9YERC8G9LjMdcNnMglve9KMghN56Xi4MFKibNYLNNeMOjIeJZ4vAW7HKh +QyTA7Yu43FHBhAHZmM5FHEOZszk7bxXWXP+cmE4lXC2w2HLW7vkoXY8WTkBQwnFU +7Z5BrYdqc4mJdiZyYnS5d6nYkfqEX+tEPMhcKMMDGsLAZ6+Lic1xMm+sCcg7DQBK +zAW2s5++xhV1WuYRoZQKyNFU5GI3g+pizKMcBKDAhwRJZ6WQnTLzntkZqgmTYfFM +0YrdAoIBADhnA88KKyU15Yte8x76CzLtr/xLgHUNFmy1dI4VGzD7jMRG8jyDh309 +bHV3fZDpxT0ZaTz2yT4cqsIP9B049cITf2qJubvVxz3IeNc4+zWHr9Zk2ArTs+wl +ZWyHCbhNwt/2H+vyug+9AwJgJVXjBHqWmMubMITyw//1LX+ti0QCYaL+o5yiQTUI +4sWXfjqQwjaJvulBbfVaFoivfjXb50pmxk16BDy1x8zmIo5mGtB5lRjraFRWJP0b +Pqdhtu7vkfe8k4hWR9qkHEEUe/39CoQZcEPcmiggHe8lXnqhyJ4svej45dT7Tabm +NqwEutecApWsLrpaK2AZpEh6flX4KnkCggEBAJdICoVNsQasg6hAIEdUjMQXm8Mc +mXDLCi8P4V01RhJlAAASjpQ2a46+fUvCLinSMwOzPRDj46D/8fw+F0poRahRYlk+ +W2oM8DPmU8+HrJghsxGicU3fOrDlalttwzxDxuazCTwO3kNpAjNRc0YbaGhwb4uK +OZjMtC8BUrOnKAXhw1sEipMUnN79lH+W3YiMfltpAAp2m1Owusfu1nMBkGMcHRT/ +T4jF02rgY3sx63LzVvPSRm6Qg638jUuQPS+yxmwGHa3Z4409DxB0yPoq3OW4bPhO +j/k9QLXdABqMYL6F14Am2fcuRDofCBgekbfn1fh3Cdmf8YMxGZWWWWoE3tE= +-----END RSA PRIVATE KEY----- diff --git a/authentik/compose.env b/authentik/compose.env new file mode 100644 index 0000000..3f0d9b8 --- /dev/null +++ b/authentik/compose.env @@ -0,0 +1,16 @@ +COMPOSE_PORT_HTTP=62751 +COMPOSE_PORT_HTTPS=62752 +PG_PASS=7n7mXBhgI2wOUm1CAEErgbnWX7wK9fNn6wXH2eVnZSfadUEt +AUTHENTIK_SECRET_KEY=3R85z4JsPEKLKSFi+oxaoKypRlFkGctjElmNSiLzyafKlWfcRr+Y62yHfTBfedY19pv2fswhGL2F8TbU +AUTHENTIK_BOOTSTRAP_EMAIL=admin@delmar.bzh +AUTHENTIK_BOOTSTRAP_PASSWORD=X5r53JMPVg97EKfL +AUTHENTIK_BOOTSTRAP_TOKEN=rzDMUWQOxOWQIYXPcVY2XKlwgTau4vOSfaSfVYsLWQQSUUITAVDY3lqwvoTbUMVD +AUTHENTIK_ERROR_REPORTING__ENABLED=true +AUTHENTIK_EMAIL__HOST=pro1.mail.ovh.net +AUTHENTIK_EMAIL__PORT=587 +AUTHENTIK_EMAIL__USERNAME=admin@delmar.bzh +AUTHENTIK_EMAIL__PASSWORD=sxS4GA8rBfmFkCFL +AUTHENTIK_EMAIL__USE_TLS=true +AUTHENTIK_EMAIL__USE_SSL=false +AUTHENTIK_EMAIL__TIMEOUT=10 +AUTHENTIK_EMAIL__FROM=admin@delmar.bzh diff --git a/authentik/compose.yaml b/authentik/compose.yaml new file mode 100644 index 0000000..26b0185 --- /dev/null +++ b/authentik/compose.yaml @@ -0,0 +1,95 @@ +volumes: + database: + redis: + +services: + postgresql: + container_name: authentik_postgres + image: docker.io/library/postgres:16-alpine + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - database:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: ${PG_PASS:?database password required} + POSTGRES_USER: ${PG_USER:-authentik} + POSTGRES_DB: ${PG_DB:-authentik} +# env_file: +# - compose.env + + redis: + container_name: authentik_redis + image: docker.io/library/redis:alpine + command: --save 60 1 --loglevel warning + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep PONG"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + volumes: + - redis:/data + + server: + container_name: authentik_server + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.0} + restart: unless-stopped + command: server + environment: + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + volumes: + - ./media:/media + - ./custom-templates:/templates +# env_file: +# - compose.env + ports: + - "${COMPOSE_PORT_HTTP:-9000}:9000" + - "${COMPOSE_PORT_HTTPS:-9443}:9443" + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy + + worker: + container_name: authentik_worker + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.0} + restart: unless-stopped + command: worker + environment: + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + # `user: root` and the docker socket volume are optional. + # See more for the docker socket integration here: + # https://goauthentik.io/docs/outposts/integrations/docker + # Removing `user: root` also prevents the worker from fixing the permissions + # on the mounted folders, so when removing this make sure the folders have the correct UID/GID + # (1000:1000 by default) + user: root + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./media:/media + - ./certs:/certs + - ./custom-templates:/templates +# env_file: +# - compose.env + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy diff --git a/authentik/nextcloud.txt b/authentik/nextcloud.txt new file mode 100644 index 0000000..319df5b --- /dev/null +++ b/authentik/nextcloud.txt @@ -0,0 +1,2 @@ +Rr23j09gXB66exXkRg8GK0Yc8XvTM9wZc1jc30MV +AZvPpQLKhUrLVZwpczXeCkQvi2tipsX1pcMgIYzD23TZ5bCNUo9warxrRxduFTXUo9tG3bIpyDying2ei2pUuJPbm3LJbuONydJsLa15Dtns6yjsIfK4sgfShRmGltUu diff --git a/bash/docker-volumes.sh b/bash/docker-volumes.sh new file mode 100644 index 0000000..94e3b02 --- /dev/null +++ b/bash/docker-volumes.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# +# The docker-export and docker-commit/docker-save commands do not save the container volumes. +# Use this script to save and load the container volumes. +# +# v1.8 by Ricardo Branco +# +# NOTES: +# + This script could have been written in Python or Go, but the tarfile module and the tar package +# lack support for writing sparse files. +# + We use the Ubuntu 22.04 Docker image with tar v1.29 that uses SEEK_DATA/SEEK_HOLE to manage sparse files. +# + +verbose="" +if [[ $1 == "-v" || $1 == "--verbose" ]] ; then + verbose="-v" + shift +fi + +if [[ $# -ne 3 || ! $2 =~ ^(save|load)$ ]] ; then + echo "Usage: $0 [-v|--verbose] CONTAINER [save|load] TARBALL" >&2 + exit 1 +fi + +IMAGE="ubuntu:22.04" + +# Set DOCKER=podman if you want to use podman.io instead of docker +DOCKER=${DOCKER:-"docker"} + +get_volumes () { + $DOCKER inspect --type container -f '{{range .Mounts}}{{printf "%v\x00" .Destination}}{{end}}' "$CONTAINER" | head -c -1 | sort -uz +} + +save_volumes () { + if [ -f "$TAR_FILE" ] ; then + echo "ERROR: $TAR_FILE already exists" >&2 + exit 1 + fi + umask 077 + # Create a void tar file to avoid mounting its directory as a volume + touch -- "$TAR_FILE" + tmp_dir=$(mktemp -du -p /) + get_volumes | $DOCKER run --rm -i --volumes-from "$CONTAINER" -e LC_ALL=C.UTF-8 -v "$TAR_FILE:/${tmp_dir}/${TAR_FILE##*/}" $IMAGE tar -c -a $verbose --null -T- -f "/${tmp_dir}/${TAR_FILE##*/}" +} + +load_volumes () { + if [ ! -f "$TAR_FILE" ] ; then + echo "ERROR: $TAR_FILE doesn't exist in the current directory" >&2 + exit 1 + fi + tmp_dir=$(mktemp -du -p /) + $DOCKER run --rm --volumes-from "$CONTAINER" -e LC_ALL=C.UTF-8 -v "$TAR_FILE:/${tmp_dir}/${TAR_FILE##*/}":ro $IMAGE tar -xp $verbose -S -f "/${tmp_dir}/${TAR_FILE##*/}" -C / --overwrite +} + +CONTAINER="$1" +TAR_FILE=$(readlink -f "$3") + +set -e + +case "$2" in + save) + save_volumes ;; + load) + load_volumes ;; +esac diff --git a/bash/update_all_docker_containers.sh b/bash/update_all_docker_containers.sh new file mode 100644 index 0000000..ff83f27 --- /dev/null +++ b/bash/update_all_docker_containers.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -e + +# Function to preserve and update container +preserve_and_update_container() { + local container=$1 + local image=$(docker inspect --format '{{.Config.Image}}' "$container") + + # Pull the latest image version + docker pull $image + + # Compare image IDs to determine if an update is needed + local latest_image_id=$(docker inspect --format '{{.Id}}' $image) + local container_image_id=$(docker inspect --format '{{.Image}}' "$container") + + if [[ "$latest_image_id" != "$container_image_id" ]]; then + echo "Updating $container..." + + # Capture current configurations + local env_vars=$(docker inspect $container --format '{{range .Config.Env}}{{println .}}{{end}}') + local volumes=$(docker inspect $container --format '{{range .Mounts}}{{println .Source ":" .Destination}}{{end}}') + local network=$(docker network ls --filter id=$(docker inspect $container --format '{{.HostConfig.NetworkMode}}') --format '{{.Name}}') + + # Remove the outdated container + docker rm -f $container + + # Recreate the container with the same configurations + docker run -d --name $container $(echo "$env_vars" | xargs -I {} echo --env '{}') $(echo "$volumes" | xargs -I {} echo -v '{}') --network="$network" $image + echo "$container updated successfully." + else + echo "$container is already up to date." + fi +} + +# Iterate over all running containers +for container in $(docker ps --format "{{.Names}}"); do + preserve_and_update_container $container +done + +echo "Container update check complete while preserving existing container configurations." diff --git a/bracket/compose.yaml b/bracket/compose.yaml new file mode 100644 index 0000000..7327adc --- /dev/null +++ b/bracket/compose.yaml @@ -0,0 +1,57 @@ +volumes: + bracket_static: + bracket_pg_data: + +services: + bracket-frontend: + container_name: bracket-frontend + image: ghcr.io/evroon/bracket-frontend + environment: + NODE_ENV: "production" + NEXT_PUBLIC_API_BASE_URL: http://localhost:8400 + # NEXT_PUBLIC_HCAPTCHA_SITE_KEY: 10000000-ffff-ffff-ffff-000000000001 + ports: + - "3000:3000" + restart: unless-stopped + + bracket-backend: + container_name: bracket-backend + image: ghcr.io/evroon/bracket-backend + depends_on: + - postgres + environment: + ENVIRONMENT: "PRODUCTION" + CORS_ORIGINS: http://localhost:3000 + PG_DSN: "postgresql://admin:rLAPG2RDeozyfgxr@postgres:5432/bracket_db" + JWT_SECRET: 4GEpdr4rFTcR637M + ADMIN_EMAIL: ${ADMIN_EMAIL} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + ALLOW_USER_REGISTRATION: true + AUTO_RUN_MIGRATIONS: true + ports: + - "8400:8400" + restart: unless-stopped + volumes: + - bracket_static:/app/static + + postgres: + container_name: bracket-postgres + image: postgres + environment: + POSTGRES_DB: bracket_db + POSTGRES_PASSWORD: rLAPG2RDeozyfgxr + POSTGRES_USER: admin + restart: always + volumes: + - bracket_pg_data:/var/lib/postgresql/data + + adminer: + container_name: adminer + image: adminer + environment: + ADMINER_DEFAULT_SERVER: postgres + depends_on: + - postgres + ports: + - 63880:8080 + restart: unless-stopped diff --git a/caddymanager/.env b/caddymanager/.env new file mode 100644 index 0000000..fe846cf --- /dev/null +++ b/caddymanager/.env @@ -0,0 +1,23 @@ +# Backend +API_BASE_URL=http://localhost:3000/api/v1 +APP_NAME=Caddy Manager +DARK_MODE=true +# Frontend +PORT=3000 +# Database Engine Configuration +DB_ENGINE=sqlite # Options: 'sqlite' or 'mongodb' +# SQLite Configuration (used when DB_ENGINE=sqlite) +SQLITE_DB_PATH=./caddymanager.sqlite +# MongoDB Configuration (used when DB_ENGINE=mongodb) +MONGO_USERNAME=mongoadmin +MONGO_PASSWORD=QaG33feoWfL2W7F9AuRYTS2N4Bm94hEA +MONGODB_URI=mongodb://mongoadmin:QaG33feoWfL2W7F9AuRYTS2N4Bm94hEA@localhost:27017/caddymanager?authSource=admin +CORS_ORIGIN=http://localhost:5173 +LOG_LEVEL=debug +CADDY_SANDBOX_URL=http://localhost:2019 +PING_INTERVAL=30000 +PING_TIMEOUT=2000 +AUDIT_LOG_MAX_SIZE_MB=100 +AUDIT_LOG_RETENTION_DAYS=90 +JWT_SECRET=YPKCVW8qEEshVN6BHPb6tq4YdhQpdQrR +JWT_EXPIRATION=24h diff --git a/caddymanager/docker-compose.yaml b/caddymanager/docker-compose.yaml new file mode 100644 index 0000000..2e05adf --- /dev/null +++ b/caddymanager/docker-compose.yaml @@ -0,0 +1,84 @@ +# bob +# https://github.com/caddymanager +--- +name: caddymanager + +networks: + caddymanager: + driver: bridge + +volumes: + mongodb_data: # Only used when MongoDB profile is active + sqlite_data: # SQLite database storage + +services: + # MongoDB database for persistent storage (optional - SQLite is used by default) + mongodb: + image: mongo:8.0 + container_name: caddymanager-mongodb + restart: unless-stopped + environment: + - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-mongoadmin} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-someSecretPassword} # Change for production! + ports: + - "27017:27017" # Expose for local dev, remove for production + volumes: + - mongodb_data:/data/db + networks: + - caddymanager + profiles: + - mongodb # Use 'docker-compose --profile mongodb up' to include MongoDB + + # Backend API server + backend: + image: caddymanager/caddymanager-backend:latest + container_name: caddymanager-backend + restart: unless-stopped + environment: + - PORT=3000 + # Database Engine Configuration (defaults to SQLite) + - DB_ENGINE=sqlite # Options: 'sqlite' or 'mongodb' + # SQLite Configuration (used when DB_ENGINE=sqlite) + - SQLITE_DB_PATH=/app/data/caddymanager.sqlite + # MongoDB Configuration (used when DB_ENGINE=mongodb) + - MONGODB_URI=mongodb://$${MONGO_USERNAME:-mongoadmin}:$${MONGO_PASSWORD:-someSecretPassword}@mongodb:27017/caddymanager?authSource=admin + - CORS_ORIGIN=http://localhost:80 + - LOG_LEVEL=debug + - CADDY_SANDBOX_URL=http://localhost:2019 + - PING_INTERVAL=30000 + - PING_TIMEOUT=2000 + - AUDIT_LOG_MAX_SIZE_MB=100 + - AUDIT_LOG_RETENTION_DAYS=90 + - METRICS_HISTORY_MAX=1000 # Optional: max number of in-memory metric history snapshots to keep + - JWT_SECRET=YPKCVW8qEEshVN6BHPb6tq4YdhQpdQrR + - JWT_EXPIRATION=24h + # Backend is now only accessible through frontend proxy + volumes: + - sqlite_data:/app/data # SQLite database storage + networks: + - caddymanager + + # Frontend web UI + frontend: + image: caddymanager/caddymanager-frontend:latest + container_name: caddymanager-frontend + restart: unless-stopped + depends_on: + - backend + environment: + - BACKEND_HOST=backend:3000 + - APP_NAME=Caddy Manager + - DARK_MODE=true + ports: + # - "80:80" # Expose web UI + - 20125:80 + networks: + - caddymanager + +# Notes: +# - SQLite is the default database engine - no additional setup required! +# - To use MongoDB instead, set DB_ENGINE=mongodb and start with: docker-compose --profile mongodb up +# - For production, use strong passwords and consider secrets management. +# - The backend uses SQLite by default, storing data in a persistent volume. +# - The frontend proxies all /api/* requests to the backend service. +# - Backend is not directly exposed - all API access goes through the frontend proxy. diff --git a/calibre/docker-compose.yaml b/calibre/docker-compose.yaml new file mode 100644 index 0000000..e9ae798 --- /dev/null +++ b/calibre/docker-compose.yaml @@ -0,0 +1,57 @@ +networks: + calibre: + +volumes: + config: + name: calibre_config + external: true + uploads: + name: calibre_uploads + external: true + plugins: + name: calibre_plugins + external: true + calibre-web: + name: calibre_calibre-web + external: true + +services: + + calibre: + image: ghcr.io/linuxserver/calibre + container_name: calibre + restart: unless-stopped + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + - CUSTOM_USER=admin + - PASSWORD=Z2Mi&S5krN@P9P + volumes: + - config:/config + - uploads:/uploads + - plugins:/plugins + - /nfs/ebooks/calibre_library:/books + ports: + - 8080:8080 + - 8081:8081 + labels: + - com.centurylinklabs.watchtower.enable=true + + calibre-web: + image: ghcr.io/linuxserver/calibre-web + container_name: calibre-web + restart: unless-stopped + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - calibre-web:/config + - /nfs/ebooks/calibre_library:/books + depends_on: + - calibre + ports: + - 8083:8083 + labels: + - com.centurylinklabs.watchtower.enable=true diff --git a/coturn/compose.env b/coturn/compose.env new file mode 100644 index 0000000..e2e9c08 --- /dev/null +++ b/coturn/compose.env @@ -0,0 +1,7 @@ +SUBDOMAIN=cloud +DOMAIN=delmar.bzh +EXTERNAL_IP=192.168.1.14 +STATIC_AUTH_SECRET=Jxge85wCdu6y42nZ +LISTENING_PORT=3478 +MIN_PORT=49160 +MAX_PORT=49200 diff --git a/coturn/compose.yaml b/coturn/compose.yaml new file mode 100644 index 0000000..e6295bf --- /dev/null +++ b/coturn/compose.yaml @@ -0,0 +1,24 @@ +## To be included in nextcloud.compose.yaml +services: + coturn: + image: instrumentisto/coturn:latest + container_name: coturn + restart: unless-stopped + ports: + - 3478:3478 + - 49160-49200:49160-49200/udp + command: + - '--realm=cloud.delmar.bzh' + - '--server-name=turn-server' + - '--fingerprint' + - '--listening-ip=0.0.0.0' + - '--external-ip=192.168.1.14' + - '--listening-port=3478' + - '--min-port=49160' + - '--max-port=49200' + - '--log-file=/var/log/turnserver.log' + - '--tls-listening-port=443' + - '--use-auth-secret' + - '--static-auth-secret=Jxge85wCdu6y42nZ' + networks: + - nextcloud diff --git a/coturn/compose.yaml.old b/coturn/compose.yaml.old new file mode 100644 index 0000000..2407961 --- /dev/null +++ b/coturn/compose.yaml.old @@ -0,0 +1,36 @@ +volumes: + + +services: + coturn: + image: instrumentisto/coturn:latest + container_name: coturn + restart: unless-stopped + ports: + - '${LISTENING_PORT}:${LISTENING_PORT}' + - '${MIN_PORT}-${MAX_PORT}:${MIN_PORT}-${MAX_PORT}/udp' + command: + - '--realm=${SUBDOMAIN}.${DOMAIN}' + - '--server-name=turn-server' + - '--fingerprint' + - '--listening-ip=0.0.0.0' + - '--external-ip=${EXTERNAL_IP}' + - '--listening-port=${LISTENING_PORT}' + - '--min-port=${MIN_PORT}' + - '--max-port=${MAX_PORT}' + - '--log-file=/var/log/turnserver.log' + - '--tls-listening-port=443' + - '--use-auth-secret' + - '--static-auth-secret=${STATIC_AUTH_SECRET}' + labels: + - 'traefik.enable=true' + - 'traefik.http.routers.turn-server.tls=true' + - 'traefik.http.routers.turn-server.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)' + - 'traefik.http.routers.turn-server.entrypoints=websecure' + - 'traefik.http.routers.turn-server.tls.certresolver=letsencrypt' + networks: + - 'web' + +networks: + web: + external: true diff --git a/crater/.dockerignore b/crater/.dockerignore new file mode 100644 index 0000000..1cf15ae --- /dev/null +++ b/crater/.dockerignore @@ -0,0 +1,10 @@ +.dockerignore +.gitignore +*.md +.git/ +.idea/ +.DS_Store/ +docker-compose.* +LICENSE +nginx.conf +yarn.lock diff --git a/crater/.editorconfig b/crater/.editorconfig new file mode 100644 index 0000000..10ad665 --- /dev/null +++ b/crater/.editorconfig @@ -0,0 +1,25 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 + +[*.vue] +indent_size = 2 + +[*.js] +indent_size = 2 + +[*.json] +indent_size = 2 diff --git a/crater/.env.example b/crater/.env.example new file mode 100644 index 0000000..c8d4e22 --- /dev/null +++ b/crater/.env.example @@ -0,0 +1,40 @@ +APP_ENV=production +APP_KEY=base64:kgk/4DW1vEVy7aEvet5FPp5un6PIGe/so8H0mvoUtW0= +APP_DEBUG=true +APP_LOG_LEVEL=debug +APP_URL=http://crater.test + +DB_CONNECTION=mysql +DB_HOST=db +DB_PORT=3306 +DB_DATABASE=crater +DB_USERNAME=crater +DB_PASSWORD="crater" + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +QUEUE_DRIVER=sync +SESSION_DRIVER=cookie +SESSION_LIFETIME=1440 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST= +MAIL_PORT= +MAIL_USERNAME= +MAIL_PASSWORD= +MAIL_ENCRYPTION= + +PUSHER_APP_ID= +PUSHER_KEY= +PUSHER_SECRET= + +SANCTUM_STATEFUL_DOMAINS=crater.test +SESSION_DOMAIN=crater.test + +TRUSTED_PROXIES="*" + +CRON_JOB_AUTH_TOKEN="" diff --git a/crater/.env.testing b/crater/.env.testing new file mode 100644 index 0000000..d5e8cab --- /dev/null +++ b/crater/.env.testing @@ -0,0 +1,13 @@ +APP_ENV=testing +APP_DEBUG=true +APP_KEY=base64:IdDlpLmYyWA9z4Ruj5st1FSYrhCR7lPOscLGCz2Jf4I= +DB_CONNECTION=sqlite + +MAIL_DRIVER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=587 +MAIL_USERNAME=ff538f0e1037f4 +MAIL_PASSWORD=c04c81145fcb73 +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="admin@craterapp.com" +MAIL_FROM_NAME="John Doe" diff --git a/crater/.eslintrc.js b/crater/.eslintrc.js new file mode 100644 index 0000000..6f9abf3 --- /dev/null +++ b/crater/.eslintrc.js @@ -0,0 +1,14 @@ +// .eslintrc.js + +module.exports = { + extends: [ + // add more generic rulesets here, such as: + // 'eslint:recommended', + "plugin:vue/vue3-recommended", + "prettier", + ], + rules: { + // override/add rules settings here, such as: + // 'vue/no-unused-vars': 'error' + }, +}; diff --git a/crater/.gitattributes b/crater/.gitattributes new file mode 100644 index 0000000..a8763f8 --- /dev/null +++ b/crater/.gitattributes @@ -0,0 +1,3 @@ +* text=auto +*.css linguist-vendored +*.scss linguist-vendored diff --git a/crater/.gitignore b/crater/.gitignore new file mode 100644 index 0000000..d1bdd40 --- /dev/null +++ b/crater/.gitignore @@ -0,0 +1,20 @@ +/Modules +/node_modules +/public/storage +/public/hot +/storage/*.key +/vendor +/.idea +Homestead.json +Homestead.yaml +.env +.phpunit.result.cache +.rnd +/.expo +/.vscode +/docker-compose/db/data/ +.gitkeep +/public/docs +/.scribe +!storage/fonts/.gitkeep +.DS_Store diff --git a/crater/.php-cs-fixer.dist.php b/crater/.php-cs-fixer.dist.php new file mode 100644 index 0000000..e9dffd6 --- /dev/null +++ b/crater/.php-cs-fixer.dist.php @@ -0,0 +1,42 @@ +in(__DIR__) + ->exclude(['bootstrap', 'storage', 'vendor']) + ->name('*.php') + ->name('_ide_helper') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +$rules = [ + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'concat_space' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'class_attributes_separation' => [ + 'elements' => [ + 'method' => 'one', + 'property' => 'one', + ], + ], + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], +]; + +return (new PhpCsFixer\Config()) + ->setUsingCache(true) + ->setRules($rules) + ->setFinder($finder); diff --git a/crater/.prettierrc.json b/crater/.prettierrc.json new file mode 100644 index 0000000..b18aca9 --- /dev/null +++ b/crater/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2 +} diff --git a/crater/CODE_OF_CONDUCT.md b/crater/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1df0525 --- /dev/null +++ b/crater/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +info@craterapp.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/crater/Dockerfile b/crater/Dockerfile new file mode 100644 index 0000000..581d041 --- /dev/null +++ b/crater/Dockerfile @@ -0,0 +1,40 @@ +FROM php:8.1-fpm + +# Arguments defined in docker-compose.yml +ARG user +ARG uid + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + git \ + curl \ + libpng-dev \ + libonig-dev \ + libxml2-dev \ + zip \ + unzip \ + libzip-dev \ + libmagickwand-dev \ + mariadb-client + +# Clear cache +RUN apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN pecl install imagick \ + && docker-php-ext-enable imagick + +# Install PHP extensions +RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath gd + +# Get latest Composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Create system user to run Composer and Artisan Commands +RUN useradd -G www-data,root -u $uid -d /home/$user $user +RUN mkdir -p /home/$user/.composer && \ + chown -R $user:$user /home/$user + +# Set working directory +WORKDIR /var/www + +USER $user diff --git a/crater/LICENSE b/crater/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/crater/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/crater/SECURITY.md b/crater/SECURITY.md new file mode 100644 index 0000000..0efa739 --- /dev/null +++ b/crater/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Please email security@craterapp.com to report any security vulnerabilities. We will acknowledge receipt of your vulnerability and strive to send you regular updates about our progress. If you're curious about the status of your disclosure please feel free to email us again. diff --git a/crater/_ide_helper.php b/crater/_ide_helper.php new file mode 100644 index 0000000..7dc9a45 --- /dev/null +++ b/crater/_ide_helper.php @@ -0,0 +1,13267 @@ + + * @see https://github.com/barryvdh/laravel-ide-helper + */ + +namespace { + exit("This file should not be included, only analyzed by your IDE"); + + class App extends \Illuminate\Support\Facades\App + { + /** + * Get the version number of the application. + * + * @return string + * @static + */ + public static function version() + { + return \Illuminate\Foundation\Application::version(); + } + + /** + * Run the given array of bootstrap classes. + * + * @param array $bootstrappers + * @return void + * @static + */ + public static function bootstrapWith($bootstrappers) + { + \Illuminate\Foundation\Application::bootstrapWith($bootstrappers); + } + + /** + * Register a callback to run after loading the environment. + * + * @param \Closure $callback + * @return void + * @static + */ + public static function afterLoadingEnvironment($callback) + { + \Illuminate\Foundation\Application::afterLoadingEnvironment($callback); + } + + /** + * Register a callback to run before a bootstrapper. + * + * @param string $bootstrapper + * @param \Closure $callback + * @return void + * @static + */ + public static function beforeBootstrapping($bootstrapper, $callback) + { + \Illuminate\Foundation\Application::beforeBootstrapping($bootstrapper, $callback); + } + + /** + * Register a callback to run after a bootstrapper. + * + * @param string $bootstrapper + * @param \Closure $callback + * @return void + * @static + */ + public static function afterBootstrapping($bootstrapper, $callback) + { + \Illuminate\Foundation\Application::afterBootstrapping($bootstrapper, $callback); + } + + /** + * Determine if the application has been bootstrapped before. + * + * @return bool + * @static + */ + public static function hasBeenBootstrapped() + { + return \Illuminate\Foundation\Application::hasBeenBootstrapped(); + } + + /** + * Set the base path for the application. + * + * @param string $basePath + * @return $this + * @static + */ + public static function setBasePath($basePath) + { + return \Illuminate\Foundation\Application::setBasePath($basePath); + } + + /** + * Get the path to the application "app" directory. + * + * @return string + * @static + */ + public static function path() + { + return \Illuminate\Foundation\Application::path(); + } + + /** + * Get the base path of the Laravel installation. + * + * @return string + * @static + */ + public static function basePath() + { + return \Illuminate\Foundation\Application::basePath(); + } + + /** + * Get the path to the bootstrap directory. + * + * @return string + * @static + */ + public static function bootstrapPath() + { + return \Illuminate\Foundation\Application::bootstrapPath(); + } + + /** + * Get the path to the application configuration files. + * + * @return string + * @static + */ + public static function configPath() + { + return \Illuminate\Foundation\Application::configPath(); + } + + /** + * Get the path to the database directory. + * + * @return string + * @static + */ + public static function databasePath() + { + return \Illuminate\Foundation\Application::databasePath(); + } + + /** + * Set the database directory. + * + * @param string $path + * @return $this + * @static + */ + public static function useDatabasePath($path) + { + return \Illuminate\Foundation\Application::useDatabasePath($path); + } + + /** + * Get the path to the language files. + * + * @return string + * @static + */ + public static function langPath() + { + return \Illuminate\Foundation\Application::langPath(); + } + + /** + * Get the path to the public / web directory. + * + * @return string + * @static + */ + public static function publicPath() + { + return \Illuminate\Foundation\Application::publicPath(); + } + + /** + * Get the path to the storage directory. + * + * @return string + * @static + */ + public static function storagePath() + { + return \Illuminate\Foundation\Application::storagePath(); + } + + /** + * Set the storage directory. + * + * @param string $path + * @return $this + * @static + */ + public static function useStoragePath($path) + { + return \Illuminate\Foundation\Application::useStoragePath($path); + } + + /** + * Get the path to the resources directory. + * + * @return string + * @static + */ + public static function resourcePath() + { + return \Illuminate\Foundation\Application::resourcePath(); + } + + /** + * Get the path to the environment file directory. + * + * @return string + * @static + */ + public static function environmentPath() + { + return \Illuminate\Foundation\Application::environmentPath(); + } + + /** + * Set the directory for the environment file. + * + * @param string $path + * @return $this + * @static + */ + public static function useEnvironmentPath($path) + { + return \Illuminate\Foundation\Application::useEnvironmentPath($path); + } + + /** + * Set the environment file to be loaded during bootstrapping. + * + * @param string $file + * @return $this + * @static + */ + public static function loadEnvironmentFrom($file) + { + return \Illuminate\Foundation\Application::loadEnvironmentFrom($file); + } + + /** + * Get the environment file the application is using. + * + * @return string + * @static + */ + public static function environmentFile() + { + return \Illuminate\Foundation\Application::environmentFile(); + } + + /** + * Get the fully qualified path to the environment file. + * + * @return string + * @static + */ + public static function environmentFilePath() + { + return \Illuminate\Foundation\Application::environmentFilePath(); + } + + /** + * Get or check the current application environment. + * + * @return string|bool + * @static + */ + public static function environment() + { + return \Illuminate\Foundation\Application::environment(); + } + + /** + * Determine if application is in local environment. + * + * @return bool + * @static + */ + public static function isLocal() + { + return \Illuminate\Foundation\Application::isLocal(); + } + + /** + * Detect the application's current environment. + * + * @param \Closure $callback + * @return string + * @static + */ + public static function detectEnvironment($callback) + { + return \Illuminate\Foundation\Application::detectEnvironment($callback); + } + + /** + * Determine if we are running in the console. + * + * @return bool + * @static + */ + public static function runningInConsole() + { + return \Illuminate\Foundation\Application::runningInConsole(); + } + + /** + * Determine if we are running unit tests. + * + * @return bool + * @static + */ + public static function runningUnitTests() + { + return \Illuminate\Foundation\Application::runningUnitTests(); + } + + /** + * Register all of the configured providers. + * + * @return void + * @static + */ + public static function registerConfiguredProviders() + { + \Illuminate\Foundation\Application::registerConfiguredProviders(); + } + + /** + * Register a service provider with the application. + * + * @param \Illuminate\Support\ServiceProvider|string $provider + * @param array $options + * @param bool $force + * @return \Illuminate\Support\ServiceProvider + * @static + */ + public static function register($provider, $options = [], $force = false) + { + return \Illuminate\Foundation\Application::register($provider, $options, $force); + } + + /** + * Get the registered service provider instance if it exists. + * + * @param \Illuminate\Support\ServiceProvider|string $provider + * @return \Illuminate\Support\ServiceProvider|null + * @static + */ + public static function getProvider($provider) + { + return \Illuminate\Foundation\Application::getProvider($provider); + } + + /** + * Resolve a service provider instance from the class name. + * + * @param string $provider + * @return \Illuminate\Support\ServiceProvider + * @static + */ + public static function resolveProvider($provider) + { + return \Illuminate\Foundation\Application::resolveProvider($provider); + } + + /** + * Load and boot all of the remaining deferred providers. + * + * @return void + * @static + */ + public static function loadDeferredProviders() + { + \Illuminate\Foundation\Application::loadDeferredProviders(); + } + + /** + * Load the provider for a deferred service. + * + * @param string $service + * @return void + * @static + */ + public static function loadDeferredProvider($service) + { + \Illuminate\Foundation\Application::loadDeferredProvider($service); + } + + /** + * Register a deferred provider and service. + * + * @param string $provider + * @param string $service + * @return void + * @static + */ + public static function registerDeferredProvider($provider, $service = null) + { + \Illuminate\Foundation\Application::registerDeferredProvider($provider, $service); + } + + /** + * Resolve the given type from the container. + * + * (Overriding Container::make) + * + * @param string $abstract + * @return mixed + * @static + */ + public static function make($abstract) + { + return \Illuminate\Foundation\Application::make($abstract); + } + + /** + * Determine if the given abstract type has been bound. + * + * (Overriding Container::bound) + * + * @param string $abstract + * @return bool + * @static + */ + public static function bound($abstract) + { + return \Illuminate\Foundation\Application::bound($abstract); + } + + /** + * Determine if the application has booted. + * + * @return bool + * @static + */ + public static function isBooted() + { + return \Illuminate\Foundation\Application::isBooted(); + } + + /** + * Boot the application's service providers. + * + * @return void + * @static + */ + public static function boot() + { + \Illuminate\Foundation\Application::boot(); + } + + /** + * Register a new boot listener. + * + * @param mixed $callback + * @return void + * @static + */ + public static function booting($callback) + { + \Illuminate\Foundation\Application::booting($callback); + } + + /** + * Register a new "booted" listener. + * + * @param mixed $callback + * @return void + * @static + */ + public static function booted($callback) + { + \Illuminate\Foundation\Application::booted($callback); + } + + /** + * {@inheritdoc} + * + * @static + */ + public static function handle($request, $type = 1, $catch = true) + { + return \Illuminate\Foundation\Application::handle($request, $type, $catch); + } + + /** + * Determine if middleware has been disabled for the application. + * + * @return bool + * @static + */ + public static function shouldSkipMiddleware() + { + return \Illuminate\Foundation\Application::shouldSkipMiddleware(); + } + + /** + * Get the path to the cached services.php file. + * + * @return string + * @static + */ + public static function getCachedServicesPath() + { + return \Illuminate\Foundation\Application::getCachedServicesPath(); + } + + /** + * Determine if the application configuration is cached. + * + * @return bool + * @static + */ + public static function configurationIsCached() + { + return \Illuminate\Foundation\Application::configurationIsCached(); + } + + /** + * Get the path to the configuration cache file. + * + * @return string + * @static + */ + public static function getCachedConfigPath() + { + return \Illuminate\Foundation\Application::getCachedConfigPath(); + } + + /** + * Determine if the application routes are cached. + * + * @return bool + * @static + */ + public static function routesAreCached() + { + return \Illuminate\Foundation\Application::routesAreCached(); + } + + /** + * Get the path to the routes cache file. + * + * @return string + * @static + */ + public static function getCachedRoutesPath() + { + return \Illuminate\Foundation\Application::getCachedRoutesPath(); + } + + /** + * Determine if the application is currently down for maintenance. + * + * @return bool + * @static + */ + public static function isDownForMaintenance() + { + return \Illuminate\Foundation\Application::isDownForMaintenance(); + } + + /** + * Throw an HttpException with the given data. + * + * @param int $code + * @param string $message + * @param array $headers + * @return void + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + * @static + */ + public static function abort($code, $message = '', $headers = []) + { + \Illuminate\Foundation\Application::abort($code, $message, $headers); + } + + /** + * Register a terminating callback with the application. + * + * @param \Closure $callback + * @return $this + * @static + */ + public static function terminating($callback) + { + return \Illuminate\Foundation\Application::terminating($callback); + } + + /** + * Terminate the application. + * + * @return void + * @static + */ + public static function terminate() + { + \Illuminate\Foundation\Application::terminate(); + } + + /** + * Get the service providers that have been loaded. + * + * @return array + * @static + */ + public static function getLoadedProviders() + { + return \Illuminate\Foundation\Application::getLoadedProviders(); + } + + /** + * Get the application's deferred services. + * + * @return array + * @static + */ + public static function getDeferredServices() + { + return \Illuminate\Foundation\Application::getDeferredServices(); + } + + /** + * Set the application's deferred services. + * + * @param array $services + * @return void + * @static + */ + public static function setDeferredServices($services) + { + \Illuminate\Foundation\Application::setDeferredServices($services); + } + + /** + * Add an array of services to the application's deferred services. + * + * @param array $services + * @return void + * @static + */ + public static function addDeferredServices($services) + { + \Illuminate\Foundation\Application::addDeferredServices($services); + } + + /** + * Determine if the given service is a deferred service. + * + * @param string $service + * @return bool + * @static + */ + public static function isDeferredService($service) + { + return \Illuminate\Foundation\Application::isDeferredService($service); + } + + /** + * Configure the real-time facade namespace. + * + * @param string $namespace + * @return void + * @static + */ + public static function provideFacades($namespace) + { + \Illuminate\Foundation\Application::provideFacades($namespace); + } + + /** + * Define a callback to be used to configure Monolog. + * + * @param callable $callback + * @return $this + * @static + */ + public static function configureMonologUsing($callback) + { + return \Illuminate\Foundation\Application::configureMonologUsing($callback); + } + + /** + * Determine if the application has a custom Monolog configurator. + * + * @return bool + * @static + */ + public static function hasMonologConfigurator() + { + return \Illuminate\Foundation\Application::hasMonologConfigurator(); + } + + /** + * Get the custom Monolog configurator for the application. + * + * @return callable + * @static + */ + public static function getMonologConfigurator() + { + return \Illuminate\Foundation\Application::getMonologConfigurator(); + } + + /** + * Get the current application locale. + * + * @return string + * @static + */ + public static function getLocale() + { + return \Illuminate\Foundation\Application::getLocale(); + } + + /** + * Set the current application locale. + * + * @param string $locale + * @return void + * @static + */ + public static function setLocale($locale) + { + \Illuminate\Foundation\Application::setLocale($locale); + } + + /** + * Determine if application locale is the given locale. + * + * @param string $locale + * @return bool + * @static + */ + public static function isLocale($locale) + { + return \Illuminate\Foundation\Application::isLocale($locale); + } + + /** + * Register the core class aliases in the container. + * + * @return void + * @static + */ + public static function registerCoreContainerAliases() + { + \Illuminate\Foundation\Application::registerCoreContainerAliases(); + } + + /** + * Flush the container of all bindings and resolved instances. + * + * @return void + * @static + */ + public static function flush() + { + \Illuminate\Foundation\Application::flush(); + } + + /** + * Get the application namespace. + * + * @return string + * @throws \RuntimeException + * @static + */ + public static function getNamespace() + { + return \Illuminate\Foundation\Application::getNamespace(); + } + + /** + * Define a contextual binding. + * + * @param string $concrete + * @return \Illuminate\Contracts\Container\ContextualBindingBuilder + * @static + */ + public static function when($concrete) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::when($concrete); + } + + /** + * Determine if the given abstract type has been resolved. + * + * @param string $abstract + * @return bool + * @static + */ + public static function resolved($abstract) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::resolved($abstract); + } + + /** + * Determine if a given type is shared. + * + * @param string $abstract + * @return bool + * @static + */ + public static function isShared($abstract) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::isShared($abstract); + } + + /** + * Determine if a given string is an alias. + * + * @param string $name + * @return bool + * @static + */ + public static function isAlias($name) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::isAlias($name); + } + + /** + * Register a binding with the container. + * + * @param string|array $abstract + * @param \Closure|string|null $concrete + * @param bool $shared + * @return void + * @static + */ + public static function bind($abstract, $concrete = null, $shared = false) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::bind($abstract, $concrete, $shared); + } + + /** + * Determine if the container has a method binding. + * + * @param string $method + * @return bool + * @static + */ + public static function hasMethodBinding($method) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::hasMethodBinding($method); + } + + /** + * Bind a callback to resolve with Container::call. + * + * @param string $method + * @param \Closure $callback + * @return void + * @static + */ + public static function bindMethod($method, $callback) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::bindMethod($method, $callback); + } + + /** + * Get the method binding for the given method. + * + * @param string $method + * @param mixed $instance + * @return mixed + * @static + */ + public static function callMethodBinding($method, $instance) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::callMethodBinding($method, $instance); + } + + /** + * Add a contextual binding to the container. + * + * @param string $concrete + * @param string $abstract + * @param \Closure|string $implementation + * @return void + * @static + */ + public static function addContextualBinding($concrete, $abstract, $implementation) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::addContextualBinding($concrete, $abstract, $implementation); + } + + /** + * Register a binding if it hasn't already been registered. + * + * @param string $abstract + * @param \Closure|string|null $concrete + * @param bool $shared + * @return void + * @static + */ + public static function bindIf($abstract, $concrete = null, $shared = false) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::bindIf($abstract, $concrete, $shared); + } + + /** + * Register a shared binding in the container. + * + * @param string|array $abstract + * @param \Closure|string|null $concrete + * @return void + * @static + */ + public static function singleton($abstract, $concrete = null) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::singleton($abstract, $concrete); + } + + /** + * "Extend" an abstract type in the container. + * + * @param string $abstract + * @param \Closure $closure + * @return void + * @throws \InvalidArgumentException + * @static + */ + public static function extend($abstract, $closure) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::extend($abstract, $closure); + } + + /** + * Register an existing instance as shared in the container. + * + * @param string $abstract + * @param mixed $instance + * @return void + * @static + */ + public static function instance($abstract, $instance) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::instance($abstract, $instance); + } + + /** + * Assign a set of tags to a given binding. + * + * @param array|string $abstracts + * @param array|mixed $tags + * @return void + * @static + */ + public static function tag($abstracts, $tags) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::tag($abstracts, $tags); + } + + /** + * Resolve all of the bindings for a given tag. + * + * @param string $tag + * @return array + * @static + */ + public static function tagged($tag) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::tagged($tag); + } + + /** + * Alias a type to a different name. + * + * @param string $abstract + * @param string $alias + * @return void + * @static + */ + public static function alias($abstract, $alias) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::alias($abstract, $alias); + } + + /** + * Bind a new callback to an abstract's rebind event. + * + * @param string $abstract + * @param \Closure $callback + * @return mixed + * @static + */ + public static function rebinding($abstract, $callback) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::rebinding($abstract, $callback); + } + + /** + * Refresh an instance on the given target and method. + * + * @param string $abstract + * @param mixed $target + * @param string $method + * @return mixed + * @static + */ + public static function refresh($abstract, $target, $method) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::refresh($abstract, $target, $method); + } + + /** + * Wrap the given closure such that its dependencies will be injected when executed. + * + * @param \Closure $callback + * @param array $parameters + * @return \Closure + * @static + */ + public static function wrap($callback, $parameters = []) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::wrap($callback, $parameters); + } + + /** + * Call the given Closure / class@method and inject its dependencies. + * + * @param callable|string $callback + * @param array $parameters + * @param string|null $defaultMethod + * @return mixed + * @static + */ + public static function call($callback, $parameters = [], $defaultMethod = null) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::call($callback, $parameters, $defaultMethod); + } + + /** + * Get a closure to resolve the given type from the container. + * + * @param string $abstract + * @return \Closure + * @static + */ + public static function factory($abstract) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::factory($abstract); + } + + /** + * Instantiate a concrete instance of the given type. + * + * @param string $concrete + * @return mixed + * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @static + */ + public static function build($concrete) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::build($concrete); + } + + /** + * Register a new resolving callback. + * + * @param string $abstract + * @param \Closure|null $callback + * @return void + * @static + */ + public static function resolving($abstract, $callback = null) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::resolving($abstract, $callback); + } + + /** + * Register a new after resolving callback for all types. + * + * @param string $abstract + * @param \Closure|null $callback + * @return void + * @static + */ + public static function afterResolving($abstract, $callback = null) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::afterResolving($abstract, $callback); + } + + /** + * Get the container's bindings. + * + * @return array + * @static + */ + public static function getBindings() + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::getBindings(); + } + + /** + * Get the alias for an abstract if available. + * + * @param string $abstract + * @return string + * @throws \LogicException + * @static + */ + public static function getAlias($abstract) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::getAlias($abstract); + } + + /** + * Remove a resolved instance from the instance cache. + * + * @param string $abstract + * @return void + * @static + */ + public static function forgetInstance($abstract) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::forgetInstance($abstract); + } + + /** + * Clear all of the instances from the container. + * + * @return void + * @static + */ + public static function forgetInstances() + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::forgetInstances(); + } + + /** + * Set the globally available instance of the container. + * + * @return static + * @static + */ + public static function getInstance() + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::getInstance(); + } + + /** + * Set the shared instance of the container. + * + * @param \Illuminate\Contracts\Container\Container|null $container + * @return static + * @static + */ + public static function setInstance($container = null) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::setInstance($container); + } + + /** + * Determine if a given offset exists. + * + * @param string $key + * @return bool + * @static + */ + public static function offsetExists($key) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::offsetExists($key); + } + + /** + * Get the value at a given offset. + * + * @param string $key + * @return mixed + * @static + */ + public static function offsetGet($key) + { + //Method inherited from \Illuminate\Container\Container + return \Illuminate\Foundation\Application::offsetGet($key); + } + + /** + * Set the value at a given offset. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function offsetSet($key, $value) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::offsetSet($key, $value); + } + + /** + * Unset the value at a given offset. + * + * @param string $key + * @return void + * @static + */ + public static function offsetUnset($key) + { + //Method inherited from \Illuminate\Container\Container + \Illuminate\Foundation\Application::offsetUnset($key); + } + } + + + class Artisan extends \Illuminate\Support\Facades\Artisan + { + /** + * Run the console application. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return int + * @static + */ + public static function handle($input, $output = null) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + return \Crater\Console\Kernel::handle($input, $output); + } + + /** + * Terminate the application. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param int $status + * @return void + * @static + */ + public static function terminate($input, $status) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + \Crater\Console\Kernel::terminate($input, $status); + } + + /** + * Register a Closure based command with the application. + * + * @param string $signature + * @param \Closure $callback + * @return \Illuminate\Foundation\Console\ClosureCommand + * @static + */ + public static function command($signature, $callback) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + return \Crater\Console\Kernel::command($signature, $callback); + } + + /** + * Register the given command with the console application. + * + * @param \Symfony\Component\Console\Command\Command $command + * @return void + * @static + */ + public static function registerCommand($command) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + \Crater\Console\Kernel::registerCommand($command); + } + + /** + * Run an Artisan console command by name. + * + * @param string $command + * @param array $parameters + * @param \Symfony\Component\Console\Output\OutputInterface $outputBuffer + * @return int + * @static + */ + public static function call($command, $parameters = [], $outputBuffer = null) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + return \Crater\Console\Kernel::call($command, $parameters, $outputBuffer); + } + + /** + * Queue the given console command. + * + * @param string $command + * @param array $parameters + * @return void + * @static + */ + public static function queue($command, $parameters = []) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + \Crater\Console\Kernel::queue($command, $parameters); + } + + /** + * Get all of the commands registered with the console. + * + * @return array + * @static + */ + public static function all() + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + return \Crater\Console\Kernel::all(); + } + + /** + * Get the output for the last run command. + * + * @return string + * @static + */ + public static function output() + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + return \Crater\Console\Kernel::output(); + } + + /** + * Bootstrap the application for artisan commands. + * + * @return void + * @static + */ + public static function bootstrap() + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + \Crater\Console\Kernel::bootstrap(); + } + + /** + * Set the Artisan application instance. + * + * @param \Illuminate\Console\Application $artisan + * @return void + * @static + */ + public static function setArtisan($artisan) + { + //Method inherited from \Illuminate\Foundation\Console\Kernel + \Crater\Console\Kernel::setArtisan($artisan); + } + } + + + class Auth extends \Illuminate\Support\Facades\Auth + { + /** + * Attempt to get the guard from the local cache. + * + * @param string $name + * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard + * @static + */ + public static function guard($name = null) + { + return \Illuminate\Auth\AuthManager::guard($name); + } + + /** + * Create a session based authentication guard. + * + * @param string $name + * @param array $config + * @return \Illuminate\Auth\SessionGuard + * @static + */ + public static function createSessionDriver($name, $config) + { + return \Illuminate\Auth\AuthManager::createSessionDriver($name, $config); + } + + /** + * Create a token based authentication guard. + * + * @param string $name + * @param array $config + * @return \Illuminate\Auth\TokenGuard + * @static + */ + public static function createTokenDriver($name, $config) + { + return \Illuminate\Auth\AuthManager::createTokenDriver($name, $config); + } + + /** + * Get the default authentication driver name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Auth\AuthManager::getDefaultDriver(); + } + + /** + * Set the default guard driver the factory should serve. + * + * @param string $name + * @return void + * @static + */ + public static function shouldUse($name) + { + \Illuminate\Auth\AuthManager::shouldUse($name); + } + + /** + * Set the default authentication driver name. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultDriver($name) + { + \Illuminate\Auth\AuthManager::setDefaultDriver($name); + } + + /** + * Register a new callback based request guard. + * + * @param string $driver + * @param callable $callback + * @return $this + * @static + */ + public static function viaRequest($driver, $callback) + { + return \Illuminate\Auth\AuthManager::viaRequest($driver, $callback); + } + + /** + * Get the user resolver callback. + * + * @return \Closure + * @static + */ + public static function userResolver() + { + return \Illuminate\Auth\AuthManager::userResolver(); + } + + /** + * Set the callback to be used to resolve users. + * + * @param \Closure $userResolver + * @return $this + * @static + */ + public static function resolveUsersUsing($userResolver) + { + return \Illuminate\Auth\AuthManager::resolveUsersUsing($userResolver); + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + * @static + */ + public static function extend($driver, $callback) + { + return \Illuminate\Auth\AuthManager::extend($driver, $callback); + } + + /** + * Register a custom provider creator Closure. + * + * @param string $name + * @param \Closure $callback + * @return $this + * @static + */ + public static function provider($name, $callback) + { + return \Illuminate\Auth\AuthManager::provider($name, $callback); + } + + /** + * Create the user provider implementation for the driver. + * + * @param string $provider + * @return \Illuminate\Contracts\Auth\UserProvider + * @throws \InvalidArgumentException + * @static + */ + public static function createUserProvider($provider) + { + return \Illuminate\Auth\AuthManager::createUserProvider($provider); + } + + /** + * Get the currently authenticated user. + * + * @return \Crater\User|null + * @static + */ + public static function user() + { + return \Illuminate\Auth\SessionGuard::user(); + } + + /** + * Get the ID for the currently authenticated user. + * + * @return int|null + * @static + */ + public static function id() + { + return \Illuminate\Auth\SessionGuard::id(); + } + + /** + * Log a user into the application without sessions or cookies. + * + * @param array $credentials + * @return bool + * @static + */ + public static function once($credentials = []) + { + return \Illuminate\Auth\SessionGuard::once($credentials); + } + + /** + * Log the given user ID into the application without sessions or cookies. + * + * @param mixed $id + * @return \Crater\User|false + * @static + */ + public static function onceUsingId($id) + { + return \Illuminate\Auth\SessionGuard::onceUsingId($id); + } + + /** + * Validate a user's credentials. + * + * @param array $credentials + * @return bool + * @static + */ + public static function validate($credentials = []) + { + return \Illuminate\Auth\SessionGuard::validate($credentials); + } + + /** + * Attempt to authenticate using HTTP Basic Auth. + * + * @param string $field + * @param array $extraConditions + * @return \Symfony\Component\HttpFoundation\Response|null + * @static + */ + public static function basic($field = 'email', $extraConditions = []) + { + return \Illuminate\Auth\SessionGuard::basic($field, $extraConditions); + } + + /** + * Perform a stateless HTTP Basic login attempt. + * + * @param string $field + * @param array $extraConditions + * @return \Symfony\Component\HttpFoundation\Response|null + * @static + */ + public static function onceBasic($field = 'email', $extraConditions = []) + { + return \Illuminate\Auth\SessionGuard::onceBasic($field, $extraConditions); + } + + /** + * Attempt to authenticate a user using the given credentials. + * + * @param array $credentials + * @param bool $remember + * @return bool + * @static + */ + public static function attempt($credentials = [], $remember = false) + { + return \Illuminate\Auth\SessionGuard::attempt($credentials, $remember); + } + + /** + * Log the given user ID into the application. + * + * @param mixed $id + * @param bool $remember + * @return \Crater\User|false + * @static + */ + public static function loginUsingId($id, $remember = false) + { + return \Illuminate\Auth\SessionGuard::loginUsingId($id, $remember); + } + + /** + * Log a user into the application. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param bool $remember + * @return void + * @static + */ + public static function login($user, $remember = false) + { + \Illuminate\Auth\SessionGuard::login($user, $remember); + } + + /** + * Log the user out of the application. + * + * @return void + * @static + */ + public static function logout() + { + \Illuminate\Auth\SessionGuard::logout(); + } + + /** + * Register an authentication attempt event listener. + * + * @param mixed $callback + * @return void + * @static + */ + public static function attempting($callback) + { + \Illuminate\Auth\SessionGuard::attempting($callback); + } + + /** + * Get the last user we attempted to authenticate. + * + * @return \Crater\User + * @static + */ + public static function getLastAttempted() + { + return \Illuminate\Auth\SessionGuard::getLastAttempted(); + } + + /** + * Get a unique identifier for the auth session value. + * + * @return string + * @static + */ + public static function getName() + { + return \Illuminate\Auth\SessionGuard::getName(); + } + + /** + * Get the name of the cookie used to store the "recaller". + * + * @return string + * @static + */ + public static function getRecallerName() + { + return \Illuminate\Auth\SessionGuard::getRecallerName(); + } + + /** + * Determine if the user was authenticated via "remember me" cookie. + * + * @return bool + * @static + */ + public static function viaRemember() + { + return \Illuminate\Auth\SessionGuard::viaRemember(); + } + + /** + * Get the cookie creator instance used by the guard. + * + * @return \Illuminate\Contracts\Cookie\QueueingFactory + * @throws \RuntimeException + * @static + */ + public static function getCookieJar() + { + return \Illuminate\Auth\SessionGuard::getCookieJar(); + } + + /** + * Set the cookie creator instance used by the guard. + * + * @param \Illuminate\Contracts\Cookie\QueueingFactory $cookie + * @return void + * @static + */ + public static function setCookieJar($cookie) + { + \Illuminate\Auth\SessionGuard::setCookieJar($cookie); + } + + /** + * Get the event dispatcher instance. + * + * @return \Illuminate\Contracts\Events\Dispatcher + * @static + */ + public static function getDispatcher() + { + return \Illuminate\Auth\SessionGuard::getDispatcher(); + } + + /** + * Set the event dispatcher instance. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + * @static + */ + public static function setDispatcher($events) + { + \Illuminate\Auth\SessionGuard::setDispatcher($events); + } + + /** + * Get the session store used by the guard. + * + * @return \Illuminate\Session\Store + * @static + */ + public static function getSession() + { + return \Illuminate\Auth\SessionGuard::getSession(); + } + + /** + * Get the user provider used by the guard. + * + * @return \Illuminate\Contracts\Auth\UserProvider + * @static + */ + public static function getProvider() + { + return \Illuminate\Auth\SessionGuard::getProvider(); + } + + /** + * Set the user provider used by the guard. + * + * @param \Illuminate\Contracts\Auth\UserProvider $provider + * @return void + * @static + */ + public static function setProvider($provider) + { + \Illuminate\Auth\SessionGuard::setProvider($provider); + } + + /** + * Return the currently cached user. + * + * @return \Crater\User|null + * @static + */ + public static function getUser() + { + return \Illuminate\Auth\SessionGuard::getUser(); + } + + /** + * Set the current user. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @return $this + * @static + */ + public static function setUser($user) + { + return \Illuminate\Auth\SessionGuard::setUser($user); + } + + /** + * Get the current request instance. + * + * @return \Symfony\Component\HttpFoundation\Request + * @static + */ + public static function getRequest() + { + return \Illuminate\Auth\SessionGuard::getRequest(); + } + + /** + * Set the current request instance. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @return $this + * @static + */ + public static function setRequest($request) + { + return \Illuminate\Auth\SessionGuard::setRequest($request); + } + + /** + * Determine if the current user is authenticated. + * + * @return \Crater\User + * @throws \Illuminate\Auth\AuthenticationException + * @static + */ + public static function authenticate() + { + return \Illuminate\Auth\SessionGuard::authenticate(); + } + + /** + * Determine if the current user is authenticated. + * + * @return bool + * @static + */ + public static function check() + { + return \Illuminate\Auth\SessionGuard::check(); + } + + /** + * Determine if the current user is a guest. + * + * @return bool + * @static + */ + public static function guest() + { + return \Illuminate\Auth\SessionGuard::guest(); + } + } + + + class Blade extends \Illuminate\Support\Facades\Blade + { + /** + * Compile the view at the given path. + * + * @param string $path + * @return void + * @static + */ + public static function compile($path = null) + { + \Illuminate\View\Compilers\BladeCompiler::compile($path); + } + + /** + * Get the path currently being compiled. + * + * @return string + * @static + */ + public static function getPath() + { + return \Illuminate\View\Compilers\BladeCompiler::getPath(); + } + + /** + * Set the path currently being compiled. + * + * @param string $path + * @return void + * @static + */ + public static function setPath($path) + { + \Illuminate\View\Compilers\BladeCompiler::setPath($path); + } + + /** + * Compile the given Blade template contents. + * + * @param string $value + * @return string + * @static + */ + public static function compileString($value) + { + return \Illuminate\View\Compilers\BladeCompiler::compileString($value); + } + + /** + * Strip the parentheses from the given expression. + * + * @param string $expression + * @return string + * @static + */ + public static function stripParentheses($expression) + { + return \Illuminate\View\Compilers\BladeCompiler::stripParentheses($expression); + } + + /** + * Register a custom Blade compiler. + * + * @param callable $compiler + * @return void + * @static + */ + public static function extend($compiler) + { + \Illuminate\View\Compilers\BladeCompiler::extend($compiler); + } + + /** + * Get the extensions used by the compiler. + * + * @return array + * @static + */ + public static function getExtensions() + { + return \Illuminate\View\Compilers\BladeCompiler::getExtensions(); + } + + /** + * Register a handler for custom directives. + * + * @param string $name + * @param callable $handler + * @return void + * @static + */ + public static function directive($name, $handler) + { + \Illuminate\View\Compilers\BladeCompiler::directive($name, $handler); + } + + /** + * Get the list of custom directives. + * + * @return array + * @static + */ + public static function getCustomDirectives() + { + return \Illuminate\View\Compilers\BladeCompiler::getCustomDirectives(); + } + + /** + * Set the echo format to be used by the compiler. + * + * @param string $format + * @return void + * @static + */ + public static function setEchoFormat($format) + { + \Illuminate\View\Compilers\BladeCompiler::setEchoFormat($format); + } + + /** + * Get the path to the compiled version of a view. + * + * @param string $path + * @return string + * @static + */ + public static function getCompiledPath($path) + { + //Method inherited from \Illuminate\View\Compilers\Compiler + return \Illuminate\View\Compilers\BladeCompiler::getCompiledPath($path); + } + + /** + * Determine if the view at the given path is expired. + * + * @param string $path + * @return bool + * @static + */ + public static function isExpired($path) + { + //Method inherited from \Illuminate\View\Compilers\Compiler + return \Illuminate\View\Compilers\BladeCompiler::isExpired($path); + } + + /** + * Compile the default values for the echo statement. + * + * @param string $value + * @return string + * @static + */ + public static function compileEchoDefaults($value) + { + return \Illuminate\View\Compilers\BladeCompiler::compileEchoDefaults($value); + } + } + + + class Bus extends \Illuminate\Support\Facades\Bus + { + /** + * Dispatch a command to its appropriate handler. + * + * @param mixed $command + * @return mixed + * @static + */ + public static function dispatch($command) + { + return \Illuminate\Bus\Dispatcher::dispatch($command); + } + + /** + * Dispatch a command to its appropriate handler in the current process. + * + * @param mixed $command + * @param mixed $handler + * @return mixed + * @static + */ + public static function dispatchNow($command, $handler = null) + { + return \Illuminate\Bus\Dispatcher::dispatchNow($command, $handler); + } + + /** + * Determine if the given command has a handler. + * + * @param mixed $command + * @return bool + * @static + */ + public static function hasCommandHandler($command) + { + return \Illuminate\Bus\Dispatcher::hasCommandHandler($command); + } + + /** + * Retrieve the handler for a command. + * + * @param mixed $command + * @return bool|mixed + * @static + */ + public static function getCommandHandler($command) + { + return \Illuminate\Bus\Dispatcher::getCommandHandler($command); + } + + /** + * Dispatch a command to its appropriate handler behind a queue. + * + * @param mixed $command + * @return mixed + * @throws \RuntimeException + * @static + */ + public static function dispatchToQueue($command) + { + return \Illuminate\Bus\Dispatcher::dispatchToQueue($command); + } + + /** + * Set the pipes through which commands should be piped before dispatching. + * + * @param array $pipes + * @return $this + * @static + */ + public static function pipeThrough($pipes) + { + return \Illuminate\Bus\Dispatcher::pipeThrough($pipes); + } + + /** + * Map a command to a handler. + * + * @param array $map + * @return $this + * @static + */ + public static function map($map) + { + return \Illuminate\Bus\Dispatcher::map($map); + } + } + + + class Cache extends \Illuminate\Support\Facades\Cache + { + /** + * Get a cache store instance by name. + * + * @param string|null $name + * @return mixed + * @static + */ + public static function store($name = null) + { + return \Illuminate\Cache\CacheManager::store($name); + } + + /** + * Get a cache driver instance. + * + * @param string $driver + * @return mixed + * @static + */ + public static function driver($driver = null) + { + return \Illuminate\Cache\CacheManager::driver($driver); + } + + /** + * Create a new cache repository with the given implementation. + * + * @param \Illuminate\Contracts\Cache\Store $store + * @return \Illuminate\Cache\Repository + * @static + */ + public static function repository($store) + { + return \Illuminate\Cache\CacheManager::repository($store); + } + + /** + * Get the default cache driver name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Cache\CacheManager::getDefaultDriver(); + } + + /** + * Set the default cache driver name. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultDriver($name) + { + \Illuminate\Cache\CacheManager::setDefaultDriver($name); + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + * @static + */ + public static function extend($driver, $callback) + { + return \Illuminate\Cache\CacheManager::extend($driver, $callback); + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + * @static + */ + public static function has($key) + { + return \Illuminate\Cache\Repository::has($key); + } + + /** + * Retrieve an item from the cache by key. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function get($key, $default = null) + { + return \Illuminate\Cache\Repository::get($key, $default); + } + + /** + * Retrieve multiple items from the cache by key. + * + * Items not found in the cache will have a null value. + * + * @param array $keys + * @return array + * @static + */ + public static function many($keys) + { + return \Illuminate\Cache\Repository::many($keys); + } + + /** + * Retrieve an item from the cache and delete it. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function pull($key, $default = null) + { + return \Illuminate\Cache\Repository::pull($key, $default); + } + + /** + * Store an item in the cache. + * + * @param string $key + * @param mixed $value + * @param \DateTime|float|int $minutes + * @return void + * @static + */ + public static function put($key, $value, $minutes = null) + { + \Illuminate\Cache\Repository::put($key, $value, $minutes); + } + + /** + * Store multiple items in the cache for a given number of minutes. + * + * @param array $values + * @param float|int $minutes + * @return void + * @static + */ + public static function putMany($values, $minutes) + { + \Illuminate\Cache\Repository::putMany($values, $minutes); + } + + /** + * Store an item in the cache if the key does not exist. + * + * @param string $key + * @param mixed $value + * @param \DateTime|float|int $minutes + * @return bool + * @static + */ + public static function add($key, $value, $minutes) + { + return \Illuminate\Cache\Repository::add($key, $value, $minutes); + } + + /** + * Increment the value of an item in the cache. + * + * @param string $key + * @param mixed $value + * @return int|bool + * @static + */ + public static function increment($key, $value = 1) + { + return \Illuminate\Cache\Repository::increment($key, $value); + } + + /** + * Decrement the value of an item in the cache. + * + * @param string $key + * @param mixed $value + * @return int|bool + * @static + */ + public static function decrement($key, $value = 1) + { + return \Illuminate\Cache\Repository::decrement($key, $value); + } + + /** + * Store an item in the cache indefinitely. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function forever($key, $value) + { + \Illuminate\Cache\Repository::forever($key, $value); + } + + /** + * Get an item from the cache, or store the default value. + * + * @param string $key + * @param \DateTime|float|int $minutes + * @param \Closure $callback + * @return mixed + * @static + */ + public static function remember($key, $minutes, $callback) + { + return \Illuminate\Cache\Repository::remember($key, $minutes, $callback); + } + + /** + * Get an item from the cache, or store the default value forever. + * + * @param string $key + * @param \Closure $callback + * @return mixed + * @static + */ + public static function sear($key, $callback) + { + return \Illuminate\Cache\Repository::sear($key, $callback); + } + + /** + * Get an item from the cache, or store the default value forever. + * + * @param string $key + * @param \Closure $callback + * @return mixed + * @static + */ + public static function rememberForever($key, $callback) + { + return \Illuminate\Cache\Repository::rememberForever($key, $callback); + } + + /** + * Remove an item from the cache. + * + * @param string $key + * @return bool + * @static + */ + public static function forget($key) + { + return \Illuminate\Cache\Repository::forget($key); + } + + /** + * Begin executing a new tags operation if the store supports it. + * + * @param array|mixed $names + * @return \Illuminate\Cache\TaggedCache + * @throws \BadMethodCallException + * @static + */ + public static function tags($names) + { + return \Illuminate\Cache\Repository::tags($names); + } + + /** + * Get the default cache time. + * + * @return float|int + * @static + */ + public static function getDefaultCacheTime() + { + return \Illuminate\Cache\Repository::getDefaultCacheTime(); + } + + /** + * Set the default cache time in minutes. + * + * @param float|int $minutes + * @return $this + * @static + */ + public static function setDefaultCacheTime($minutes) + { + return \Illuminate\Cache\Repository::setDefaultCacheTime($minutes); + } + + /** + * Get the cache store implementation. + * + * @return \Illuminate\Contracts\Cache\Store + * @static + */ + public static function getStore() + { + return \Illuminate\Cache\Repository::getStore(); + } + + /** + * Set the event dispatcher instance. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + * @static + */ + public static function setEventDispatcher($events) + { + \Illuminate\Cache\Repository::setEventDispatcher($events); + } + + /** + * Determine if a cached value exists. + * + * @param string $key + * @return bool + * @static + */ + public static function offsetExists($key) + { + return \Illuminate\Cache\Repository::offsetExists($key); + } + + /** + * Retrieve an item from the cache by key. + * + * @param string $key + * @return mixed + * @static + */ + public static function offsetGet($key) + { + return \Illuminate\Cache\Repository::offsetGet($key); + } + + /** + * Store an item in the cache for the default time. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function offsetSet($key, $value) + { + \Illuminate\Cache\Repository::offsetSet($key, $value); + } + + /** + * Remove an item from the cache. + * + * @param string $key + * @return void + * @static + */ + public static function offsetUnset($key) + { + \Illuminate\Cache\Repository::offsetUnset($key); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Cache\Repository::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Cache\Repository::hasMacro($name); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method + * @param array $parameters + * @return mixed + * @throws \BadMethodCallException + * @static + */ + public static function macroCall($method, $parameters) + { + return \Illuminate\Cache\Repository::macroCall($method, $parameters); + } + + /** + * Remove all items from the cache. + * + * @return bool + * @static + */ + public static function flush() + { + return \Illuminate\Cache\FileStore::flush(); + } + + /** + * Get the Filesystem instance. + * + * @return \Illuminate\Filesystem\Filesystem + * @static + */ + public static function getFilesystem() + { + return \Illuminate\Cache\FileStore::getFilesystem(); + } + + /** + * Get the working directory of the cache. + * + * @return string + * @static + */ + public static function getDirectory() + { + return \Illuminate\Cache\FileStore::getDirectory(); + } + + /** + * Get the cache key prefix. + * + * @return string + * @static + */ + public static function getPrefix() + { + return \Illuminate\Cache\FileStore::getPrefix(); + } + } + + + class Config extends \Illuminate\Support\Facades\Config + { + /** + * Determine if the given configuration value exists. + * + * @param string $key + * @return bool + * @static + */ + public static function has($key) + { + return \Illuminate\Config\Repository::has($key); + } + + /** + * Get the specified configuration value. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function get($key, $default = null) + { + return \Illuminate\Config\Repository::get($key, $default); + } + + /** + * Set a given configuration value. + * + * @param array|string $key + * @param mixed $value + * @return void + * @static + */ + public static function set($key, $value = null) + { + \Illuminate\Config\Repository::set($key, $value); + } + + /** + * Prepend a value onto an array configuration value. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function prepend($key, $value) + { + \Illuminate\Config\Repository::prepend($key, $value); + } + + /** + * Push a value onto an array configuration value. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function push($key, $value) + { + \Illuminate\Config\Repository::push($key, $value); + } + + /** + * Get all of the configuration items for the application. + * + * @return array + * @static + */ + public static function all() + { + return \Illuminate\Config\Repository::all(); + } + + /** + * Determine if the given configuration option exists. + * + * @param string $key + * @return bool + * @static + */ + public static function offsetExists($key) + { + return \Illuminate\Config\Repository::offsetExists($key); + } + + /** + * Get a configuration option. + * + * @param string $key + * @return mixed + * @static + */ + public static function offsetGet($key) + { + return \Illuminate\Config\Repository::offsetGet($key); + } + + /** + * Set a configuration option. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function offsetSet($key, $value) + { + \Illuminate\Config\Repository::offsetSet($key, $value); + } + + /** + * Unset a configuration option. + * + * @param string $key + * @return void + * @static + */ + public static function offsetUnset($key) + { + \Illuminate\Config\Repository::offsetUnset($key); + } + } + + + class Cookie extends \Illuminate\Support\Facades\Cookie + { + /** + * Create a new cookie instance. + * + * @param string $name + * @param string $value + * @param int $minutes + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httpOnly + * @return \Symfony\Component\HttpFoundation\Cookie + * @static + */ + public static function make($name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true) + { + return \Illuminate\Cookie\CookieJar::make($name, $value, $minutes, $path, $domain, $secure, $httpOnly); + } + + /** + * Create a cookie that lasts "forever" (five years). + * + * @param string $name + * @param string $value + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httpOnly + * @return \Symfony\Component\HttpFoundation\Cookie + * @static + */ + public static function forever($name, $value, $path = null, $domain = null, $secure = false, $httpOnly = true) + { + return \Illuminate\Cookie\CookieJar::forever($name, $value, $path, $domain, $secure, $httpOnly); + } + + /** + * Expire the given cookie. + * + * @param string $name + * @param string $path + * @param string $domain + * @return \Symfony\Component\HttpFoundation\Cookie + * @static + */ + public static function forget($name, $path = null, $domain = null) + { + return \Illuminate\Cookie\CookieJar::forget($name, $path, $domain); + } + + /** + * Determine if a cookie has been queued. + * + * @param string $key + * @return bool + * @static + */ + public static function hasQueued($key) + { + return \Illuminate\Cookie\CookieJar::hasQueued($key); + } + + /** + * Get a queued cookie instance. + * + * @param string $key + * @param mixed $default + * @return \Symfony\Component\HttpFoundation\Cookie + * @static + */ + public static function queued($key, $default = null) + { + return \Illuminate\Cookie\CookieJar::queued($key, $default); + } + + /** + * Queue a cookie to send with the next response. + * + * @param array $parameters + * @return void + * @static + */ + public static function queue($parameters = null) + { + \Illuminate\Cookie\CookieJar::queue($parameters); + } + + /** + * Remove a cookie from the queue. + * + * @param string $name + * @return void + * @static + */ + public static function unqueue($name) + { + \Illuminate\Cookie\CookieJar::unqueue($name); + } + + /** + * Set the default path and domain for the jar. + * + * @param string $path + * @param string $domain + * @param bool $secure + * @return $this + * @static + */ + public static function setDefaultPathAndDomain($path, $domain, $secure = false) + { + return \Illuminate\Cookie\CookieJar::setDefaultPathAndDomain($path, $domain, $secure); + } + + /** + * Get the cookies which have been queued for the next request. + * + * @return array + * @static + */ + public static function getQueuedCookies() + { + return \Illuminate\Cookie\CookieJar::getQueuedCookies(); + } + } + + + class Crypt extends \Illuminate\Support\Facades\Crypt + { + /** + * Determine if the given key and cipher combination is valid. + * + * @param string $key + * @param string $cipher + * @return bool + * @static + */ + public static function supported($key, $cipher) + { + return \Illuminate\Encryption\Encrypter::supported($key, $cipher); + } + + /** + * Encrypt the given value. + * + * @param mixed $value + * @param bool $serialize + * @return string + * @throws \Illuminate\Contracts\Encryption\EncryptException + * @static + */ + public static function encrypt($value, $serialize = true) + { + return \Illuminate\Encryption\Encrypter::encrypt($value, $serialize); + } + + /** + * Encrypt a string without serialization. + * + * @param string $value + * @return string + * @static + */ + public static function encryptString($value) + { + return \Illuminate\Encryption\Encrypter::encryptString($value); + } + + /** + * Decrypt the given value. + * + * @param mixed $payload + * @param bool $unserialize + * @return string + * @throws \Illuminate\Contracts\Encryption\DecryptException + * @static + */ + public static function decrypt($payload, $unserialize = true) + { + return \Illuminate\Encryption\Encrypter::decrypt($payload, $unserialize); + } + + /** + * Decrypt the given string without unserialization. + * + * @param string $payload + * @return string + * @static + */ + public static function decryptString($payload) + { + return \Illuminate\Encryption\Encrypter::decryptString($payload); + } + + /** + * Get the encryption key. + * + * @return string + * @static + */ + public static function getKey() + { + return \Illuminate\Encryption\Encrypter::getKey(); + } + } + + + class DB extends \Illuminate\Support\Facades\DB + { + /** + * Get a database connection instance. + * + * @param string $name + * @return \Illuminate\Database\Connection + * @static + */ + public static function connection($name = null) + { + return \Illuminate\Database\DatabaseManager::connection($name); + } + + /** + * Disconnect from the given database and remove from local cache. + * + * @param string $name + * @return void + * @static + */ + public static function purge($name = null) + { + \Illuminate\Database\DatabaseManager::purge($name); + } + + /** + * Disconnect from the given database. + * + * @param string $name + * @return void + * @static + */ + public static function disconnect($name = null) + { + \Illuminate\Database\DatabaseManager::disconnect($name); + } + + /** + * Reconnect to the given database. + * + * @param string $name + * @return \Illuminate\Database\Connection + * @static + */ + public static function reconnect($name = null) + { + return \Illuminate\Database\DatabaseManager::reconnect($name); + } + + /** + * Get the default connection name. + * + * @return string + * @static + */ + public static function getDefaultConnection() + { + return \Illuminate\Database\DatabaseManager::getDefaultConnection(); + } + + /** + * Set the default connection name. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultConnection($name) + { + \Illuminate\Database\DatabaseManager::setDefaultConnection($name); + } + + /** + * Get all of the support drivers. + * + * @return array + * @static + */ + public static function supportedDrivers() + { + return \Illuminate\Database\DatabaseManager::supportedDrivers(); + } + + /** + * Get all of the drivers that are actually available. + * + * @return array + * @static + */ + public static function availableDrivers() + { + return \Illuminate\Database\DatabaseManager::availableDrivers(); + } + + /** + * Register an extension connection resolver. + * + * @param string $name + * @param callable $resolver + * @return void + * @static + */ + public static function extend($name, $resolver) + { + \Illuminate\Database\DatabaseManager::extend($name, $resolver); + } + + /** + * Return all of the created connections. + * + * @return array + * @static + */ + public static function getConnections() + { + return \Illuminate\Database\DatabaseManager::getConnections(); + } + + /** + * Get a schema builder instance for the connection. + * + * @return \Illuminate\Database\Schema\MySqlBuilder + * @static + */ + public static function getSchemaBuilder() + { + return \Illuminate\Database\MySqlConnection::getSchemaBuilder(); + } + + /** + * Bind values to their parameters in the given statement. + * + * @param \PDOStatement $statement + * @param array $bindings + * @return void + * @static + */ + public static function bindValues($statement, $bindings) + { + \Illuminate\Database\MySqlConnection::bindValues($statement, $bindings); + } + + /** + * Set the query grammar to the default implementation. + * + * @return void + * @static + */ + public static function useDefaultQueryGrammar() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::useDefaultQueryGrammar(); + } + + /** + * Set the schema grammar to the default implementation. + * + * @return void + * @static + */ + public static function useDefaultSchemaGrammar() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::useDefaultSchemaGrammar(); + } + + /** + * Set the query post processor to the default implementation. + * + * @return void + * @static + */ + public static function useDefaultPostProcessor() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::useDefaultPostProcessor(); + } + + /** + * Begin a fluent query against a database table. + * + * @param string $table + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function table($table) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::table($table); + } + + /** + * Get a new query builder instance. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function query() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::query(); + } + + /** + * Run a select statement and return a single result. + * + * @param string $query + * @param array $bindings + * @param bool $useReadPdo + * @return mixed + * @static + */ + public static function selectOne($query, $bindings = [], $useReadPdo = true) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::selectOne($query, $bindings, $useReadPdo); + } + + /** + * Run a select statement against the database. + * + * @param string $query + * @param array $bindings + * @return array + * @static + */ + public static function selectFromWriteConnection($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::selectFromWriteConnection($query, $bindings); + } + + /** + * Run a select statement against the database. + * + * @param string $query + * @param array $bindings + * @param bool $useReadPdo + * @return array + * @static + */ + public static function select($query, $bindings = [], $useReadPdo = true) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::select($query, $bindings, $useReadPdo); + } + + /** + * Run a select statement against the database and returns a generator. + * + * @param string $query + * @param array $bindings + * @param bool $useReadPdo + * @return \Generator + * @static + */ + public static function cursor($query, $bindings = [], $useReadPdo = true) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::cursor($query, $bindings, $useReadPdo); + } + + /** + * Run an insert statement against the database. + * + * @param string $query + * @param array $bindings + * @return bool + * @static + */ + public static function insert($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::insert($query, $bindings); + } + + /** + * Run an update statement against the database. + * + * @param string $query + * @param array $bindings + * @return int + * @static + */ + public static function update($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::update($query, $bindings); + } + + /** + * Run a delete statement against the database. + * + * @param string $query + * @param array $bindings + * @return int + * @static + */ + public static function delete($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::delete($query, $bindings); + } + + /** + * Execute an SQL statement and return the boolean result. + * + * @param string $query + * @param array $bindings + * @return bool + * @static + */ + public static function statement($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::statement($query, $bindings); + } + + /** + * Run an SQL statement and get the number of rows affected. + * + * @param string $query + * @param array $bindings + * @return int + * @static + */ + public static function affectingStatement($query, $bindings = []) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::affectingStatement($query, $bindings); + } + + /** + * Run a raw, unprepared query against the PDO connection. + * + * @param string $query + * @return bool + * @static + */ + public static function unprepared($query) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::unprepared($query); + } + + /** + * Execute the given callback in "dry run" mode. + * + * @param \Closure $callback + * @return array + * @static + */ + public static function pretend($callback) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::pretend($callback); + } + + /** + * Prepare the query bindings for execution. + * + * @param array $bindings + * @return array + * @static + */ + public static function prepareBindings($bindings) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::prepareBindings($bindings); + } + + /** + * Log a query in the connection's query log. + * + * @param string $query + * @param array $bindings + * @param float|null $time + * @return void + * @static + */ + public static function logQuery($query, $bindings, $time = null) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::logQuery($query, $bindings, $time); + } + + /** + * Register a database query listener with the connection. + * + * @param \Closure $callback + * @return void + * @static + */ + public static function listen($callback) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::listen($callback); + } + + /** + * Get a new raw query expression. + * + * @param mixed $value + * @return \Illuminate\Database\Query\Expression + * @static + */ + public static function raw($value) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::raw($value); + } + + /** + * Is Doctrine available? + * + * @return bool + * @static + */ + public static function isDoctrineAvailable() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::isDoctrineAvailable(); + } + + /** + * Get a Doctrine Schema Column instance. + * + * @param string $table + * @param string $column + * @return \Doctrine\DBAL\Schema\Column + * @static + */ + public static function getDoctrineColumn($table, $column) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getDoctrineColumn($table, $column); + } + + /** + * Get the Doctrine DBAL schema manager for the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + * @static + */ + public static function getDoctrineSchemaManager() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getDoctrineSchemaManager(); + } + + /** + * Get the Doctrine DBAL database connection instance. + * + * @return \Doctrine\DBAL\Connection + * @static + */ + public static function getDoctrineConnection() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getDoctrineConnection(); + } + + /** + * Get the current PDO connection. + * + * @return \PDO + * @static + */ + public static function getPdo() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getPdo(); + } + + /** + * Get the current PDO connection used for reading. + * + * @return \PDO + * @static + */ + public static function getReadPdo() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getReadPdo(); + } + + /** + * Set the PDO connection. + * + * @param \PDO|null $pdo + * @return $this + * @static + */ + public static function setPdo($pdo) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::setPdo($pdo); + } + + /** + * Set the PDO connection used for reading. + * + * @param \PDO|null $pdo + * @return $this + * @static + */ + public static function setReadPdo($pdo) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::setReadPdo($pdo); + } + + /** + * Set the reconnect instance on the connection. + * + * @param callable $reconnector + * @return $this + * @static + */ + public static function setReconnector($reconnector) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::setReconnector($reconnector); + } + + /** + * Get the database connection name. + * + * @return string|null + * @static + */ + public static function getName() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getName(); + } + + /** + * Get an option from the configuration options. + * + * @param string $option + * @return mixed + * @static + */ + public static function getConfig($option) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getConfig($option); + } + + /** + * Get the PDO driver name. + * + * @return string + * @static + */ + public static function getDriverName() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getDriverName(); + } + + /** + * Get the query grammar used by the connection. + * + * @return \Illuminate\Database\Query\Grammars\Grammar + * @static + */ + public static function getQueryGrammar() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getQueryGrammar(); + } + + /** + * Set the query grammar used by the connection. + * + * @param \Illuminate\Database\Query\Grammars\Grammar $grammar + * @return void + * @static + */ + public static function setQueryGrammar($grammar) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::setQueryGrammar($grammar); + } + + /** + * Get the schema grammar used by the connection. + * + * @return \Illuminate\Database\Schema\Grammars\Grammar + * @static + */ + public static function getSchemaGrammar() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getSchemaGrammar(); + } + + /** + * Set the schema grammar used by the connection. + * + * @param \Illuminate\Database\Schema\Grammars\Grammar $grammar + * @return void + * @static + */ + public static function setSchemaGrammar($grammar) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::setSchemaGrammar($grammar); + } + + /** + * Get the query post processor used by the connection. + * + * @return \Illuminate\Database\Query\Processors\Processor + * @static + */ + public static function getPostProcessor() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getPostProcessor(); + } + + /** + * Set the query post processor used by the connection. + * + * @param \Illuminate\Database\Query\Processors\Processor $processor + * @return void + * @static + */ + public static function setPostProcessor($processor) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::setPostProcessor($processor); + } + + /** + * Get the event dispatcher used by the connection. + * + * @return \Illuminate\Contracts\Events\Dispatcher + * @static + */ + public static function getEventDispatcher() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getEventDispatcher(); + } + + /** + * Set the event dispatcher instance on the connection. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + * @static + */ + public static function setEventDispatcher($events) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::setEventDispatcher($events); + } + + /** + * Determine if the connection in a "dry run". + * + * @return bool + * @static + */ + public static function pretending() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::pretending(); + } + + /** + * Get the connection query log. + * + * @return array + * @static + */ + public static function getQueryLog() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getQueryLog(); + } + + /** + * Clear the query log. + * + * @return void + * @static + */ + public static function flushQueryLog() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::flushQueryLog(); + } + + /** + * Enable the query log on the connection. + * + * @return void + * @static + */ + public static function enableQueryLog() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::enableQueryLog(); + } + + /** + * Disable the query log on the connection. + * + * @return void + * @static + */ + public static function disableQueryLog() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::disableQueryLog(); + } + + /** + * Determine whether we're logging queries. + * + * @return bool + * @static + */ + public static function logging() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::logging(); + } + + /** + * Get the name of the connected database. + * + * @return string + * @static + */ + public static function getDatabaseName() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getDatabaseName(); + } + + /** + * Set the name of the connected database. + * + * @param string $database + * @return string + * @static + */ + public static function setDatabaseName($database) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::setDatabaseName($database); + } + + /** + * Get the table prefix for the connection. + * + * @return string + * @static + */ + public static function getTablePrefix() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getTablePrefix(); + } + + /** + * Set the table prefix in use by the connection. + * + * @param string $prefix + * @return void + * @static + */ + public static function setTablePrefix($prefix) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::setTablePrefix($prefix); + } + + /** + * Set the table prefix and return the grammar. + * + * @param \Illuminate\Database\Grammar $grammar + * @return \Illuminate\Database\Grammar + * @static + */ + public static function withTablePrefix($grammar) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::withTablePrefix($grammar); + } + + /** + * Register a connection resolver. + * + * @param string $driver + * @param \Closure $callback + * @return void + * @static + */ + public static function resolverFor($driver, $callback) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::resolverFor($driver, $callback); + } + + /** + * Get the connection resolver for the given driver. + * + * @param string $driver + * @return mixed + * @static + */ + public static function getResolver($driver) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::getResolver($driver); + } + + /** + * Execute a Closure within a transaction. + * + * @param \Closure $callback + * @param int $attempts + * @return mixed + * @throws \Exception|\Throwable + * @static + */ + public static function transaction($callback, $attempts = 1) + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::transaction($callback, $attempts); + } + + /** + * Start a new database transaction. + * + * @return void + * @throws \Exception + * @static + */ + public static function beginTransaction() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::beginTransaction(); + } + + /** + * Commit the active database transaction. + * + * @return void + * @static + */ + public static function commit() + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::commit(); + } + + /** + * Rollback the active database transaction. + * + * @param int|null $toLevel + * @return void + * @static + */ + public static function rollBack($toLevel = null) + { + //Method inherited from \Illuminate\Database\Connection + \Illuminate\Database\MySqlConnection::rollBack($toLevel); + } + + /** + * Get the number of active transactions. + * + * @return int + * @static + */ + public static function transactionLevel() + { + //Method inherited from \Illuminate\Database\Connection + return \Illuminate\Database\MySqlConnection::transactionLevel(); + } + } + + + class Eloquent extends \Illuminate\Database\Eloquent\Model + { + /** + * Register a new global scope. + * + * @param string $identifier + * @param \Illuminate\Database\Eloquent\Scope|\Closure $scope + * @return $this + * @static + */ + public static function withGlobalScope($identifier, $scope) + { + return \Illuminate\Database\Eloquent\Builder::withGlobalScope($identifier, $scope); + } + + /** + * Remove a registered global scope. + * + * @param \Illuminate\Database\Eloquent\Scope|string $scope + * @return $this + * @static + */ + public static function withoutGlobalScope($scope) + { + return \Illuminate\Database\Eloquent\Builder::withoutGlobalScope($scope); + } + + /** + * Remove all or passed registered global scopes. + * + * @param array|null $scopes + * @return $this + * @static + */ + public static function withoutGlobalScopes($scopes = null) + { + return \Illuminate\Database\Eloquent\Builder::withoutGlobalScopes($scopes); + } + + /** + * Get an array of global scopes that were removed from the query. + * + * @return array + * @static + */ + public static function removedScopes() + { + return \Illuminate\Database\Eloquent\Builder::removedScopes(); + } + + /** + * Apply the callback's query changes if the given "value" is true. + * + * @param bool $value + * @param \Closure $callback + * @param \Closure $default + * @return $this + * @static + */ + public static function when($value, $callback, $default = null) + { + return \Illuminate\Database\Eloquent\Builder::when($value, $callback, $default); + } + + /** + * Add a where clause on the primary key to the query. + * + * @param mixed $id + * @return $this + * @static + */ + public static function whereKey($id) + { + return \Illuminate\Database\Eloquent\Builder::whereKey($id); + } + + /** + * Add a basic where clause to the query. + * + * @param string|\Closure $column + * @param string $operator + * @param mixed $value + * @param string $boolean + * @return $this + * @static + */ + public static function where($column, $operator = null, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Eloquent\Builder::where($column, $operator, $value, $boolean); + } + + /** + * Add an "or where" clause to the query. + * + * @param string|\Closure $column + * @param string $operator + * @param mixed $value + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function orWhere($column, $operator = null, $value = null) + { + return \Illuminate\Database\Eloquent\Builder::orWhere($column, $operator, $value); + } + + /** + * Create a collection of models from plain arrays. + * + * @param array $items + * @return \Illuminate\Database\Eloquent\Collection + * @static + */ + public static function hydrate($items) + { + return \Illuminate\Database\Eloquent\Builder::hydrate($items); + } + + /** + * Create a collection of models from a raw query. + * + * @param string $query + * @param array $bindings + * @return \Illuminate\Database\Eloquent\Collection + * @static + */ + public static function fromQuery($query, $bindings = []) + { + return \Illuminate\Database\Eloquent\Builder::fromQuery($query, $bindings); + } + + /** + * Find a model by its primary key. + * + * @param mixed $id + * @param array $columns + * @return mixed + * @static + */ + public static function find($id, $columns = []) + { + return \Illuminate\Database\Eloquent\Builder::find($id, $columns); + } + + /** + * Find multiple models by their primary keys. + * + * @param array $ids + * @param array $columns + * @return \Illuminate\Database\Eloquent\Collection + * @static + */ + public static function findMany($ids, $columns = []) + { + return \Illuminate\Database\Eloquent\Builder::findMany($ids, $columns); + } + + /** + * Find a model by its primary key or throw an exception. + * + * @param mixed $id + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @static + */ + public static function findOrFail($id, $columns = []) + { + return \Illuminate\Database\Eloquent\Builder::findOrFail($id, $columns); + } + + /** + * Find a model by its primary key or return fresh model instance. + * + * @param mixed $id + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function findOrNew($id, $columns = []) + { + return \Illuminate\Database\Eloquent\Builder::findOrNew($id, $columns); + } + + /** + * Get the first record matching the attributes or instantiate it. + * + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function firstOrNew($attributes, $values = []) + { + return \Illuminate\Database\Eloquent\Builder::firstOrNew($attributes, $values); + } + + /** + * Get the first record matching the attributes or create it. + * + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function firstOrCreate($attributes, $values = []) + { + return \Illuminate\Database\Eloquent\Builder::firstOrCreate($attributes, $values); + } + + /** + * Create or update a record matching the attributes, and fill it with values. + * + * @param array $attributes + * @param array $values + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function updateOrCreate($attributes, $values = []) + { + return \Illuminate\Database\Eloquent\Builder::updateOrCreate($attributes, $values); + } + + /** + * Execute the query and get the first result. + * + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model|static|null + * @static + */ + public static function first($columns = []) + { + return \Illuminate\Database\Eloquent\Builder::first($columns); + } + + /** + * Execute the query and get the first result or throw an exception. + * + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model|static + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @static + */ + public static function firstOrFail($columns = []) + { + return \Illuminate\Database\Eloquent\Builder::firstOrFail($columns); + } + + /** + * Execute the query and get the first result or call a callback. + * + * @param \Closure|array $columns + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Model|static|mixed + * @static + */ + public static function firstOr($columns = [], $callback = null) + { + return \Illuminate\Database\Eloquent\Builder::firstOr($columns, $callback); + } + + /** + * Get a single column's value from the first result of a query. + * + * @param string $column + * @return mixed + * @static + */ + public static function value($column) + { + return \Illuminate\Database\Eloquent\Builder::value($column); + } + + /** + * Execute the query as a "select" statement. + * + * @param array $columns + * @return \Illuminate\Database\Eloquent\Collection|static[] + * @static + */ + public static function get($columns = []) + { + return \Illuminate\Database\Eloquent\Builder::get($columns); + } + + /** + * Get the hydrated models without eager loading. + * + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model[] + * @static + */ + public static function getModels($columns = []) + { + return \Illuminate\Database\Eloquent\Builder::getModels($columns); + } + + /** + * Eager load the relationships for the models. + * + * @param array $models + * @return array + * @static + */ + public static function eagerLoadRelations($models) + { + return \Illuminate\Database\Eloquent\Builder::eagerLoadRelations($models); + } + + /** + * Get a generator for the given query. + * + * @return \Generator + * @static + */ + public static function cursor() + { + return \Illuminate\Database\Eloquent\Builder::cursor(); + } + + /** + * Chunk the results of the query. + * + * @param int $count + * @param callable $callback + * @return bool + * @static + */ + public static function chunk($count, $callback) + { + return \Illuminate\Database\Eloquent\Builder::chunk($count, $callback); + } + + /** + * Chunk the results of a query by comparing numeric IDs. + * + * @param int $count + * @param callable $callback + * @param string $column + * @param string|null $alias + * @return bool + * @static + */ + public static function chunkById($count, $callback, $column = null, $alias = null) + { + return \Illuminate\Database\Eloquent\Builder::chunkById($count, $callback, $column, $alias); + } + + /** + * Execute a callback over each item while chunking. + * + * @param callable $callback + * @param int $count + * @return bool + * @static + */ + public static function each($callback, $count = 1000) + { + return \Illuminate\Database\Eloquent\Builder::each($callback, $count); + } + + /** + * Get an array with the values of a given column. + * + * @param string $column + * @param string|null $key + * @return \Illuminate\Support\Collection + * @static + */ + public static function pluck($column, $key = null) + { + return \Illuminate\Database\Eloquent\Builder::pluck($column, $key); + } + + /** + * Paginate the given query. + * + * @param int $perPage + * @param array $columns + * @param string $pageName + * @param int|null $page + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + * @throws \InvalidArgumentException + * @static + */ + public static function paginate($perPage = null, $columns = [], $pageName = 'page', $page = null) + { + return \Illuminate\Database\Eloquent\Builder::paginate($perPage, $columns, $pageName, $page); + } + + /** + * Paginate the given query into a simple paginator. + * + * @param int $perPage + * @param array $columns + * @param string $pageName + * @param int|null $page + * @return \Illuminate\Contracts\Pagination\Paginator + * @static + */ + public static function simplePaginate($perPage = null, $columns = [], $pageName = 'page', $page = null) + { + return \Illuminate\Database\Eloquent\Builder::simplePaginate($perPage, $columns, $pageName, $page); + } + + /** + * Save a new model and return the instance. + * + * @param array $attributes + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function create($attributes = []) + { + return \Illuminate\Database\Eloquent\Builder::create($attributes); + } + + /** + * Save a new model and return the instance. Allow mass-assignment. + * + * @param array $attributes + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function forceCreate($attributes) + { + return \Illuminate\Database\Eloquent\Builder::forceCreate($attributes); + } + + /** + * Register a replacement for the default delete function. + * + * @param \Closure $callback + * @return void + * @static + */ + public static function onDelete($callback) + { + \Illuminate\Database\Eloquent\Builder::onDelete($callback); + } + + /** + * Call the given local model scopes. + * + * @param array $scopes + * @return mixed + * @static + */ + public static function scopes($scopes) + { + return \Illuminate\Database\Eloquent\Builder::scopes($scopes); + } + + /** + * Apply the scopes to the Eloquent builder instance and return it. + * + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function applyScopes() + { + return \Illuminate\Database\Eloquent\Builder::applyScopes(); + } + + /** + * Prevent the specified relations from being eager loaded. + * + * @param mixed $relations + * @return $this + * @static + */ + public static function without($relations) + { + return \Illuminate\Database\Eloquent\Builder::without($relations); + } + + /** + * Get the underlying query builder instance. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function getQuery() + { + return \Illuminate\Database\Eloquent\Builder::getQuery(); + } + + /** + * Set the underlying query builder instance. + * + * @param \Illuminate\Database\Query\Builder $query + * @return $this + * @static + */ + public static function setQuery($query) + { + return \Illuminate\Database\Eloquent\Builder::setQuery($query); + } + + /** + * Get a base query builder instance. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function toBase() + { + return \Illuminate\Database\Eloquent\Builder::toBase(); + } + + /** + * Get the relationships being eagerly loaded. + * + * @return array + * @static + */ + public static function getEagerLoads() + { + return \Illuminate\Database\Eloquent\Builder::getEagerLoads(); + } + + /** + * Set the relationships being eagerly loaded. + * + * @param array $eagerLoad + * @return $this + * @static + */ + public static function setEagerLoads($eagerLoad) + { + return \Illuminate\Database\Eloquent\Builder::setEagerLoads($eagerLoad); + } + + /** + * Get the model instance being queried. + * + * @return \Illuminate\Database\Eloquent\Model + * @static + */ + public static function getModel() + { + return \Illuminate\Database\Eloquent\Builder::getModel(); + } + + /** + * Set a model instance for the model being queried. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @return $this + * @static + */ + public static function setModel($model) + { + return \Illuminate\Database\Eloquent\Builder::setModel($model); + } + + /** + * Extend the builder with a given callback. + * + * @param string $name + * @param \Closure $callback + * @return void + * @static + */ + public static function macro($name, $callback) + { + \Illuminate\Database\Eloquent\Builder::macro($name, $callback); + } + + /** + * Get the given macro by name. + * + * @param string $name + * @return \Closure + * @static + */ + public static function getMacro($name) + { + return \Illuminate\Database\Eloquent\Builder::getMacro($name); + } + + /** + * Add a relationship count / exists condition to the query. + * + * @param string $relation + * @param string $operator + * @param int $count + * @param string $boolean + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null) + { + return \Illuminate\Database\Eloquent\Builder::has($relation, $operator, $count, $boolean, $callback); + } + + /** + * Add a relationship count / exists condition to the query with an "or". + * + * @param string $relation + * @param string $operator + * @param int $count + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function orHas($relation, $operator = '>=', $count = 1) + { + return \Illuminate\Database\Eloquent\Builder::orHas($relation, $operator, $count); + } + + /** + * Add a relationship count / exists condition to the query. + * + * @param string $relation + * @param string $boolean + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function doesntHave($relation, $boolean = 'and', $callback = null) + { + return \Illuminate\Database\Eloquent\Builder::doesntHave($relation, $boolean, $callback); + } + + /** + * Add a relationship count / exists condition to the query with where clauses. + * + * @param string $relation + * @param \Closure|null $callback + * @param string $operator + * @param int $count + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function whereHas($relation, $callback = null, $operator = '>=', $count = 1) + { + return \Illuminate\Database\Eloquent\Builder::whereHas($relation, $callback, $operator, $count); + } + + /** + * Add a relationship count / exists condition to the query with where clauses and an "or". + * + * @param string $relation + * @param \Closure $callback + * @param string $operator + * @param int $count + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function orWhereHas($relation, $callback, $operator = '>=', $count = 1) + { + return \Illuminate\Database\Eloquent\Builder::orWhereHas($relation, $callback, $operator, $count); + } + + /** + * Add a relationship count / exists condition to the query with where clauses. + * + * @param string $relation + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function whereDoesntHave($relation, $callback = null) + { + return \Illuminate\Database\Eloquent\Builder::whereDoesntHave($relation, $callback); + } + + /** + * Add subselect queries to count the relations. + * + * @param mixed $relations + * @return $this + * @static + */ + public static function withCount($relations) + { + return \Illuminate\Database\Eloquent\Builder::withCount($relations); + } + + /** + * Merge the where constraints from another query to the current query. + * + * @param \Illuminate\Database\Eloquent\Builder $from + * @return \Illuminate\Database\Eloquent\Builder|static + * @static + */ + public static function mergeConstraintsFrom($from) + { + return \Illuminate\Database\Eloquent\Builder::mergeConstraintsFrom($from); + } + + /** + * Set the columns to be selected. + * + * @param array|mixed $columns + * @return $this + * @static + */ + public static function select($columns = []) + { + return \Illuminate\Database\Query\Builder::select($columns); + } + + /** + * Add a new "raw" select expression to the query. + * + * @param string $expression + * @param array $bindings + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function selectRaw($expression, $bindings = []) + { + return \Illuminate\Database\Query\Builder::selectRaw($expression, $bindings); + } + + /** + * Add a subselect expression to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @return \Illuminate\Database\Query\Builder|static + * @throws \InvalidArgumentException + * @static + */ + public static function selectSub($query, $as) + { + return \Illuminate\Database\Query\Builder::selectSub($query, $as); + } + + /** + * Add a new select column to the query. + * + * @param array|mixed $column + * @return $this + * @static + */ + public static function addSelect($column) + { + return \Illuminate\Database\Query\Builder::addSelect($column); + } + + /** + * Force the query to only return distinct results. + * + * @return $this + * @static + */ + public static function distinct() + { + return \Illuminate\Database\Query\Builder::distinct(); + } + + /** + * Set the table which the query is targeting. + * + * @param string $table + * @return $this + * @static + */ + public static function from($table) + { + return \Illuminate\Database\Query\Builder::from($table); + } + + /** + * Add a join clause to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @param string $type + * @param bool $where + * @return $this + * @static + */ + public static function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) + { + return \Illuminate\Database\Query\Builder::join($table, $first, $operator, $second, $type, $where); + } + + /** + * Add a "join where" clause to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @param string $type + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function joinWhere($table, $first, $operator, $second, $type = 'inner') + { + return \Illuminate\Database\Query\Builder::joinWhere($table, $first, $operator, $second, $type); + } + + /** + * Add a left join to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function leftJoin($table, $first, $operator = null, $second = null) + { + return \Illuminate\Database\Query\Builder::leftJoin($table, $first, $operator, $second); + } + + /** + * Add a "join where" clause to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function leftJoinWhere($table, $first, $operator, $second) + { + return \Illuminate\Database\Query\Builder::leftJoinWhere($table, $first, $operator, $second); + } + + /** + * Add a right join to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function rightJoin($table, $first, $operator = null, $second = null) + { + return \Illuminate\Database\Query\Builder::rightJoin($table, $first, $operator, $second); + } + + /** + * Add a "right join where" clause to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function rightJoinWhere($table, $first, $operator, $second) + { + return \Illuminate\Database\Query\Builder::rightJoinWhere($table, $first, $operator, $second); + } + + /** + * Add a "cross join" clause to the query. + * + * @param string $table + * @param string $first + * @param string $operator + * @param string $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function crossJoin($table, $first = null, $operator = null, $second = null) + { + return \Illuminate\Database\Query\Builder::crossJoin($table, $first, $operator, $second); + } + + /** + * Merge an array of where clauses and bindings. + * + * @param array $wheres + * @param array $bindings + * @return void + * @static + */ + public static function mergeWheres($wheres, $bindings) + { + \Illuminate\Database\Query\Builder::mergeWheres($wheres, $bindings); + } + + /** + * Add a "where" clause comparing two columns to the query. + * + * @param string|array $first + * @param string|null $operator + * @param string|null $second + * @param string|null $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereColumn($first, $operator = null, $second = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereColumn($first, $operator, $second, $boolean); + } + + /** + * Add an "or where" clause comparing two columns to the query. + * + * @param string|array $first + * @param string|null $operator + * @param string|null $second + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereColumn($first, $operator = null, $second = null) + { + return \Illuminate\Database\Query\Builder::orWhereColumn($first, $operator, $second); + } + + /** + * Add a raw where clause to the query. + * + * @param string $sql + * @param mixed $bindings + * @param string $boolean + * @return $this + * @static + */ + public static function whereRaw($sql, $bindings = [], $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereRaw($sql, $bindings, $boolean); + } + + /** + * Add a raw or where clause to the query. + * + * @param string $sql + * @param array $bindings + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereRaw($sql, $bindings = []) + { + return \Illuminate\Database\Query\Builder::orWhereRaw($sql, $bindings); + } + + /** + * Add a "where in" clause to the query. + * + * @param string $column + * @param mixed $values + * @param string $boolean + * @param bool $not + * @return $this + * @static + */ + public static function whereIn($column, $values, $boolean = 'and', $not = false) + { + return \Illuminate\Database\Query\Builder::whereIn($column, $values, $boolean, $not); + } + + /** + * Add an "or where in" clause to the query. + * + * @param string $column + * @param mixed $values + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereIn($column, $values) + { + return \Illuminate\Database\Query\Builder::orWhereIn($column, $values); + } + + /** + * Add a "where not in" clause to the query. + * + * @param string $column + * @param mixed $values + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereNotIn($column, $values, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereNotIn($column, $values, $boolean); + } + + /** + * Add an "or where not in" clause to the query. + * + * @param string $column + * @param mixed $values + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereNotIn($column, $values) + { + return \Illuminate\Database\Query\Builder::orWhereNotIn($column, $values); + } + + /** + * Add a "where null" clause to the query. + * + * @param string $column + * @param string $boolean + * @param bool $not + * @return $this + * @static + */ + public static function whereNull($column, $boolean = 'and', $not = false) + { + return \Illuminate\Database\Query\Builder::whereNull($column, $boolean, $not); + } + + /** + * Add an "or where null" clause to the query. + * + * @param string $column + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereNull($column) + { + return \Illuminate\Database\Query\Builder::orWhereNull($column); + } + + /** + * Add a "where not null" clause to the query. + * + * @param string $column + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereNotNull($column, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereNotNull($column, $boolean); + } + + /** + * Add a where between statement to the query. + * + * @param string $column + * @param array $values + * @param string $boolean + * @param bool $not + * @return $this + * @static + */ + public static function whereBetween($column, $values, $boolean = 'and', $not = false) + { + return \Illuminate\Database\Query\Builder::whereBetween($column, $values, $boolean, $not); + } + + /** + * Add an or where between statement to the query. + * + * @param string $column + * @param array $values + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereBetween($column, $values) + { + return \Illuminate\Database\Query\Builder::orWhereBetween($column, $values); + } + + /** + * Add a where not between statement to the query. + * + * @param string $column + * @param array $values + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereNotBetween($column, $values, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereNotBetween($column, $values, $boolean); + } + + /** + * Add an or where not between statement to the query. + * + * @param string $column + * @param array $values + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereNotBetween($column, $values) + { + return \Illuminate\Database\Query\Builder::orWhereNotBetween($column, $values); + } + + /** + * Add an "or where not null" clause to the query. + * + * @param string $column + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereNotNull($column) + { + return \Illuminate\Database\Query\Builder::orWhereNotNull($column); + } + + /** + * Add a "where date" statement to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereDate($column, $operator, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereDate($column, $operator, $value, $boolean); + } + + /** + * Add an "or where date" statement to the query. + * + * @param string $column + * @param string $operator + * @param string $value + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereDate($column, $operator, $value) + { + return \Illuminate\Database\Query\Builder::orWhereDate($column, $operator, $value); + } + + /** + * Add a "where time" statement to the query. + * + * @param string $column + * @param string $operator + * @param int $value + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereTime($column, $operator, $value, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereTime($column, $operator, $value, $boolean); + } + + /** + * Add an "or where time" statement to the query. + * + * @param string $column + * @param string $operator + * @param int $value + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereTime($column, $operator, $value) + { + return \Illuminate\Database\Query\Builder::orWhereTime($column, $operator, $value); + } + + /** + * Add a "where day" statement to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereDay($column, $operator, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereDay($column, $operator, $value, $boolean); + } + + /** + * Add a "where month" statement to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereMonth($column, $operator, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereMonth($column, $operator, $value, $boolean); + } + + /** + * Add a "where year" statement to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereYear($column, $operator, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereYear($column, $operator, $value, $boolean); + } + + /** + * Add a nested where statement to the query. + * + * @param \Closure $callback + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereNested($callback, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereNested($callback, $boolean); + } + + /** + * Create a new query instance for nested where condition. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function forNestedWhere() + { + return \Illuminate\Database\Query\Builder::forNestedWhere(); + } + + /** + * Add another query builder as a nested where to the query builder. + * + * @param \Illuminate\Database\Query\Builder|static $query + * @param string $boolean + * @return $this + * @static + */ + public static function addNestedWhereQuery($query, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::addNestedWhereQuery($query, $boolean); + } + + /** + * Add an exists clause to the query. + * + * @param \Closure $callback + * @param string $boolean + * @param bool $not + * @return $this + * @static + */ + public static function whereExists($callback, $boolean = 'and', $not = false) + { + return \Illuminate\Database\Query\Builder::whereExists($callback, $boolean, $not); + } + + /** + * Add an or exists clause to the query. + * + * @param \Closure $callback + * @param bool $not + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereExists($callback, $not = false) + { + return \Illuminate\Database\Query\Builder::orWhereExists($callback, $not); + } + + /** + * Add a where not exists clause to the query. + * + * @param \Closure $callback + * @param string $boolean + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function whereNotExists($callback, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::whereNotExists($callback, $boolean); + } + + /** + * Add a where not exists clause to the query. + * + * @param \Closure $callback + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orWhereNotExists($callback) + { + return \Illuminate\Database\Query\Builder::orWhereNotExists($callback); + } + + /** + * Add an exists clause to the query. + * + * @param \Illuminate\Database\Query\Builder $query + * @param string $boolean + * @param bool $not + * @return $this + * @static + */ + public static function addWhereExistsQuery($query, $boolean = 'and', $not = false) + { + return \Illuminate\Database\Query\Builder::addWhereExistsQuery($query, $boolean, $not); + } + + /** + * Handles dynamic "where" clauses to the query. + * + * @param string $method + * @param string $parameters + * @return $this + * @static + */ + public static function dynamicWhere($method, $parameters) + { + return \Illuminate\Database\Query\Builder::dynamicWhere($method, $parameters); + } + + /** + * Add a "group by" clause to the query. + * + * @param array $groups + * @return $this + * @static + */ + public static function groupBy($groups = null) + { + return \Illuminate\Database\Query\Builder::groupBy($groups); + } + + /** + * Add a "having" clause to the query. + * + * @param string $column + * @param string $operator + * @param string $value + * @param string $boolean + * @return $this + * @static + */ + public static function having($column, $operator = null, $value = null, $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::having($column, $operator, $value, $boolean); + } + + /** + * Add a "or having" clause to the query. + * + * @param string $column + * @param string $operator + * @param string $value + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orHaving($column, $operator = null, $value = null) + { + return \Illuminate\Database\Query\Builder::orHaving($column, $operator, $value); + } + + /** + * Add a raw having clause to the query. + * + * @param string $sql + * @param array $bindings + * @param string $boolean + * @return $this + * @static + */ + public static function havingRaw($sql, $bindings = [], $boolean = 'and') + { + return \Illuminate\Database\Query\Builder::havingRaw($sql, $bindings, $boolean); + } + + /** + * Add a raw or having clause to the query. + * + * @param string $sql + * @param array $bindings + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function orHavingRaw($sql, $bindings = []) + { + return \Illuminate\Database\Query\Builder::orHavingRaw($sql, $bindings); + } + + /** + * Add an "order by" clause to the query. + * + * @param string $column + * @param string $direction + * @return $this + * @static + */ + public static function orderBy($column, $direction = 'asc') + { + return \Illuminate\Database\Query\Builder::orderBy($column, $direction); + } + + /** + * Add an "order by" clause for a timestamp to the query. + * + * @param string $column + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function latest($column = 'created_at') + { + return \Illuminate\Database\Query\Builder::latest($column); + } + + /** + * Add an "order by" clause for a timestamp to the query. + * + * @param string $column + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function oldest($column = 'created_at') + { + return \Illuminate\Database\Query\Builder::oldest($column); + } + + /** + * Put the query's results in random order. + * + * @param string $seed + * @return $this + * @static + */ + public static function inRandomOrder($seed = '') + { + return \Illuminate\Database\Query\Builder::inRandomOrder($seed); + } + + /** + * Add a raw "order by" clause to the query. + * + * @param string $sql + * @param array $bindings + * @return $this + * @static + */ + public static function orderByRaw($sql, $bindings = []) + { + return \Illuminate\Database\Query\Builder::orderByRaw($sql, $bindings); + } + + /** + * Alias to set the "offset" value of the query. + * + * @param int $value + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function skip($value) + { + return \Illuminate\Database\Query\Builder::skip($value); + } + + /** + * Set the "offset" value of the query. + * + * @param int $value + * @return $this + * @static + */ + public static function offset($value) + { + return \Illuminate\Database\Query\Builder::offset($value); + } + + /** + * Alias to set the "limit" value of the query. + * + * @param int $value + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function take($value) + { + return \Illuminate\Database\Query\Builder::take($value); + } + + /** + * Set the "limit" value of the query. + * + * @param int $value + * @return $this + * @static + */ + public static function limit($value) + { + return \Illuminate\Database\Query\Builder::limit($value); + } + + /** + * Set the limit and offset for a given page. + * + * @param int $page + * @param int $perPage + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function forPage($page, $perPage = 15) + { + return \Illuminate\Database\Query\Builder::forPage($page, $perPage); + } + + /** + * Constrain the query to the next "page" of results after a given ID. + * + * @param int $perPage + * @param int $lastId + * @param string $column + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') + { + return \Illuminate\Database\Query\Builder::forPageAfterId($perPage, $lastId, $column); + } + + /** + * Add a union statement to the query. + * + * @param \Illuminate\Database\Query\Builder|\Closure $query + * @param bool $all + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function union($query, $all = false) + { + return \Illuminate\Database\Query\Builder::union($query, $all); + } + + /** + * Add a union all statement to the query. + * + * @param \Illuminate\Database\Query\Builder|\Closure $query + * @return \Illuminate\Database\Query\Builder|static + * @static + */ + public static function unionAll($query) + { + return \Illuminate\Database\Query\Builder::unionAll($query); + } + + /** + * Lock the selected rows in the table. + * + * @param string|bool $value + * @return $this + * @static + */ + public static function lock($value = true) + { + return \Illuminate\Database\Query\Builder::lock($value); + } + + /** + * Lock the selected rows in the table for updating. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function lockForUpdate() + { + return \Illuminate\Database\Query\Builder::lockForUpdate(); + } + + /** + * Share lock the selected rows in the table. + * + * @return \Illuminate\Database\Query\Builder + * @static + */ + public static function sharedLock() + { + return \Illuminate\Database\Query\Builder::sharedLock(); + } + + /** + * Get the SQL representation of the query. + * + * @return string + * @static + */ + public static function toSql() + { + return \Illuminate\Database\Query\Builder::toSql(); + } + + /** + * Get the count of the total records for the paginator. + * + * @param array $columns + * @return int + * @static + */ + public static function getCountForPagination($columns = []) + { + return \Illuminate\Database\Query\Builder::getCountForPagination($columns); + } + + /** + * Concatenate values of a given column as a string. + * + * @param string $column + * @param string $glue + * @return string + * @static + */ + public static function implode($column, $glue = '') + { + return \Illuminate\Database\Query\Builder::implode($column, $glue); + } + + /** + * Determine if any rows exist for the current query. + * + * @return bool + * @static + */ + public static function exists() + { + return \Illuminate\Database\Query\Builder::exists(); + } + + /** + * Retrieve the "count" result of the query. + * + * @param string $columns + * @return int + * @static + */ + public static function count($columns = '*') + { + return \Illuminate\Database\Query\Builder::count($columns); + } + + /** + * Retrieve the minimum value of a given column. + * + * @param string $column + * @return mixed + * @static + */ + public static function min($column) + { + return \Illuminate\Database\Query\Builder::min($column); + } + + /** + * Retrieve the maximum value of a given column. + * + * @param string $column + * @return mixed + * @static + */ + public static function max($column) + { + return \Illuminate\Database\Query\Builder::max($column); + } + + /** + * Retrieve the sum of the values of a given column. + * + * @param string $column + * @return mixed + * @static + */ + public static function sum($column) + { + return \Illuminate\Database\Query\Builder::sum($column); + } + + /** + * Retrieve the average of the values of a given column. + * + * @param string $column + * @return mixed + * @static + */ + public static function avg($column) + { + return \Illuminate\Database\Query\Builder::avg($column); + } + + /** + * Alias for the "avg" method. + * + * @param string $column + * @return mixed + * @static + */ + public static function average($column) + { + return \Illuminate\Database\Query\Builder::average($column); + } + + /** + * Execute an aggregate function on the database. + * + * @param string $function + * @param array $columns + * @return mixed + * @static + */ + public static function aggregate($function, $columns = []) + { + return \Illuminate\Database\Query\Builder::aggregate($function, $columns); + } + + /** + * Execute a numeric aggregate function on the database. + * + * @param string $function + * @param array $columns + * @return float|int + * @static + */ + public static function numericAggregate($function, $columns = []) + { + return \Illuminate\Database\Query\Builder::numericAggregate($function, $columns); + } + + /** + * Insert a new record into the database. + * + * @param array $values + * @return bool + * @static + */ + public static function insert($values) + { + return \Illuminate\Database\Query\Builder::insert($values); + } + + /** + * Insert a new record and get the value of the primary key. + * + * @param array $values + * @param string $sequence + * @return int + * @static + */ + public static function insertGetId($values, $sequence = null) + { + return \Illuminate\Database\Query\Builder::insertGetId($values, $sequence); + } + + /** + * Insert or update a record matching the attributes, and fill it with values. + * + * @param array $attributes + * @param array $values + * @return bool + * @static + */ + public static function updateOrInsert($attributes, $values = []) + { + return \Illuminate\Database\Query\Builder::updateOrInsert($attributes, $values); + } + + /** + * Run a truncate statement on the table. + * + * @return void + * @static + */ + public static function truncate() + { + \Illuminate\Database\Query\Builder::truncate(); + } + + /** + * Create a raw database expression. + * + * @param mixed $value + * @return \Illuminate\Database\Query\Expression + * @static + */ + public static function raw($value) + { + return \Illuminate\Database\Query\Builder::raw($value); + } + + /** + * Get the current query value bindings in a flattened array. + * + * @return array + * @static + */ + public static function getBindings() + { + return \Illuminate\Database\Query\Builder::getBindings(); + } + + /** + * Get the raw array of bindings. + * + * @return array + * @static + */ + public static function getRawBindings() + { + return \Illuminate\Database\Query\Builder::getRawBindings(); + } + + /** + * Set the bindings on the query builder. + * + * @param array $bindings + * @param string $type + * @return $this + * @throws \InvalidArgumentException + * @static + */ + public static function setBindings($bindings, $type = 'where') + { + return \Illuminate\Database\Query\Builder::setBindings($bindings, $type); + } + + /** + * Add a binding to the query. + * + * @param mixed $value + * @param string $type + * @return $this + * @throws \InvalidArgumentException + * @static + */ + public static function addBinding($value, $type = 'where') + { + return \Illuminate\Database\Query\Builder::addBinding($value, $type); + } + + /** + * Merge an array of bindings into our bindings. + * + * @param \Illuminate\Database\Query\Builder $query + * @return $this + * @static + */ + public static function mergeBindings($query) + { + return \Illuminate\Database\Query\Builder::mergeBindings($query); + } + + /** + * Get the database query processor instance. + * + * @return \Illuminate\Database\Query\Processors\Processor + * @static + */ + public static function getProcessor() + { + return \Illuminate\Database\Query\Builder::getProcessor(); + } + + /** + * Get the query grammar instance. + * + * @return \Illuminate\Database\Query\Grammars\Grammar + * @static + */ + public static function getGrammar() + { + return \Illuminate\Database\Query\Builder::getGrammar(); + } + + /** + * Use the write pdo for query. + * + * @return $this + * @static + */ + public static function useWritePdo() + { + return \Illuminate\Database\Query\Builder::useWritePdo(); + } + + /** + * Clone the query without the given properties. + * + * @param array $except + * @return static + * @static + */ + public static function cloneWithout($except) + { + return \Illuminate\Database\Query\Builder::cloneWithout($except); + } + + /** + * Clone the query without the given bindings. + * + * @param array $except + * @return static + * @static + */ + public static function cloneWithoutBindings($except) + { + return \Illuminate\Database\Query\Builder::cloneWithoutBindings($except); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Database\Query\Builder::hasMacro($name); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method + * @param array $parameters + * @return mixed + * @throws \BadMethodCallException + * @static + */ + public static function macroCall($method, $parameters) + { + return \Illuminate\Database\Query\Builder::macroCall($method, $parameters); + } + } + + + class Event extends \Illuminate\Support\Facades\Event + { + /** + * Register an event listener with the dispatcher. + * + * @param string|array $events + * @param mixed $listener + * @return void + * @static + */ + public static function listen($events, $listener) + { + \Illuminate\Events\Dispatcher::listen($events, $listener); + } + + /** + * Determine if a given event has listeners. + * + * @param string $eventName + * @return bool + * @static + */ + public static function hasListeners($eventName) + { + return \Illuminate\Events\Dispatcher::hasListeners($eventName); + } + + /** + * Register an event and payload to be fired later. + * + * @param string $event + * @param array $payload + * @return void + * @static + */ + public static function push($event, $payload = []) + { + \Illuminate\Events\Dispatcher::push($event, $payload); + } + + /** + * Flush a set of pushed events. + * + * @param string $event + * @return void + * @static + */ + public static function flush($event) + { + \Illuminate\Events\Dispatcher::flush($event); + } + + /** + * Register an event subscriber with the dispatcher. + * + * @param object|string $subscriber + * @return void + * @static + */ + public static function subscribe($subscriber) + { + \Illuminate\Events\Dispatcher::subscribe($subscriber); + } + + /** + * Fire an event until the first non-null response is returned. + * + * @param string|object $event + * @param mixed $payload + * @return array|null + * @static + */ + public static function until($event, $payload = []) + { + return \Illuminate\Events\Dispatcher::until($event, $payload); + } + + /** + * Fire an event and call the listeners. + * + * @param string|object $event + * @param mixed $payload + * @param bool $halt + * @return array|null + * @static + */ + public static function fire($event, $payload = [], $halt = false) + { + return \Illuminate\Events\Dispatcher::fire($event, $payload, $halt); + } + + /** + * Fire an event and call the listeners. + * + * @param string|object $event + * @param mixed $payload + * @param bool $halt + * @return array|null + * @static + */ + public static function dispatch($event, $payload = [], $halt = false) + { + return \Illuminate\Events\Dispatcher::dispatch($event, $payload, $halt); + } + + /** + * Get all of the listeners for a given event name. + * + * @param string $eventName + * @return array + * @static + */ + public static function getListeners($eventName) + { + return \Illuminate\Events\Dispatcher::getListeners($eventName); + } + + /** + * Register an event listener with the dispatcher. + * + * @param string|\Closure $listener + * @param bool $wildcard + * @return mixed + * @static + */ + public static function makeListener($listener, $wildcard = false) + { + return \Illuminate\Events\Dispatcher::makeListener($listener, $wildcard); + } + + /** + * Create a class based listener using the IoC container. + * + * @param string $listener + * @param bool $wildcard + * @return \Closure + * @static + */ + public static function createClassListener($listener, $wildcard = false) + { + return \Illuminate\Events\Dispatcher::createClassListener($listener, $wildcard); + } + + /** + * Remove a set of listeners from the dispatcher. + * + * @param string $event + * @return void + * @static + */ + public static function forget($event) + { + \Illuminate\Events\Dispatcher::forget($event); + } + + /** + * Forget all of the pushed listeners. + * + * @return void + * @static + */ + public static function forgetPushed() + { + \Illuminate\Events\Dispatcher::forgetPushed(); + } + + /** + * Set the queue resolver implementation. + * + * @param callable $resolver + * @return $this + * @static + */ + public static function setQueueResolver($resolver) + { + return \Illuminate\Events\Dispatcher::setQueueResolver($resolver); + } + } + + + class File extends \Illuminate\Support\Facades\File + { + /** + * Determine if a file or directory exists. + * + * @param string $path + * @return bool + * @static + */ + public static function exists($path) + { + return \Illuminate\Filesystem\Filesystem::exists($path); + } + + /** + * Get the contents of a file. + * + * @param string $path + * @param bool $lock + * @return string + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @static + */ + public static function get($path, $lock = false) + { + return \Illuminate\Filesystem\Filesystem::get($path, $lock); + } + + /** + * Get contents of a file with shared access. + * + * @param string $path + * @return string + * @static + */ + public static function sharedGet($path) + { + return \Illuminate\Filesystem\Filesystem::sharedGet($path); + } + + /** + * Get the returned value of a file. + * + * @param string $path + * @return mixed + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @static + */ + public static function getRequire($path) + { + return \Illuminate\Filesystem\Filesystem::getRequire($path); + } + + /** + * Require the given file once. + * + * @param string $file + * @return mixed + * @static + */ + public static function requireOnce($file) + { + return \Illuminate\Filesystem\Filesystem::requireOnce($file); + } + + /** + * Write the contents of a file. + * + * @param string $path + * @param string $contents + * @param bool $lock + * @return int + * @static + */ + public static function put($path, $contents, $lock = false) + { + return \Illuminate\Filesystem\Filesystem::put($path, $contents, $lock); + } + + /** + * Prepend to a file. + * + * @param string $path + * @param string $data + * @return int + * @static + */ + public static function prepend($path, $data) + { + return \Illuminate\Filesystem\Filesystem::prepend($path, $data); + } + + /** + * Append to a file. + * + * @param string $path + * @param string $data + * @return int + * @static + */ + public static function append($path, $data) + { + return \Illuminate\Filesystem\Filesystem::append($path, $data); + } + + /** + * Get or set UNIX mode of a file or directory. + * + * @param string $path + * @param int $mode + * @return mixed + * @static + */ + public static function chmod($path, $mode = null) + { + return \Illuminate\Filesystem\Filesystem::chmod($path, $mode); + } + + /** + * Delete the file at a given path. + * + * @param string|array $paths + * @return bool + * @static + */ + public static function delete($paths) + { + return \Illuminate\Filesystem\Filesystem::delete($paths); + } + + /** + * Move a file to a new location. + * + * @param string $path + * @param string $target + * @return bool + * @static + */ + public static function move($path, $target) + { + return \Illuminate\Filesystem\Filesystem::move($path, $target); + } + + /** + * Copy a file to a new location. + * + * @param string $path + * @param string $target + * @return bool + * @static + */ + public static function copy($path, $target) + { + return \Illuminate\Filesystem\Filesystem::copy($path, $target); + } + + /** + * Create a hard link to the target file or directory. + * + * @param string $target + * @param string $link + * @return void + * @static + */ + public static function link($target, $link) + { + \Illuminate\Filesystem\Filesystem::link($target, $link); + } + + /** + * Extract the file name from a file path. + * + * @param string $path + * @return string + * @static + */ + public static function name($path) + { + return \Illuminate\Filesystem\Filesystem::name($path); + } + + /** + * Extract the trailing name component from a file path. + * + * @param string $path + * @return string + * @static + */ + public static function basename($path) + { + return \Illuminate\Filesystem\Filesystem::basename($path); + } + + /** + * Extract the parent directory from a file path. + * + * @param string $path + * @return string + * @static + */ + public static function dirname($path) + { + return \Illuminate\Filesystem\Filesystem::dirname($path); + } + + /** + * Extract the file extension from a file path. + * + * @param string $path + * @return string + * @static + */ + public static function extension($path) + { + return \Illuminate\Filesystem\Filesystem::extension($path); + } + + /** + * Get the file type of a given file. + * + * @param string $path + * @return string + * @static + */ + public static function type($path) + { + return \Illuminate\Filesystem\Filesystem::type($path); + } + + /** + * Get the mime-type of a given file. + * + * @param string $path + * @return string|false + * @static + */ + public static function mimeType($path) + { + return \Illuminate\Filesystem\Filesystem::mimeType($path); + } + + /** + * Get the file size of a given file. + * + * @param string $path + * @return int + * @static + */ + public static function size($path) + { + return \Illuminate\Filesystem\Filesystem::size($path); + } + + /** + * Get the file's last modification time. + * + * @param string $path + * @return int + * @static + */ + public static function lastModified($path) + { + return \Illuminate\Filesystem\Filesystem::lastModified($path); + } + + /** + * Determine if the given path is a directory. + * + * @param string $directory + * @return bool + * @static + */ + public static function isDirectory($directory) + { + return \Illuminate\Filesystem\Filesystem::isDirectory($directory); + } + + /** + * Determine if the given path is readable. + * + * @param string $path + * @return bool + * @static + */ + public static function isReadable($path) + { + return \Illuminate\Filesystem\Filesystem::isReadable($path); + } + + /** + * Determine if the given path is writable. + * + * @param string $path + * @return bool + * @static + */ + public static function isWritable($path) + { + return \Illuminate\Filesystem\Filesystem::isWritable($path); + } + + /** + * Determine if the given path is a file. + * + * @param string $file + * @return bool + * @static + */ + public static function isFile($file) + { + return \Illuminate\Filesystem\Filesystem::isFile($file); + } + + /** + * Find path names matching a given pattern. + * + * @param string $pattern + * @param int $flags + * @return array + * @static + */ + public static function glob($pattern, $flags = 0) + { + return \Illuminate\Filesystem\Filesystem::glob($pattern, $flags); + } + + /** + * Get an array of all files in a directory. + * + * @param string $directory + * @return array + * @static + */ + public static function files($directory) + { + return \Illuminate\Filesystem\Filesystem::files($directory); + } + + /** + * Get all of the files from the given directory (recursive). + * + * @param string $directory + * @param bool $hidden + * @return array + * @static + */ + public static function allFiles($directory, $hidden = false) + { + return \Illuminate\Filesystem\Filesystem::allFiles($directory, $hidden); + } + + /** + * Get all of the directories within a given directory. + * + * @param string $directory + * @return array + * @static + */ + public static function directories($directory) + { + return \Illuminate\Filesystem\Filesystem::directories($directory); + } + + /** + * Create a directory. + * + * @param string $path + * @param int $mode + * @param bool $recursive + * @param bool $force + * @return bool + * @static + */ + public static function makeDirectory($path, $mode = 493, $recursive = false, $force = false) + { + return \Illuminate\Filesystem\Filesystem::makeDirectory($path, $mode, $recursive, $force); + } + + /** + * Move a directory. + * + * @param string $from + * @param string $to + * @param bool $overwrite + * @return bool + * @static + */ + public static function moveDirectory($from, $to, $overwrite = false) + { + return \Illuminate\Filesystem\Filesystem::moveDirectory($from, $to, $overwrite); + } + + /** + * Copy a directory from one location to another. + * + * @param string $directory + * @param string $destination + * @param int $options + * @return bool + * @static + */ + public static function copyDirectory($directory, $destination, $options = null) + { + return \Illuminate\Filesystem\Filesystem::copyDirectory($directory, $destination, $options); + } + + /** + * Recursively delete a directory. + * + * The directory itself may be optionally preserved. + * + * @param string $directory + * @param bool $preserve + * @return bool + * @static + */ + public static function deleteDirectory($directory, $preserve = false) + { + return \Illuminate\Filesystem\Filesystem::deleteDirectory($directory, $preserve); + } + + /** + * Empty the specified directory of all files and folders. + * + * @param string $directory + * @return bool + * @static + */ + public static function cleanDirectory($directory) + { + return \Illuminate\Filesystem\Filesystem::cleanDirectory($directory); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Filesystem\Filesystem::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Filesystem\Filesystem::hasMacro($name); + } + } + + + class Gate extends \Illuminate\Support\Facades\Gate + { + /** + * Determine if a given ability has been defined. + * + * @param string $ability + * @return bool + * @static + */ + public static function has($ability) + { + return \Illuminate\Auth\Access\Gate::has($ability); + } + + /** + * Define a new ability. + * + * @param string $ability + * @param callable|string $callback + * @return $this + * @throws \InvalidArgumentException + * @static + */ + public static function define($ability, $callback) + { + return \Illuminate\Auth\Access\Gate::define($ability, $callback); + } + + /** + * Define a policy class for a given class type. + * + * @param string $class + * @param string $policy + * @return $this + * @static + */ + public static function policy($class, $policy) + { + return \Illuminate\Auth\Access\Gate::policy($class, $policy); + } + + /** + * Register a callback to run before all Gate checks. + * + * @param callable $callback + * @return $this + * @static + */ + public static function before($callback) + { + return \Illuminate\Auth\Access\Gate::before($callback); + } + + /** + * Register a callback to run after all Gate checks. + * + * @param callable $callback + * @return $this + * @static + */ + public static function after($callback) + { + return \Illuminate\Auth\Access\Gate::after($callback); + } + + /** + * Determine if the given ability should be granted for the current user. + * + * @param string $ability + * @param array|mixed $arguments + * @return bool + * @static + */ + public static function allows($ability, $arguments = []) + { + return \Illuminate\Auth\Access\Gate::allows($ability, $arguments); + } + + /** + * Determine if the given ability should be denied for the current user. + * + * @param string $ability + * @param array|mixed $arguments + * @return bool + * @static + */ + public static function denies($ability, $arguments = []) + { + return \Illuminate\Auth\Access\Gate::denies($ability, $arguments); + } + + /** + * Determine if the given ability should be granted for the current user. + * + * @param string $ability + * @param array|mixed $arguments + * @return bool + * @static + */ + public static function check($ability, $arguments = []) + { + return \Illuminate\Auth\Access\Gate::check($ability, $arguments); + } + + /** + * Determine if the given ability should be granted for the current user. + * + * @param string $ability + * @param array|mixed $arguments + * @return \Illuminate\Auth\Access\Response + * @throws \Illuminate\Auth\Access\AuthorizationException + * @static + */ + public static function authorize($ability, $arguments = []) + { + return \Illuminate\Auth\Access\Gate::authorize($ability, $arguments); + } + + /** + * Get a policy instance for a given class. + * + * @param object|string $class + * @return mixed + * @static + */ + public static function getPolicyFor($class) + { + return \Illuminate\Auth\Access\Gate::getPolicyFor($class); + } + + /** + * Build a policy class instance of the given type. + * + * @param object|string $class + * @return mixed + * @static + */ + public static function resolvePolicy($class) + { + return \Illuminate\Auth\Access\Gate::resolvePolicy($class); + } + + /** + * Get a gate instance for the given user. + * + * @param \Illuminate\Contracts\Auth\Authenticatable|mixed $user + * @return static + * @static + */ + public static function forUser($user) + { + return \Illuminate\Auth\Access\Gate::forUser($user); + } + } + + + class Hash extends \Illuminate\Support\Facades\Hash + { + /** + * Hash the given value. + * + * @param string $value + * @param array $options + * @return string + * @throws \RuntimeException + * @static + */ + public static function make($value, $options = []) + { + return \Illuminate\Hashing\BcryptHasher::make($value, $options); + } + + /** + * Check the given plain value against a hash. + * + * @param string $value + * @param string $hashedValue + * @param array $options + * @return bool + * @static + */ + public static function check($value, $hashedValue, $options = []) + { + return \Illuminate\Hashing\BcryptHasher::check($value, $hashedValue, $options); + } + + /** + * Check if the given hash has been hashed using the given options. + * + * @param string $hashedValue + * @param array $options + * @return bool + * @static + */ + public static function needsRehash($hashedValue, $options = []) + { + return \Illuminate\Hashing\BcryptHasher::needsRehash($hashedValue, $options); + } + + /** + * Set the default password work factor. + * + * @param int $rounds + * @return $this + * @static + */ + public static function setRounds($rounds) + { + return \Illuminate\Hashing\BcryptHasher::setRounds($rounds); + } + } + + + class Lang extends \Illuminate\Support\Facades\Lang + { + /** + * Determine if a translation exists for a given locale. + * + * @param string $key + * @param string|null $locale + * @return bool + * @static + */ + public static function hasForLocale($key, $locale = null) + { + return \Illuminate\Translation\Translator::hasForLocale($key, $locale); + } + + /** + * Determine if a translation exists. + * + * @param string $key + * @param string|null $locale + * @param bool $fallback + * @return bool + * @static + */ + public static function has($key, $locale = null, $fallback = true) + { + return \Illuminate\Translation\Translator::has($key, $locale, $fallback); + } + + /** + * Get the translation for a given key. + * + * @param string $key + * @param array $replace + * @param string $locale + * @return string|array|null + * @static + */ + public static function trans($key, $replace = [], $locale = null) + { + return \Illuminate\Translation\Translator::trans($key, $replace, $locale); + } + + /** + * Get the translation for the given key. + * + * @param string $key + * @param array $replace + * @param string|null $locale + * @param bool $fallback + * @return string|array|null + * @static + */ + public static function get($key, $replace = [], $locale = null, $fallback = true) + { + return \Illuminate\Translation\Translator::get($key, $replace, $locale, $fallback); + } + + /** + * Get the translation for a given key from the JSON translation files. + * + * @param string $key + * @param array $replace + * @param string $locale + * @return string + * @static + */ + public static function getFromJson($key, $replace = [], $locale = null) + { + return \Illuminate\Translation\Translator::getFromJson($key, $replace, $locale); + } + + /** + * Get a translation according to an integer value. + * + * @param string $key + * @param int|array|\Countable $number + * @param array $replace + * @param string $locale + * @return string + * @static + */ + public static function transChoice($key, $number, $replace = [], $locale = null) + { + return \Illuminate\Translation\Translator::transChoice($key, $number, $replace, $locale); + } + + /** + * Get a translation according to an integer value. + * + * @param string $key + * @param int|array|\Countable $number + * @param array $replace + * @param string $locale + * @return string + * @static + */ + public static function choice($key, $number, $replace = [], $locale = null) + { + return \Illuminate\Translation\Translator::choice($key, $number, $replace, $locale); + } + + /** + * Add translation lines to the given locale. + * + * @param array $lines + * @param string $locale + * @param string $namespace + * @return void + * @static + */ + public static function addLines($lines, $locale, $namespace = '*') + { + \Illuminate\Translation\Translator::addLines($lines, $locale, $namespace); + } + + /** + * Load the specified language group. + * + * @param string $namespace + * @param string $group + * @param string $locale + * @return void + * @static + */ + public static function load($namespace, $group, $locale) + { + \Illuminate\Translation\Translator::load($namespace, $group, $locale); + } + + /** + * Add a new namespace to the loader. + * + * @param string $namespace + * @param string $hint + * @return void + * @static + */ + public static function addNamespace($namespace, $hint) + { + \Illuminate\Translation\Translator::addNamespace($namespace, $hint); + } + + /** + * Parse a key into namespace, group, and item. + * + * @param string $key + * @return array + * @static + */ + public static function parseKey($key) + { + return \Illuminate\Translation\Translator::parseKey($key); + } + + /** + * Get the message selector instance. + * + * @return \Illuminate\Translation\MessageSelector + * @static + */ + public static function getSelector() + { + return \Illuminate\Translation\Translator::getSelector(); + } + + /** + * Set the message selector instance. + * + * @param \Illuminate\Translation\MessageSelector $selector + * @return void + * @static + */ + public static function setSelector($selector) + { + \Illuminate\Translation\Translator::setSelector($selector); + } + + /** + * Get the language line loader implementation. + * + * @return \Illuminate\Translation\LoaderInterface + * @static + */ + public static function getLoader() + { + return \Illuminate\Translation\Translator::getLoader(); + } + + /** + * Get the default locale being used. + * + * @return string + * @static + */ + public static function locale() + { + return \Illuminate\Translation\Translator::locale(); + } + + /** + * Get the default locale being used. + * + * @return string + * @static + */ + public static function getLocale() + { + return \Illuminate\Translation\Translator::getLocale(); + } + + /** + * Set the default locale. + * + * @param string $locale + * @return void + * @static + */ + public static function setLocale($locale) + { + \Illuminate\Translation\Translator::setLocale($locale); + } + + /** + * Get the fallback locale being used. + * + * @return string + * @static + */ + public static function getFallback() + { + return \Illuminate\Translation\Translator::getFallback(); + } + + /** + * Set the fallback locale being used. + * + * @param string $fallback + * @return void + * @static + */ + public static function setFallback($fallback) + { + \Illuminate\Translation\Translator::setFallback($fallback); + } + + /** + * Set the parsed value of a key. + * + * @param string $key + * @param array $parsed + * @return void + * @static + */ + public static function setParsedKey($key, $parsed) + { + //Method inherited from \Illuminate\Support\NamespacedItemResolver + \Illuminate\Translation\Translator::setParsedKey($key, $parsed); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Translation\Translator::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Translation\Translator::hasMacro($name); + } + } + + + class Log extends \Illuminate\Support\Facades\Log + { + /** + * Adds a log record at the DEBUG level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function debug($message, $context = []) + { + return \Monolog\Logger::debug($message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function info($message, $context = []) + { + return \Monolog\Logger::info($message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function notice($message, $context = []) + { + return \Monolog\Logger::notice($message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function warning($message, $context = []) + { + return \Monolog\Logger::warning($message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function error($message, $context = []) + { + return \Monolog\Logger::error($message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function critical($message, $context = []) + { + return \Monolog\Logger::critical($message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function alert($message, $context = []) + { + return \Monolog\Logger::alert($message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + * @static + */ + public static function emergency($message, $context = []) + { + return \Monolog\Logger::emergency($message, $context); + } + + /** + * Log a message to the logs. + * + * @param string $level + * @param string $message + * @param array $context + * @return void + * @static + */ + public static function log($level, $message, $context = []) + { + \Illuminate\Log\Writer::log($level, $message, $context); + } + + /** + * Dynamically pass log calls into the writer. + * + * @param string $level + * @param string $message + * @param array $context + * @return void + * @static + */ + public static function write($level, $message, $context = []) + { + \Illuminate\Log\Writer::write($level, $message, $context); + } + + /** + * Register a file log handler. + * + * @param string $path + * @param string $level + * @return void + * @static + */ + public static function useFiles($path, $level = 'debug') + { + \Illuminate\Log\Writer::useFiles($path, $level); + } + + /** + * Register a daily file log handler. + * + * @param string $path + * @param int $days + * @param string $level + * @return void + * @static + */ + public static function useDailyFiles($path, $days = 0, $level = 'debug') + { + \Illuminate\Log\Writer::useDailyFiles($path, $days, $level); + } + + /** + * Register a Syslog handler. + * + * @param string $name + * @param string $level + * @return \Psr\Log\LoggerInterface + * @static + */ + public static function useSyslog($name = 'laravel', $level = 'debug') + { + return \Illuminate\Log\Writer::useSyslog($name, $level); + } + + /** + * Register an error_log handler. + * + * @param string $level + * @param int $messageType + * @return void + * @static + */ + public static function useErrorLog($level = 'debug', $messageType = 0) + { + \Illuminate\Log\Writer::useErrorLog($level, $messageType); + } + + /** + * Register a new callback handler for when a log event is triggered. + * + * @param \Closure $callback + * @return void + * @throws \RuntimeException + * @static + */ + public static function listen($callback) + { + \Illuminate\Log\Writer::listen($callback); + } + + /** + * Get the underlying Monolog instance. + * + * @return \Monolog\Logger + * @static + */ + public static function getMonolog() + { + return \Illuminate\Log\Writer::getMonolog(); + } + + /** + * Get the event dispatcher instance. + * + * @return \Illuminate\Contracts\Events\Dispatcher + * @static + */ + public static function getEventDispatcher() + { + return \Illuminate\Log\Writer::getEventDispatcher(); + } + + /** + * Set the event dispatcher instance. + * + * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher + * @return void + * @static + */ + public static function setEventDispatcher($dispatcher) + { + \Illuminate\Log\Writer::setEventDispatcher($dispatcher); + } + } + + + class Mail extends \Illuminate\Support\Facades\Mail + { + /** + * Set the global from address and name. + * + * @param string $address + * @param string|null $name + * @return void + * @static + */ + public static function alwaysFrom($address, $name = null) + { + \Illuminate\Mail\Mailer::alwaysFrom($address, $name); + } + + /** + * Set the global reply-to address and name. + * + * @param string $address + * @param string|null $name + * @return void + * @static + */ + public static function alwaysReplyTo($address, $name = null) + { + \Illuminate\Mail\Mailer::alwaysReplyTo($address, $name); + } + + /** + * Set the global to address and name. + * + * @param string $address + * @param string|null $name + * @return void + * @static + */ + public static function alwaysTo($address, $name = null) + { + \Illuminate\Mail\Mailer::alwaysTo($address, $name); + } + + /** + * Begin the process of mailing a mailable class instance. + * + * @param mixed $users + * @return \Illuminate\Mail\PendingMail + * @static + */ + public static function to($users) + { + return \Illuminate\Mail\Mailer::to($users); + } + + /** + * Begin the process of mailing a mailable class instance. + * + * @param mixed $users + * @return \Illuminate\Mail\PendingMail + * @static + */ + public static function bcc($users) + { + return \Illuminate\Mail\Mailer::bcc($users); + } + + /** + * Send a new message when only a raw text part. + * + * @param string $text + * @param mixed $callback + * @return void + * @static + */ + public static function raw($text, $callback) + { + \Illuminate\Mail\Mailer::raw($text, $callback); + } + + /** + * Send a new message when only a plain part. + * + * @param string $view + * @param array $data + * @param mixed $callback + * @return void + * @static + */ + public static function plain($view, $data, $callback) + { + \Illuminate\Mail\Mailer::plain($view, $data, $callback); + } + + /** + * Send a new message using a view. + * + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @return void + * @static + */ + public static function send($view, $data = [], $callback = null) + { + \Illuminate\Mail\Mailer::send($view, $data, $callback); + } + + /** + * Queue a new e-mail message for sending. + * + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @param string|null $queue + * @return mixed + * @static + */ + public static function queue($view, $data = [], $callback = null, $queue = null) + { + return \Illuminate\Mail\Mailer::queue($view, $data, $callback, $queue); + } + + /** + * Queue a new e-mail message for sending on the given queue. + * + * @param string $queue + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @return mixed + * @static + */ + public static function onQueue($queue, $view, $data, $callback) + { + return \Illuminate\Mail\Mailer::onQueue($queue, $view, $data, $callback); + } + + /** + * Queue a new e-mail message for sending on the given queue. + * + * This method didn't match rest of framework's "onQueue" phrasing. Added "onQueue". + * + * @param string $queue + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @return mixed + * @static + */ + public static function queueOn($queue, $view, $data, $callback) + { + return \Illuminate\Mail\Mailer::queueOn($queue, $view, $data, $callback); + } + + /** + * Queue a new e-mail message for sending after (n) seconds. + * + * @param int $delay + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @param string|null $queue + * @return mixed + * @static + */ + public static function later($delay, $view, $data = [], $callback = null, $queue = null) + { + return \Illuminate\Mail\Mailer::later($delay, $view, $data, $callback, $queue); + } + + /** + * Queue a new e-mail message for sending after (n) seconds on the given queue. + * + * @param string $queue + * @param int $delay + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @return mixed + * @static + */ + public static function laterOn($queue, $delay, $view, $data, $callback) + { + return \Illuminate\Mail\Mailer::laterOn($queue, $delay, $view, $data, $callback); + } + + /** + * Get the view factory instance. + * + * @return \Illuminate\Contracts\View\Factory + * @static + */ + public static function getViewFactory() + { + return \Illuminate\Mail\Mailer::getViewFactory(); + } + + /** + * Get the Swift Mailer instance. + * + * @return \Swift_Mailer + * @static + */ + public static function getSwiftMailer() + { + return \Illuminate\Mail\Mailer::getSwiftMailer(); + } + + /** + * Get the array of failed recipients. + * + * @return array + * @static + */ + public static function failures() + { + return \Illuminate\Mail\Mailer::failures(); + } + + /** + * Set the Swift Mailer instance. + * + * @param \Swift_Mailer $swift + * @return void + * @static + */ + public static function setSwiftMailer($swift) + { + \Illuminate\Mail\Mailer::setSwiftMailer($swift); + } + + /** + * Set the queue manager instance. + * + * @param \Illuminate\Contracts\Queue\Factory $queue + * @return $this + * @static + */ + public static function setQueue($queue) + { + return \Illuminate\Mail\Mailer::setQueue($queue); + } + } + + + class Notification extends \Illuminate\Support\Facades\Notification + { + /** + * Send the given notification to the given notifiable entities. + * + * @param \Illuminate\Support\Collection|array|mixed $notifiables + * @param mixed $notification + * @return void + * @static + */ + public static function send($notifiables, $notification) + { + \Illuminate\Notifications\ChannelManager::send($notifiables, $notification); + } + + /** + * Send the given notification immediately. + * + * @param \Illuminate\Support\Collection|array|mixed $notifiables + * @param mixed $notification + * @param array|null $channels + * @return void + * @static + */ + public static function sendNow($notifiables, $notification, $channels = null) + { + \Illuminate\Notifications\ChannelManager::sendNow($notifiables, $notification, $channels); + } + + /** + * Get a channel instance. + * + * @param string|null $name + * @return mixed + * @static + */ + public static function channel($name = null) + { + return \Illuminate\Notifications\ChannelManager::channel($name); + } + + /** + * Get the default channel driver name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Notifications\ChannelManager::getDefaultDriver(); + } + + /** + * Get the default channel driver name. + * + * @return string + * @static + */ + public static function deliversVia() + { + return \Illuminate\Notifications\ChannelManager::deliversVia(); + } + + /** + * Set the default channel driver name. + * + * @param string $channel + * @return void + * @static + */ + public static function deliverVia($channel) + { + \Illuminate\Notifications\ChannelManager::deliverVia($channel); + } + + /** + * Get a driver instance. + * + * @param string $driver + * @return mixed + * @static + */ + public static function driver($driver = null) + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Notifications\ChannelManager::driver($driver); + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + * @static + */ + public static function extend($driver, $callback) + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Notifications\ChannelManager::extend($driver, $callback); + } + + /** + * Get all of the created "drivers". + * + * @return array + * @static + */ + public static function getDrivers() + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Notifications\ChannelManager::getDrivers(); + } + } + + + class Password extends \Illuminate\Support\Facades\Password + { + /** + * Attempt to get the broker from the local cache. + * + * @param string $name + * @return \Illuminate\Contracts\Auth\PasswordBroker + * @static + */ + public static function broker($name = null) + { + return \Illuminate\Auth\Passwords\PasswordBrokerManager::broker($name); + } + + /** + * Get the default password broker name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Auth\Passwords\PasswordBrokerManager::getDefaultDriver(); + } + + /** + * Set the default password broker name. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultDriver($name) + { + \Illuminate\Auth\Passwords\PasswordBrokerManager::setDefaultDriver($name); + } + } + + + class Queue extends \Illuminate\Support\Facades\Queue + { + /** + * Register an event listener for the before job event. + * + * @param mixed $callback + * @return void + * @static + */ + public static function before($callback) + { + \Illuminate\Queue\QueueManager::before($callback); + } + + /** + * Register an event listener for the after job event. + * + * @param mixed $callback + * @return void + * @static + */ + public static function after($callback) + { + \Illuminate\Queue\QueueManager::after($callback); + } + + /** + * Register an event listener for the exception occurred job event. + * + * @param mixed $callback + * @return void + * @static + */ + public static function exceptionOccurred($callback) + { + \Illuminate\Queue\QueueManager::exceptionOccurred($callback); + } + + /** + * Register an event listener for the daemon queue loop. + * + * @param mixed $callback + * @return void + * @static + */ + public static function looping($callback) + { + \Illuminate\Queue\QueueManager::looping($callback); + } + + /** + * Register an event listener for the failed job event. + * + * @param mixed $callback + * @return void + * @static + */ + public static function failing($callback) + { + \Illuminate\Queue\QueueManager::failing($callback); + } + + /** + * Register an event listener for the daemon queue stopping. + * + * @param mixed $callback + * @return void + * @static + */ + public static function stopping($callback) + { + \Illuminate\Queue\QueueManager::stopping($callback); + } + + /** + * Determine if the driver is connected. + * + * @param string $name + * @return bool + * @static + */ + public static function connected($name = null) + { + return \Illuminate\Queue\QueueManager::connected($name); + } + + /** + * Resolve a queue connection instance. + * + * @param string $name + * @return \Illuminate\Contracts\Queue\Queue + * @static + */ + public static function connection($name = null) + { + return \Illuminate\Queue\QueueManager::connection($name); + } + + /** + * Add a queue connection resolver. + * + * @param string $driver + * @param \Closure $resolver + * @return void + * @static + */ + public static function extend($driver, $resolver) + { + \Illuminate\Queue\QueueManager::extend($driver, $resolver); + } + + /** + * Add a queue connection resolver. + * + * @param string $driver + * @param \Closure $resolver + * @return void + * @static + */ + public static function addConnector($driver, $resolver) + { + \Illuminate\Queue\QueueManager::addConnector($driver, $resolver); + } + + /** + * Get the name of the default queue connection. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Queue\QueueManager::getDefaultDriver(); + } + + /** + * Set the name of the default queue connection. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultDriver($name) + { + \Illuminate\Queue\QueueManager::setDefaultDriver($name); + } + + /** + * Get the full name for the given connection. + * + * @param string $connection + * @return string + * @static + */ + public static function getName($connection = null) + { + return \Illuminate\Queue\QueueManager::getName($connection); + } + + /** + * Determine if the application is in maintenance mode. + * + * @return bool + * @static + */ + public static function isDownForMaintenance() + { + return \Illuminate\Queue\QueueManager::isDownForMaintenance(); + } + + /** + * Get the size of the queue. + * + * @param string $queue + * @return int + * @static + */ + public static function size($queue = null) + { + return \Illuminate\Queue\SyncQueue::size($queue); + } + + /** + * Push a new job onto the queue. + * + * @param string $job + * @param mixed $data + * @param string $queue + * @return mixed + * @throws \Exception|\Throwable + * @static + */ + public static function push($job, $data = '', $queue = null) + { + return \Illuminate\Queue\SyncQueue::push($job, $data, $queue); + } + + /** + * Push a raw payload onto the queue. + * + * @param string $payload + * @param string $queue + * @param array $options + * @return mixed + * @static + */ + public static function pushRaw($payload, $queue = null, $options = []) + { + return \Illuminate\Queue\SyncQueue::pushRaw($payload, $queue, $options); + } + + /** + * Push a new job onto the queue after a delay. + * + * @param \DateTime|int $delay + * @param string $job + * @param mixed $data + * @param string $queue + * @return mixed + * @static + */ + public static function later($delay, $job, $data = '', $queue = null) + { + return \Illuminate\Queue\SyncQueue::later($delay, $job, $data, $queue); + } + + /** + * Pop the next job off of the queue. + * + * @param string $queue + * @return \Illuminate\Contracts\Queue\Job|null + * @static + */ + public static function pop($queue = null) + { + return \Illuminate\Queue\SyncQueue::pop($queue); + } + + /** + * Push a new job onto the queue. + * + * @param string $queue + * @param string $job + * @param mixed $data + * @return mixed + * @static + */ + public static function pushOn($queue, $job, $data = '') + { + //Method inherited from \Illuminate\Queue\Queue + return \Illuminate\Queue\SyncQueue::pushOn($queue, $job, $data); + } + + /** + * Push a new job onto the queue after a delay. + * + * @param string $queue + * @param \DateTime|int $delay + * @param string $job + * @param mixed $data + * @return mixed + * @static + */ + public static function laterOn($queue, $delay, $job, $data = '') + { + //Method inherited from \Illuminate\Queue\Queue + return \Illuminate\Queue\SyncQueue::laterOn($queue, $delay, $job, $data); + } + + /** + * Push an array of jobs onto the queue. + * + * @param array $jobs + * @param mixed $data + * @param string $queue + * @return mixed + * @static + */ + public static function bulk($jobs, $data = '', $queue = null) + { + //Method inherited from \Illuminate\Queue\Queue + return \Illuminate\Queue\SyncQueue::bulk($jobs, $data, $queue); + } + + /** + * Get the connection name for the queue. + * + * @return string + * @static + */ + public static function getConnectionName() + { + //Method inherited from \Illuminate\Queue\Queue + return \Illuminate\Queue\SyncQueue::getConnectionName(); + } + + /** + * Set the connection name for the queue. + * + * @param string $name + * @return $this + * @static + */ + public static function setConnectionName($name) + { + //Method inherited from \Illuminate\Queue\Queue + return \Illuminate\Queue\SyncQueue::setConnectionName($name); + } + + /** + * Set the IoC container instance. + * + * @param \Illuminate\Container\Container $container + * @return void + * @static + */ + public static function setContainer($container) + { + //Method inherited from \Illuminate\Queue\Queue + \Illuminate\Queue\SyncQueue::setContainer($container); + } + } + + + class Redirect extends \Illuminate\Support\Facades\Redirect + { + /** + * Create a new redirect response to the "home" route. + * + * @param int $status + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function home($status = 302) + { + return \Illuminate\Routing\Redirector::home($status); + } + + /** + * Create a new redirect response to the previous location. + * + * @param int $status + * @param array $headers + * @param mixed $fallback + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function back($status = 302, $headers = [], $fallback = false) + { + return \Illuminate\Routing\Redirector::back($status, $headers, $fallback); + } + + /** + * Create a new redirect response to the current URI. + * + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function refresh($status = 302, $headers = []) + { + return \Illuminate\Routing\Redirector::refresh($status, $headers); + } + + /** + * Create a new redirect response, while putting the current URL in the session. + * + * @param string $path + * @param int $status + * @param array $headers + * @param bool $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function guest($path, $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\Redirector::guest($path, $status, $headers, $secure); + } + + /** + * Create a new redirect response to the previously intended location. + * + * @param string $default + * @param int $status + * @param array $headers + * @param bool $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function intended($default = '/', $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\Redirector::intended($default, $status, $headers, $secure); + } + + /** + * Create a new redirect response to the given path. + * + * @param string $path + * @param int $status + * @param array $headers + * @param bool $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function to($path, $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\Redirector::to($path, $status, $headers, $secure); + } + + /** + * Create a new redirect response to an external URL (no validation). + * + * @param string $path + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function away($path, $status = 302, $headers = []) + { + return \Illuminate\Routing\Redirector::away($path, $status, $headers); + } + + /** + * Create a new redirect response to the given HTTPS path. + * + * @param string $path + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function secure($path, $status = 302, $headers = []) + { + return \Illuminate\Routing\Redirector::secure($path, $status, $headers); + } + + /** + * Create a new redirect response to a named route. + * + * @param string $route + * @param array $parameters + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function route($route, $parameters = [], $status = 302, $headers = []) + { + return \Illuminate\Routing\Redirector::route($route, $parameters, $status, $headers); + } + + /** + * Create a new redirect response to a controller action. + * + * @param string $action + * @param array $parameters + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function action($action, $parameters = [], $status = 302, $headers = []) + { + return \Illuminate\Routing\Redirector::action($action, $parameters, $status, $headers); + } + + /** + * Get the URL generator instance. + * + * @return \Illuminate\Routing\UrlGenerator + * @static + */ + public static function getUrlGenerator() + { + return \Illuminate\Routing\Redirector::getUrlGenerator(); + } + + /** + * Set the active session store. + * + * @param \Illuminate\Session\Store $session + * @return void + * @static + */ + public static function setSession($session) + { + \Illuminate\Routing\Redirector::setSession($session); + } + } + + + class Request extends \Illuminate\Support\Facades\Request + { + /** + * Create a new Illuminate HTTP request from server variables. + * + * @return static + * @static + */ + public static function capture() + { + return \Illuminate\Http\Request::capture(); + } + + /** + * Return the Request instance. + * + * @return $this + * @static + */ + public static function instance() + { + return \Illuminate\Http\Request::instance(); + } + + /** + * Get the request method. + * + * @return string + * @static + */ + public static function method() + { + return \Illuminate\Http\Request::method(); + } + + /** + * Get the root URL for the application. + * + * @return string + * @static + */ + public static function root() + { + return \Illuminate\Http\Request::root(); + } + + /** + * Get the URL (no query string) for the request. + * + * @return string + * @static + */ + public static function url() + { + return \Illuminate\Http\Request::url(); + } + + /** + * Get the full URL for the request. + * + * @return string + * @static + */ + public static function fullUrl() + { + return \Illuminate\Http\Request::fullUrl(); + } + + /** + * Get the full URL for the request with the added query string parameters. + * + * @param array $query + * @return string + * @static + */ + public static function fullUrlWithQuery($query) + { + return \Illuminate\Http\Request::fullUrlWithQuery($query); + } + + /** + * Get the current path info for the request. + * + * @return string + * @static + */ + public static function path() + { + return \Illuminate\Http\Request::path(); + } + + /** + * Get the current encoded path info for the request. + * + * @return string + * @static + */ + public static function decodedPath() + { + return \Illuminate\Http\Request::decodedPath(); + } + + /** + * Get a segment from the URI (1 based index). + * + * @param int $index + * @param string|null $default + * @return string|null + * @static + */ + public static function segment($index, $default = null) + { + return \Illuminate\Http\Request::segment($index, $default); + } + + /** + * Get all of the segments for the request path. + * + * @return array + * @static + */ + public static function segments() + { + return \Illuminate\Http\Request::segments(); + } + + /** + * Determine if the current request URI matches a pattern. + * + * @return bool + * @static + */ + public static function is() + { + return \Illuminate\Http\Request::is(); + } + + /** + * Determine if the current request URL and query string matches a pattern. + * + * @return bool + * @static + */ + public static function fullUrlIs() + { + return \Illuminate\Http\Request::fullUrlIs(); + } + + /** + * Determine if the request is the result of an AJAX call. + * + * @return bool + * @static + */ + public static function ajax() + { + return \Illuminate\Http\Request::ajax(); + } + + /** + * Determine if the request is the result of an PJAX call. + * + * @return bool + * @static + */ + public static function pjax() + { + return \Illuminate\Http\Request::pjax(); + } + + /** + * Determine if the request is over HTTPS. + * + * @return bool + * @static + */ + public static function secure() + { + return \Illuminate\Http\Request::secure(); + } + + /** + * Returns the client IP address. + * + * @return string + * @static + */ + public static function ip() + { + return \Illuminate\Http\Request::ip(); + } + + /** + * Returns the client IP addresses. + * + * @return array + * @static + */ + public static function ips() + { + return \Illuminate\Http\Request::ips(); + } + + /** + * Merge new input into the current request's input array. + * + * @param array $input + * @return void + * @static + */ + public static function merge($input) + { + \Illuminate\Http\Request::merge($input); + } + + /** + * Replace the input for the current request. + * + * @param array $input + * @return void + * @static + */ + public static function replace($input) + { + \Illuminate\Http\Request::replace($input); + } + + /** + * Get the JSON payload for the request. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function json($key = null, $default = null) + { + return \Illuminate\Http\Request::json($key, $default); + } + + /** + * Create an Illuminate request from a Symfony instance. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Illuminate\Http\Request + * @static + */ + public static function createFromBase($request) + { + return \Illuminate\Http\Request::createFromBase($request); + } + + /** + * Clones a request and overrides some of its parameters. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @return static + * @static + */ + public static function duplicate($query = null, $request = null, $attributes = null, $cookies = null, $files = null, $server = null) + { + return \Illuminate\Http\Request::duplicate($query, $request, $attributes, $cookies, $files, $server); + } + + /** + * Get the session associated with the request. + * + * @return \Illuminate\Session\Store + * @throws \RuntimeException + * @static + */ + public static function session() + { + return \Illuminate\Http\Request::session(); + } + + /** + * Set the session instance on the request. + * + * @param \Illuminate\Contracts\Session\Session $session + * @return void + * @static + */ + public static function setLaravelSession($session) + { + \Illuminate\Http\Request::setLaravelSession($session); + } + + /** + * Get the user making the request. + * + * @param string|null $guard + * @return mixed + * @static + */ + public static function user($guard = null) + { + return \Illuminate\Http\Request::user($guard); + } + + /** + * Get the route handling the request. + * + * @param string|null $param + * @return \Illuminate\Routing\Route|object|string + * @static + */ + public static function route($param = null) + { + return \Illuminate\Http\Request::route($param); + } + + /** + * Get a unique fingerprint for the request / route / IP address. + * + * @return string + * @throws \RuntimeException + * @static + */ + public static function fingerprint() + { + return \Illuminate\Http\Request::fingerprint(); + } + + /** + * Get the user resolver callback. + * + * @return \Closure + * @static + */ + public static function getUserResolver() + { + return \Illuminate\Http\Request::getUserResolver(); + } + + /** + * Set the user resolver callback. + * + * @param \Closure $callback + * @return $this + * @static + */ + public static function setUserResolver($callback) + { + return \Illuminate\Http\Request::setUserResolver($callback); + } + + /** + * Get the route resolver callback. + * + * @return \Closure + * @static + */ + public static function getRouteResolver() + { + return \Illuminate\Http\Request::getRouteResolver(); + } + + /** + * Set the route resolver callback. + * + * @param \Closure $callback + * @return $this + * @static + */ + public static function setRouteResolver($callback) + { + return \Illuminate\Http\Request::setRouteResolver($callback); + } + + /** + * Get all of the input and files for the request. + * + * @return array + * @static + */ + public static function toArray() + { + return \Illuminate\Http\Request::toArray(); + } + + /** + * Determine if the given offset exists. + * + * @param string $offset + * @return bool + * @static + */ + public static function offsetExists($offset) + { + return \Illuminate\Http\Request::offsetExists($offset); + } + + /** + * Get the value at the given offset. + * + * @param string $offset + * @return mixed + * @static + */ + public static function offsetGet($offset) + { + return \Illuminate\Http\Request::offsetGet($offset); + } + + /** + * Set the value at the given offset. + * + * @param string $offset + * @param mixed $value + * @return void + * @static + */ + public static function offsetSet($offset, $value) + { + \Illuminate\Http\Request::offsetSet($offset, $value); + } + + /** + * Remove the value at the given offset. + * + * @param string $offset + * @return void + * @static + */ + public static function offsetUnset($offset) + { + \Illuminate\Http\Request::offsetUnset($offset); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string|resource $content The raw body data + * @static + */ + public static function initialize($query = [], $request = [], $attributes = [], $cookies = [], $files = [], $server = [], $content = null) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return static + * @static + */ + public static function createFromGlobals() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::createFromGlobals(); + } + + /** + * Creates a Request based on a given URI and configuration. + * + * The information contained in the URI always take precedence + * over the other information (server and parameters). + * + * @param string $uri The URI + * @param string $method The HTTP method + * @param array $parameters The query (GET) or request (POST) parameters + * @param array $cookies The request cookies ($_COOKIE) + * @param array $files The request files ($_FILES) + * @param array $server The server parameters ($_SERVER) + * @param string $content The raw body data + * @return static + * @static + */ + public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::create($uri, $method, $parameters, $cookies, $files, $server, $content); + } + + /** + * Sets a callable able to create a Request instance. + * + * This is mainly useful when you need to override the Request class + * to keep BC with an existing system. It should not be used for any + * other purpose. + * + * @param callable|null $callable A PHP callable + * @static + */ + public static function setFactory($callable) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setFactory($callable); + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. + * $_FILES is never overridden, see rfc1867 + * + * @static + */ + public static function overrideGlobals() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::overrideGlobals(); + } + + /** + * Sets a list of trusted proxies. + * + * You should only list the reverse proxies that you manage directly. + * + * @param array $proxies A list of trusted proxies + * @static + */ + public static function setTrustedProxies($proxies) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setTrustedProxies($proxies); + } + + /** + * Gets the list of trusted proxies. + * + * @return array An array of trusted proxies + * @static + */ + public static function getTrustedProxies() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getTrustedProxies(); + } + + /** + * Sets a list of trusted host patterns. + * + * You should only list the hosts you manage using regexs. + * + * @param array $hostPatterns A list of trusted host patterns + * @static + */ + public static function setTrustedHosts($hostPatterns) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setTrustedHosts($hostPatterns); + } + + /** + * Gets the list of trusted host patterns. + * + * @return array An array of trusted host patterns + * @static + */ + public static function getTrustedHosts() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getTrustedHosts(); + } + + /** + * Sets the name for trusted headers. + * + * The following header keys are supported: + * + * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) + * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost()) + * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort()) + * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) + * + * Setting an empty value allows to disable the trusted header for the given key. + * + * @param string $key The header key + * @param string $value The header name + * @throws \InvalidArgumentException + * @static + */ + public static function setTrustedHeaderName($key, $value) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setTrustedHeaderName($key, $value); + } + + /** + * Gets the trusted proxy header name. + * + * @param string $key The header key + * @return string The header name + * @throws \InvalidArgumentException + * @static + */ + public static function getTrustedHeaderName($key) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getTrustedHeaderName($key); + } + + /** + * Normalizes a query string. + * + * It builds a normalized query string, where keys/value pairs are alphabetized, + * have consistent escaping and unneeded delimiters are removed. + * + * @param string $qs Query string + * @return string A normalized query string for the Request + * @static + */ + public static function normalizeQueryString($qs) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::normalizeQueryString($qs); + } + + /** + * Enables support for the _method request parameter to determine the intended HTTP method. + * + * Be warned that enabling this feature might lead to CSRF issues in your code. + * Check that you are using CSRF tokens when required. + * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered + * and used to send a "PUT" or "DELETE" request via the _method request parameter. + * If these methods are not protected against CSRF, this presents a possible vulnerability. + * + * The HTTP method can only be overridden when the real HTTP method is POST. + * + * @static + */ + public static function enableHttpMethodParameterOverride() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::enableHttpMethodParameterOverride(); + } + + /** + * Checks whether support for the _method request parameter is enabled. + * + * @return bool True when the _method request parameter is enabled, false otherwise + * @static + */ + public static function getHttpMethodParameterOverride() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getHttpMethodParameterOverride(); + } + + /** + * Gets a "parameter" value from any bag. + * + * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the + * flexibility in controllers, it is better to explicitly get request parameters from the appropriate + * public property instead (attributes, query, request). + * + * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY + * + * @param string $key the key + * @param mixed $default the default value if the parameter key does not exist + * @return mixed + * @static + */ + public static function get($key, $default = null) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::get($key, $default); + } + + /** + * Gets the Session. + * + * @return \Symfony\Component\HttpFoundation\SessionInterface|null The session + * @static + */ + public static function getSession() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getSession(); + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return bool + * @static + */ + public static function hasPreviousSession() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::hasPreviousSession(); + } + + /** + * Whether the request contains a Session object. + * + * This method does not give any information about the state of the session object, + * like whether the session is started or not. It is just a way to check if this Request + * is associated with a Session instance. + * + * @return bool true when the Request contains a Session object, false otherwise + * @static + */ + public static function hasSession() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::hasSession(); + } + + /** + * Sets the Session. + * + * @param \Symfony\Component\HttpFoundation\SessionInterface $session The Session + * @static + */ + public static function setSession($session) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setSession($session); + } + + /** + * Returns the client IP addresses. + * + * In the returned array the most trusted IP address is first, and the + * least trusted one last. The "real" client IP address is the last one, + * but this is also the least trusted one. Trusted proxies are stripped. + * + * Use this method carefully; you should use getClientIp() instead. + * + * @return array The client IP addresses + * @see getClientIp() + * @static + */ + public static function getClientIps() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getClientIps(); + } + + /** + * Returns the client IP address. + * + * This method can read the client IP address from the "X-Forwarded-For" header + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" + * header value is a comma+space separated list of IP addresses, the left-most + * being the original client, and each successive proxy that passed the request + * adding the IP address where it received the request from. + * + * If your reverse proxy uses a different header name than "X-Forwarded-For", + * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with + * the "client-ip" key. + * + * @return string The client IP address + * @see getClientIps() + * @see http://en.wikipedia.org/wiki/X-Forwarded-For + * @static + */ + public static function getClientIp() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getClientIp(); + } + + /** + * Returns current script name. + * + * @return string + * @static + */ + public static function getScriptName() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getScriptName(); + } + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/enco%20ded returns '/enco%20ded' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string The raw path (i.e. not urldecoded) + * @static + */ + public static function getPathInfo() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getPathInfo(); + } + + /** + * Returns the root path from which this request is executed. + * + * Suppose that an index.php file instantiates this request object: + * + * * http://localhost/index.php returns an empty string + * * http://localhost/index.php/page returns an empty string + * * http://localhost/web/index.php returns '/web' + * * http://localhost/we%20b/index.php returns '/we%20b' + * + * @return string The raw path (i.e. not urldecoded) + * @static + */ + public static function getBasePath() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getBasePath(); + } + + /** + * Returns the root URL from which this request is executed. + * + * The base URL never ends with a /. + * + * This is similar to getBasePath(), except that it also includes the + * script filename (e.g. index.php) if one exists. + * + * @return string The raw URL (i.e. not urldecoded) + * @static + */ + public static function getBaseUrl() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getBaseUrl(); + } + + /** + * Gets the request's scheme. + * + * @return string + * @static + */ + public static function getScheme() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getScheme(); + } + + /** + * Returns the port on which the request is made. + * + * This method can read the client port from the "X-Forwarded-Port" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Port" header must contain the client port. + * + * If your reverse proxy uses a different header name than "X-Forwarded-Port", + * configure it via "setTrustedHeaderName()" with the "client-port" key. + * + * @return string + * @static + */ + public static function getPort() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getPort(); + } + + /** + * Returns the user. + * + * @return string|null + * @static + */ + public static function getUser() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getUser(); + } + + /** + * Returns the password. + * + * @return string|null + * @static + */ + public static function getPassword() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getPassword(); + } + + /** + * Gets the user info. + * + * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server + * @static + */ + public static function getUserInfo() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getUserInfo(); + } + + /** + * Returns the HTTP host being requested. + * + * The port name will be appended to the host if it's non-standard. + * + * @return string + * @static + */ + public static function getHttpHost() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getHttpHost(); + } + + /** + * Returns the requested URI (path and query string). + * + * @return string The raw URI (i.e. not URI decoded) + * @static + */ + public static function getRequestUri() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getRequestUri(); + } + + /** + * Gets the scheme and HTTP host. + * + * If the URL was called with basic authentication, the user + * and the password are not added to the generated string. + * + * @return string The scheme and HTTP host + * @static + */ + public static function getSchemeAndHttpHost() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getSchemeAndHttpHost(); + } + + /** + * Generates a normalized URI (URL) for the Request. + * + * @return string A normalized URI (URL) for the Request + * @see getQueryString() + * @static + */ + public static function getUri() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getUri(); + } + + /** + * Generates a normalized URI for the given path. + * + * @param string $path A path to use instead of the current one + * @return string The normalized URI for the path + * @static + */ + public static function getUriForPath($path) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getUriForPath($path); + } + + /** + * Returns the path as relative reference from the current Request path. + * + * Only the URIs path component (no schema, host etc.) is relevant and must be given. + * Both paths must be absolute and not contain relative parts. + * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. + * Furthermore, they can be used to reduce the link size in documents. + * + * Example target paths, given a base path of "/a/b/c/d": + * - "/a/b/c/d" -> "" + * - "/a/b/c/" -> "./" + * - "/a/b/" -> "../" + * - "/a/b/c/other" -> "other" + * - "/a/x/y" -> "../../x/y" + * + * @param string $path The target path + * @return string The relative target path + * @static + */ + public static function getRelativeUriForPath($path) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getRelativeUriForPath($path); + } + + /** + * Generates the normalized query string for the Request. + * + * It builds a normalized query string, where keys/value pairs are alphabetized + * and have consistent escaping. + * + * @return string|null A normalized query string for the Request + * @static + */ + public static function getQueryString() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getQueryString(); + } + + /** + * Checks whether the request is secure or not. + * + * This method can read the client protocol from the "X-Forwarded-Proto" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". + * + * If your reverse proxy uses a different header name than "X-Forwarded-Proto" + * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with + * the "client-proto" key. + * + * @return bool + * @static + */ + public static function isSecure() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isSecure(); + } + + /** + * Returns the host name. + * + * This method can read the client host name from the "X-Forwarded-Host" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Host" header must contain the client host name. + * + * If your reverse proxy uses a different header name than "X-Forwarded-Host", + * configure it via "setTrustedHeaderName()" with the "client-host" key. + * + * @return string + * @throws \UnexpectedValueException when the host name is invalid + * @static + */ + public static function getHost() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getHost(); + } + + /** + * Sets the request method. + * + * @param string $method + * @static + */ + public static function setMethod($method) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setMethod($method); + } + + /** + * Gets the request "intended" method. + * + * If the X-HTTP-Method-Override header is set, and if the method is a POST, + * then it is used to determine the "real" intended HTTP method. + * + * The _method request parameter can also be used to determine the HTTP method, + * but only if enableHttpMethodParameterOverride() has been called. + * + * The method is always an uppercased string. + * + * @return string The request method + * @see getRealMethod() + * @static + */ + public static function getMethod() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getMethod(); + } + + /** + * Gets the "real" request method. + * + * @return string The request method + * @see getMethod() + * @static + */ + public static function getRealMethod() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getRealMethod(); + } + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * @return string The associated mime type (null if not found) + * @static + */ + public static function getMimeType($format) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getMimeType($format); + } + + /** + * Gets the mime types associated with the format. + * + * @param string $format The format + * @return array The associated mime types + * @static + */ + public static function getMimeTypes($format) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getMimeTypes($format); + } + + /** + * Gets the format associated with the mime type. + * + * @param string $mimeType The associated mime type + * @return string|null The format (null if not found) + * @static + */ + public static function getFormat($mimeType) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getFormat($mimeType); + } + + /** + * Associates a format with mime types. + * + * @param string $format The format + * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) + * @static + */ + public static function setFormat($format, $mimeTypes) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setFormat($format, $mimeTypes); + } + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request attribute + * * $default + * + * @param string $default The default format + * @return string The request format + * @static + */ + public static function getRequestFormat($default = 'html') + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getRequestFormat($default); + } + + /** + * Sets the request format. + * + * @param string $format The request format + * @static + */ + public static function setRequestFormat($format) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setRequestFormat($format); + } + + /** + * Gets the format associated with the request. + * + * @return string|null The format (null if no content type is present) + * @static + */ + public static function getContentType() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getContentType(); + } + + /** + * Sets the default locale. + * + * @param string $locale + * @static + */ + public static function setDefaultLocale($locale) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setDefaultLocale($locale); + } + + /** + * Get the default locale. + * + * @return string + * @static + */ + public static function getDefaultLocale() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getDefaultLocale(); + } + + /** + * Sets the locale. + * + * @param string $locale + * @static + */ + public static function setLocale($locale) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::setLocale($locale); + } + + /** + * Get the locale. + * + * @return string + * @static + */ + public static function getLocale() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getLocale(); + } + + /** + * Checks if the request method is of specified type. + * + * @param string $method Uppercase request method (GET, POST etc) + * @return bool + * @static + */ + public static function isMethod($method) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isMethod($method); + } + + /** + * Checks whether or not the method is safe. + * + * @see https://tools.ietf.org/html/rfc7231#section-4.2.1 + * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default. + * @return bool + * @static + */ + public static function isMethodSafe() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isMethodSafe(); + } + + /** + * Checks whether or not the method is idempotent. + * + * @return bool + * @static + */ + public static function isMethodIdempotent() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isMethodIdempotent(); + } + + /** + * Checks whether the method is cacheable or not. + * + * @see https://tools.ietf.org/html/rfc7231#section-4.2.3 + * @return bool + * @static + */ + public static function isMethodCacheable() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isMethodCacheable(); + } + + /** + * Returns the request body content. + * + * @param bool $asResource If true, a resource will be returned + * @return string|resource The request body content or a resource to read the body stream + * @throws \LogicException + * @static + */ + public static function getContent($asResource = false) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getContent($asResource); + } + + /** + * Gets the Etags. + * + * @return array The entity tags + * @static + */ + public static function getETags() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getETags(); + } + + /** + * + * + * @return bool + * @static + */ + public static function isNoCache() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isNoCache(); + } + + /** + * Returns the preferred language. + * + * @param array $locales An array of ordered available locales + * @return string|null The preferred locale + * @static + */ + public static function getPreferredLanguage($locales = null) + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getPreferredLanguage($locales); + } + + /** + * Gets a list of languages acceptable by the client browser. + * + * @return array Languages ordered in the user browser preferences + * @static + */ + public static function getLanguages() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getLanguages(); + } + + /** + * Gets a list of charsets acceptable by the client browser. + * + * @return array List of charsets in preferable order + * @static + */ + public static function getCharsets() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getCharsets(); + } + + /** + * Gets a list of encodings acceptable by the client browser. + * + * @return array List of encodings in preferable order + * @static + */ + public static function getEncodings() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getEncodings(); + } + + /** + * Gets a list of content types acceptable by the client browser. + * + * @return array List of content types in preferable order + * @static + */ + public static function getAcceptableContentTypes() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::getAcceptableContentTypes(); + } + + /** + * Returns true if the request is a XMLHttpRequest. + * + * It works if your JavaScript library sets an X-Requested-With HTTP header. + * It is known to work with common JavaScript frameworks: + * + * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript + * @return bool true if the request is an XMLHttpRequest, false otherwise + * @static + */ + public static function isXmlHttpRequest() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isXmlHttpRequest(); + } + + /** + * Indicates whether this request originated from a trusted proxy. + * + * This can be useful to determine whether or not to trust the + * contents of a proxy-specific header. + * + * @return bool true if the request came from a trusted proxy, false otherwise + * @static + */ + public static function isFromTrustedProxy() + { + //Method inherited from \Symfony\Component\HttpFoundation\Request + return \Illuminate\Http\Request::isFromTrustedProxy(); + } + + /** + * Determine if the given content types match. + * + * @param string $actual + * @param string $type + * @return bool + * @static + */ + public static function matchesType($actual, $type) + { + return \Illuminate\Http\Request::matchesType($actual, $type); + } + + /** + * Determine if the request is sending JSON. + * + * @return bool + * @static + */ + public static function isJson() + { + return \Illuminate\Http\Request::isJson(); + } + + /** + * Determine if the current request probably expects a JSON response. + * + * @return bool + * @static + */ + public static function expectsJson() + { + return \Illuminate\Http\Request::expectsJson(); + } + + /** + * Determine if the current request is asking for JSON in return. + * + * @return bool + * @static + */ + public static function wantsJson() + { + return \Illuminate\Http\Request::wantsJson(); + } + + /** + * Determines whether the current requests accepts a given content type. + * + * @param string|array $contentTypes + * @return bool + * @static + */ + public static function accepts($contentTypes) + { + return \Illuminate\Http\Request::accepts($contentTypes); + } + + /** + * Return the most suitable content type from the given array based on content negotiation. + * + * @param string|array $contentTypes + * @return string|null + * @static + */ + public static function prefers($contentTypes) + { + return \Illuminate\Http\Request::prefers($contentTypes); + } + + /** + * Determines whether a request accepts JSON. + * + * @return bool + * @static + */ + public static function acceptsJson() + { + return \Illuminate\Http\Request::acceptsJson(); + } + + /** + * Determines whether a request accepts HTML. + * + * @return bool + * @static + */ + public static function acceptsHtml() + { + return \Illuminate\Http\Request::acceptsHtml(); + } + + /** + * Get the data format expected in the response. + * + * @param string $default + * @return string + * @static + */ + public static function format($default = 'html') + { + return \Illuminate\Http\Request::format($default); + } + + /** + * Retrieve an old input item. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function old($key = null, $default = null) + { + return \Illuminate\Http\Request::old($key, $default); + } + + /** + * Flash the input for the current request to the session. + * + * @return void + * @static + */ + public static function flash() + { + \Illuminate\Http\Request::flash(); + } + + /** + * Flash only some of the input to the session. + * + * @param array|mixed $keys + * @return void + * @static + */ + public static function flashOnly($keys) + { + \Illuminate\Http\Request::flashOnly($keys); + } + + /** + * Flash only some of the input to the session. + * + * @param array|mixed $keys + * @return void + * @static + */ + public static function flashExcept($keys) + { + \Illuminate\Http\Request::flashExcept($keys); + } + + /** + * Flush all of the old input from the session. + * + * @return void + * @static + */ + public static function flush() + { + \Illuminate\Http\Request::flush(); + } + + /** + * Retrieve a server variable from the request. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function server($key = null, $default = null) + { + return \Illuminate\Http\Request::server($key, $default); + } + + /** + * Determine if a header is set on the request. + * + * @param string $key + * @return bool + * @static + */ + public static function hasHeader($key) + { + return \Illuminate\Http\Request::hasHeader($key); + } + + /** + * Retrieve a header from the request. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function header($key = null, $default = null) + { + return \Illuminate\Http\Request::header($key, $default); + } + + /** + * Get the bearer token from the request headers. + * + * @return string|null + * @static + */ + public static function bearerToken() + { + return \Illuminate\Http\Request::bearerToken(); + } + + /** + * Determine if the request contains a given input item key. + * + * @param string|array $key + * @return bool + * @static + */ + public static function exists($key) + { + return \Illuminate\Http\Request::exists($key); + } + + /** + * Determine if the request contains a non-empty value for an input item. + * + * @param string|array $key + * @return bool + * @static + */ + public static function has($key) + { + return \Illuminate\Http\Request::has($key); + } + + /** + * Get all of the input and files for the request. + * + * @return array + * @static + */ + public static function all() + { + return \Illuminate\Http\Request::all(); + } + + /** + * Retrieve an input item from the request. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function input($key = null, $default = null) + { + return \Illuminate\Http\Request::input($key, $default); + } + + /** + * Get a subset containing the provided keys with values from the input data. + * + * @param array|mixed $keys + * @return array + * @static + */ + public static function only($keys) + { + return \Illuminate\Http\Request::only($keys); + } + + /** + * Get all of the input except for a specified array of items. + * + * @param array|mixed $keys + * @return array + * @static + */ + public static function except($keys) + { + return \Illuminate\Http\Request::except($keys); + } + + /** + * Intersect an array of items with the input data. + * + * @param array|mixed $keys + * @return array + * @static + */ + public static function intersect($keys) + { + return \Illuminate\Http\Request::intersect($keys); + } + + /** + * Retrieve a query string item from the request. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function query($key = null, $default = null) + { + return \Illuminate\Http\Request::query($key, $default); + } + + /** + * Determine if a cookie is set on the request. + * + * @param string $key + * @return bool + * @static + */ + public static function hasCookie($key) + { + return \Illuminate\Http\Request::hasCookie($key); + } + + /** + * Retrieve a cookie from the request. + * + * @param string $key + * @param string|array|null $default + * @return string|array + * @static + */ + public static function cookie($key = null, $default = null) + { + return \Illuminate\Http\Request::cookie($key, $default); + } + + /** + * Get an array of all of the files on the request. + * + * @return array + * @static + */ + public static function allFiles() + { + return \Illuminate\Http\Request::allFiles(); + } + + /** + * Determine if the uploaded data contains a file. + * + * @param string $key + * @return bool + * @static + */ + public static function hasFile($key) + { + return \Illuminate\Http\Request::hasFile($key); + } + + /** + * Retrieve a file from the request. + * + * @param string $key + * @param mixed $default + * @return \Illuminate\Http\UploadedFile|array|null + * @static + */ + public static function file($key = null, $default = null) + { + return \Illuminate\Http\Request::file($key, $default); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Http\Request::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Http\Request::hasMacro($name); + } + } + + + class Response extends \Illuminate\Support\Facades\Response + { + /** + * Return a new response from the application. + * + * @param string $content + * @param int $status + * @param array $headers + * @return \Illuminate\Http\Response + * @static + */ + public static function make($content = '', $status = 200, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::make($content, $status, $headers); + } + + /** + * Return a new view response from the application. + * + * @param string $view + * @param array $data + * @param int $status + * @param array $headers + * @return \Illuminate\Http\Response + * @static + */ + public static function view($view, $data = [], $status = 200, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::view($view, $data, $status, $headers); + } + + /** + * Return a new JSON response from the application. + * + * @param mixed $data + * @param int $status + * @param array $headers + * @param int $options + * @return \Illuminate\Http\JsonResponse + * @static + */ + public static function json($data = [], $status = 200, $headers = [], $options = 0) + { + return \Illuminate\Routing\ResponseFactory::json($data, $status, $headers, $options); + } + + /** + * Return a new JSONP response from the application. + * + * @param string $callback + * @param mixed $data + * @param int $status + * @param array $headers + * @param int $options + * @return \Illuminate\Http\JsonResponse + * @static + */ + public static function jsonp($callback, $data = [], $status = 200, $headers = [], $options = 0) + { + return \Illuminate\Routing\ResponseFactory::jsonp($callback, $data, $status, $headers, $options); + } + + /** + * Return a new streamed response from the application. + * + * @param \Closure $callback + * @param int $status + * @param array $headers + * @return \Symfony\Component\HttpFoundation\StreamedResponse + * @static + */ + public static function stream($callback, $status = 200, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::stream($callback, $status, $headers); + } + + /** + * Create a new file download response. + * + * @param \SplFileInfo|string $file + * @param string $name + * @param array $headers + * @param string|null $disposition + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + * @static + */ + public static function download($file, $name = null, $headers = [], $disposition = 'attachment') + { + return \Illuminate\Routing\ResponseFactory::download($file, $name, $headers, $disposition); + } + + /** + * Return the raw contents of a binary file. + * + * @param \SplFileInfo|string $file + * @param array $headers + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + * @static + */ + public static function file($file, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::file($file, $headers); + } + + /** + * Create a new redirect response to the given path. + * + * @param string $path + * @param int $status + * @param array $headers + * @param bool|null $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function redirectTo($path, $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\ResponseFactory::redirectTo($path, $status, $headers, $secure); + } + + /** + * Create a new redirect response to a named route. + * + * @param string $route + * @param array $parameters + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function redirectToRoute($route, $parameters = [], $status = 302, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::redirectToRoute($route, $parameters, $status, $headers); + } + + /** + * Create a new redirect response to a controller action. + * + * @param string $action + * @param array $parameters + * @param int $status + * @param array $headers + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function redirectToAction($action, $parameters = [], $status = 302, $headers = []) + { + return \Illuminate\Routing\ResponseFactory::redirectToAction($action, $parameters, $status, $headers); + } + + /** + * Create a new redirect response, while putting the current URL in the session. + * + * @param string $path + * @param int $status + * @param array $headers + * @param bool|null $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function redirectGuest($path, $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\ResponseFactory::redirectGuest($path, $status, $headers, $secure); + } + + /** + * Create a new redirect response to the previously intended location. + * + * @param string $default + * @param int $status + * @param array $headers + * @param bool|null $secure + * @return \Illuminate\Http\RedirectResponse + * @static + */ + public static function redirectToIntended($default = '/', $status = 302, $headers = [], $secure = null) + { + return \Illuminate\Routing\ResponseFactory::redirectToIntended($default, $status, $headers, $secure); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Routing\ResponseFactory::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Routing\ResponseFactory::hasMacro($name); + } + } + + + class Route extends \Illuminate\Support\Facades\Route + { + /** + * Register a new GET route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function get($uri, $action = null) + { + return \Illuminate\Routing\Router::get($uri, $action); + } + + /** + * Register a new POST route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function post($uri, $action = null) + { + return \Illuminate\Routing\Router::post($uri, $action); + } + + /** + * Register a new PUT route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function put($uri, $action = null) + { + return \Illuminate\Routing\Router::put($uri, $action); + } + + /** + * Register a new PATCH route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function patch($uri, $action = null) + { + return \Illuminate\Routing\Router::patch($uri, $action); + } + + /** + * Register a new DELETE route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function delete($uri, $action = null) + { + return \Illuminate\Routing\Router::delete($uri, $action); + } + + /** + * Register a new OPTIONS route with the router. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function options($uri, $action = null) + { + return \Illuminate\Routing\Router::options($uri, $action); + } + + /** + * Register a new route responding to all verbs. + * + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function any($uri, $action = null) + { + return \Illuminate\Routing\Router::any($uri, $action); + } + + /** + * Register a new route with the given verbs. + * + * @param array|string $methods + * @param string $uri + * @param \Closure|array|string|null $action + * @return \Illuminate\Routing\Route + * @static + */ + public static function match($methods, $uri, $action = null) + { + return \Illuminate\Routing\Router::match($methods, $uri, $action); + } + + /** + * Register an array of resource controllers. + * + * @param array $resources + * @return void + * @static + */ + public static function resources($resources) + { + \Illuminate\Routing\Router::resources($resources); + } + + /** + * Route a resource to a controller. + * + * @param string $name + * @param string $controller + * @param array $options + * @return void + * @static + */ + public static function resource($name, $controller, $options = []) + { + \Illuminate\Routing\Router::resource($name, $controller, $options); + } + + /** + * Create a route group with shared attributes. + * + * @param array $attributes + * @param \Closure|string $routes + * @return void + * @static + */ + public static function group($attributes, $routes) + { + \Illuminate\Routing\Router::group($attributes, $routes); + } + + /** + * Merge the given array with the last group stack. + * + * @param array $new + * @return array + * @static + */ + public static function mergeWithLastGroup($new) + { + return \Illuminate\Routing\Router::mergeWithLastGroup($new); + } + + /** + * Get the prefix from the last group on the stack. + * + * @return string + * @static + */ + public static function getLastGroupPrefix() + { + return \Illuminate\Routing\Router::getLastGroupPrefix(); + } + + /** + * Dispatch the request to the application. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + * @static + */ + public static function dispatch($request) + { + return \Illuminate\Routing\Router::dispatch($request); + } + + /** + * Dispatch the request to a route and return the response. + * + * @param \Illuminate\Http\Request $request + * @return mixed + * @static + */ + public static function dispatchToRoute($request) + { + return \Illuminate\Routing\Router::dispatchToRoute($request); + } + + /** + * Gather the middleware for the given route with resolved class names. + * + * @param \Illuminate\Routing\Route $route + * @return array + * @static + */ + public static function gatherRouteMiddleware($route) + { + return \Illuminate\Routing\Router::gatherRouteMiddleware($route); + } + + /** + * Create a response instance from the given value. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param mixed $response + * @return \Illuminate\Http\Response + * @static + */ + public static function prepareResponse($request, $response) + { + return \Illuminate\Routing\Router::prepareResponse($request, $response); + } + + /** + * Substitute the route bindings onto the route. + * + * @param \Illuminate\Routing\Route $route + * @return \Illuminate\Routing\Route + * @static + */ + public static function substituteBindings($route) + { + return \Illuminate\Routing\Router::substituteBindings($route); + } + + /** + * Substitute the implicit Eloquent model bindings for the route. + * + * @param \Illuminate\Routing\Route $route + * @return void + * @static + */ + public static function substituteImplicitBindings($route) + { + \Illuminate\Routing\Router::substituteImplicitBindings($route); + } + + /** + * Register a route matched event listener. + * + * @param string|callable $callback + * @return void + * @static + */ + public static function matched($callback) + { + \Illuminate\Routing\Router::matched($callback); + } + + /** + * Get all of the defined middleware short-hand names. + * + * @return array + * @static + */ + public static function getMiddleware() + { + return \Illuminate\Routing\Router::getMiddleware(); + } + + /** + * Register a short-hand name for a middleware. + * + * @param string $name + * @param string $class + * @return $this + * @static + */ + public static function aliasMiddleware($name, $class) + { + return \Illuminate\Routing\Router::aliasMiddleware($name, $class); + } + + /** + * Check if a middlewareGroup with the given name exists. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMiddlewareGroup($name) + { + return \Illuminate\Routing\Router::hasMiddlewareGroup($name); + } + + /** + * Get all of the defined middleware groups. + * + * @return array + * @static + */ + public static function getMiddlewareGroups() + { + return \Illuminate\Routing\Router::getMiddlewareGroups(); + } + + /** + * Register a group of middleware. + * + * @param string $name + * @param array $middleware + * @return $this + * @static + */ + public static function middlewareGroup($name, $middleware) + { + return \Illuminate\Routing\Router::middlewareGroup($name, $middleware); + } + + /** + * Add a middleware to the beginning of a middleware group. + * + * If the middleware is already in the group, it will not be added again. + * + * @param string $group + * @param string $middleware + * @return $this + * @static + */ + public static function prependMiddlewareToGroup($group, $middleware) + { + return \Illuminate\Routing\Router::prependMiddlewareToGroup($group, $middleware); + } + + /** + * Add a middleware to the end of a middleware group. + * + * If the middleware is already in the group, it will not be added again. + * + * @param string $group + * @param string $middleware + * @return $this + * @static + */ + public static function pushMiddlewareToGroup($group, $middleware) + { + return \Illuminate\Routing\Router::pushMiddlewareToGroup($group, $middleware); + } + + /** + * Add a new route parameter binder. + * + * @param string $key + * @param string|callable $binder + * @return void + * @static + */ + public static function bind($key, $binder) + { + \Illuminate\Routing\Router::bind($key, $binder); + } + + /** + * Register a model binder for a wildcard. + * + * @param string $key + * @param string $class + * @param \Closure|null $callback + * @return void + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @static + */ + public static function model($key, $class, $callback = null) + { + \Illuminate\Routing\Router::model($key, $class, $callback); + } + + /** + * Get the binding callback for a given binding. + * + * @param string $key + * @return \Closure|null + * @static + */ + public static function getBindingCallback($key) + { + return \Illuminate\Routing\Router::getBindingCallback($key); + } + + /** + * Get the global "where" patterns. + * + * @return array + * @static + */ + public static function getPatterns() + { + return \Illuminate\Routing\Router::getPatterns(); + } + + /** + * Set a global where pattern on all routes. + * + * @param string $key + * @param string $pattern + * @return void + * @static + */ + public static function pattern($key, $pattern) + { + \Illuminate\Routing\Router::pattern($key, $pattern); + } + + /** + * Set a group of global where patterns on all routes. + * + * @param array $patterns + * @return void + * @static + */ + public static function patterns($patterns) + { + \Illuminate\Routing\Router::patterns($patterns); + } + + /** + * Determine if the router currently has a group stack. + * + * @return bool + * @static + */ + public static function hasGroupStack() + { + return \Illuminate\Routing\Router::hasGroupStack(); + } + + /** + * Get the current group stack for the router. + * + * @return array + * @static + */ + public static function getGroupStack() + { + return \Illuminate\Routing\Router::getGroupStack(); + } + + /** + * Get a route parameter for the current route. + * + * @param string $key + * @param string $default + * @return mixed + * @static + */ + public static function input($key, $default = null) + { + return \Illuminate\Routing\Router::input($key, $default); + } + + /** + * Get the request currently being dispatched. + * + * @return \Illuminate\Http\Request + * @static + */ + public static function getCurrentRequest() + { + return \Illuminate\Routing\Router::getCurrentRequest(); + } + + /** + * Get the currently dispatched route instance. + * + * @return \Illuminate\Routing\Route + * @static + */ + public static function getCurrentRoute() + { + return \Illuminate\Routing\Router::getCurrentRoute(); + } + + /** + * Get the currently dispatched route instance. + * + * @return \Illuminate\Routing\Route + * @static + */ + public static function current() + { + return \Illuminate\Routing\Router::current(); + } + + /** + * Check if a route with the given name exists. + * + * @param string $name + * @return bool + * @static + */ + public static function has($name) + { + return \Illuminate\Routing\Router::has($name); + } + + /** + * Get the current route name. + * + * @return string|null + * @static + */ + public static function currentRouteName() + { + return \Illuminate\Routing\Router::currentRouteName(); + } + + /** + * Alias for the "currentRouteNamed" method. + * + * @return bool + * @static + */ + public static function is() + { + return \Illuminate\Routing\Router::is(); + } + + /** + * Determine if the current route matches a given name. + * + * @param string $name + * @return bool + * @static + */ + public static function currentRouteNamed($name) + { + return \Illuminate\Routing\Router::currentRouteNamed($name); + } + + /** + * Get the current route action. + * + * @return string|null + * @static + */ + public static function currentRouteAction() + { + return \Illuminate\Routing\Router::currentRouteAction(); + } + + /** + * Alias for the "currentRouteUses" method. + * + * @return bool + * @static + */ + public static function uses() + { + return \Illuminate\Routing\Router::uses(); + } + + /** + * Determine if the current route action matches a given action. + * + * @param string $action + * @return bool + * @static + */ + public static function currentRouteUses($action) + { + return \Illuminate\Routing\Router::currentRouteUses($action); + } + + /** + * Register the typical authentication routes for an application. + * + * @return void + * @static + */ + public static function auth() + { + \Illuminate\Routing\Router::auth(); + } + + /** + * Set the unmapped global resource parameters to singular. + * + * @param bool $singular + * @return void + * @static + */ + public static function singularResourceParameters($singular = true) + { + \Illuminate\Routing\Router::singularResourceParameters($singular); + } + + /** + * Set the global resource parameter mapping. + * + * @param array $parameters + * @return void + * @static + */ + public static function resourceParameters($parameters = []) + { + \Illuminate\Routing\Router::resourceParameters($parameters); + } + + /** + * Get or set the verbs used in the resource URIs. + * + * @param array $verbs + * @return array|null + * @static + */ + public static function resourceVerbs($verbs = []) + { + return \Illuminate\Routing\Router::resourceVerbs($verbs); + } + + /** + * Get the underlying route collection. + * + * @return \Illuminate\Routing\RouteCollection + * @static + */ + public static function getRoutes() + { + return \Illuminate\Routing\Router::getRoutes(); + } + + /** + * Set the route collection instance. + * + * @param \Illuminate\Routing\RouteCollection $routes + * @return void + * @static + */ + public static function setRoutes($routes) + { + \Illuminate\Routing\Router::setRoutes($routes); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Routing\Router::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Routing\Router::hasMacro($name); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method + * @param array $parameters + * @return mixed + * @throws \BadMethodCallException + * @static + */ + public static function macroCall($method, $parameters) + { + return \Illuminate\Routing\Router::macroCall($method, $parameters); + } + } + + + class Schema extends \Illuminate\Support\Facades\Schema + { + /** + * Determine if the given table exists. + * + * @param string $table + * @return bool + * @static + */ + public static function hasTable($table) + { + return \Illuminate\Database\Schema\MySqlBuilder::hasTable($table); + } + + /** + * Get the column listing for a given table. + * + * @param string $table + * @return array + * @static + */ + public static function getColumnListing($table) + { + return \Illuminate\Database\Schema\MySqlBuilder::getColumnListing($table); + } + + /** + * Set the default string length for migrations. + * + * @param int $length + * @return void + * @static + */ + public static function defaultStringLength($length) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::defaultStringLength($length); + } + + /** + * Determine if the given table has a given column. + * + * @param string $table + * @param string $column + * @return bool + * @static + */ + public static function hasColumn($table, $column) + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::hasColumn($table, $column); + } + + /** + * Determine if the given table has given columns. + * + * @param string $table + * @param array $columns + * @return bool + * @static + */ + public static function hasColumns($table, $columns) + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::hasColumns($table, $columns); + } + + /** + * Get the data type for the given column name. + * + * @param string $table + * @param string $column + * @return string + * @static + */ + public static function getColumnType($table, $column) + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::getColumnType($table, $column); + } + + /** + * Modify a table on the schema. + * + * @param string $table + * @param \Closure $callback + * @return void + * @static + */ + public static function table($table, $callback) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::table($table, $callback); + } + + /** + * Create a new table on the schema. + * + * @param string $table + * @param \Closure $callback + * @return void + * @static + */ + public static function create($table, $callback) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::create($table, $callback); + } + + /** + * Drop a table from the schema. + * + * @param string $table + * @return void + * @static + */ + public static function drop($table) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::drop($table); + } + + /** + * Drop a table from the schema if it exists. + * + * @param string $table + * @return void + * @static + */ + public static function dropIfExists($table) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::dropIfExists($table); + } + + /** + * Rename a table on the schema. + * + * @param string $from + * @param string $to + * @return void + * @static + */ + public static function rename($from, $to) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::rename($from, $to); + } + + /** + * Enable foreign key constraints. + * + * @return bool + * @static + */ + public static function enableForeignKeyConstraints() + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::enableForeignKeyConstraints(); + } + + /** + * Disable foreign key constraints. + * + * @return bool + * @static + */ + public static function disableForeignKeyConstraints() + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::disableForeignKeyConstraints(); + } + + /** + * Get the database connection instance. + * + * @return \Illuminate\Database\Connection + * @static + */ + public static function getConnection() + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::getConnection(); + } + + /** + * Set the database connection instance. + * + * @param \Illuminate\Database\Connection $connection + * @return $this + * @static + */ + public static function setConnection($connection) + { + //Method inherited from \Illuminate\Database\Schema\Builder + return \Illuminate\Database\Schema\MySqlBuilder::setConnection($connection); + } + + /** + * Set the Schema Blueprint resolver callback. + * + * @param \Closure $resolver + * @return void + * @static + */ + public static function blueprintResolver($resolver) + { + //Method inherited from \Illuminate\Database\Schema\Builder + \Illuminate\Database\Schema\MySqlBuilder::blueprintResolver($resolver); + } + } + + + class Session extends \Illuminate\Support\Facades\Session + { + /** + * Get the session configuration. + * + * @return array + * @static + */ + public static function getSessionConfig() + { + return \Illuminate\Session\SessionManager::getSessionConfig(); + } + + /** + * Get the default session driver name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Session\SessionManager::getDefaultDriver(); + } + + /** + * Set the default session driver name. + * + * @param string $name + * @return void + * @static + */ + public static function setDefaultDriver($name) + { + \Illuminate\Session\SessionManager::setDefaultDriver($name); + } + + /** + * Get a driver instance. + * + * @param string $driver + * @return mixed + * @static + */ + public static function driver($driver = null) + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Session\SessionManager::driver($driver); + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + * @static + */ + public static function extend($driver, $callback) + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Session\SessionManager::extend($driver, $callback); + } + + /** + * Get all of the created "drivers". + * + * @return array + * @static + */ + public static function getDrivers() + { + //Method inherited from \Illuminate\Support\Manager + return \Illuminate\Session\SessionManager::getDrivers(); + } + + /** + * Start the session, reading the data from a handler. + * + * @return bool + * @static + */ + public static function start() + { + return \Illuminate\Session\Store::start(); + } + + /** + * Save the session data to storage. + * + * @return bool + * @static + */ + public static function save() + { + return \Illuminate\Session\Store::save(); + } + + /** + * Age the flash data for the session. + * + * @return void + * @static + */ + public static function ageFlashData() + { + \Illuminate\Session\Store::ageFlashData(); + } + + /** + * Get all of the session data. + * + * @return array + * @static + */ + public static function all() + { + return \Illuminate\Session\Store::all(); + } + + /** + * Checks if a key exists. + * + * @param string|array $key + * @return bool + * @static + */ + public static function exists($key) + { + return \Illuminate\Session\Store::exists($key); + } + + /** + * Checks if an a key is present and not null. + * + * @param string|array $key + * @return bool + * @static + */ + public static function has($key) + { + return \Illuminate\Session\Store::has($key); + } + + /** + * Get an item from the session. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function get($key, $default = null) + { + return \Illuminate\Session\Store::get($key, $default); + } + + /** + * Get the value of a given key and then forget it. + * + * @param string $key + * @param string $default + * @return mixed + * @static + */ + public static function pull($key, $default = null) + { + return \Illuminate\Session\Store::pull($key, $default); + } + + /** + * Determine if the session contains old input. + * + * @param string $key + * @return bool + * @static + */ + public static function hasOldInput($key = null) + { + return \Illuminate\Session\Store::hasOldInput($key); + } + + /** + * Get the requested item from the flashed input array. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function getOldInput($key = null, $default = null) + { + return \Illuminate\Session\Store::getOldInput($key, $default); + } + + /** + * Replace the given session attributes entirely. + * + * @param array $attributes + * @return void + * @static + */ + public static function replace($attributes) + { + \Illuminate\Session\Store::replace($attributes); + } + + /** + * Put a key / value pair or array of key / value pairs in the session. + * + * @param string|array $key + * @param mixed $value + * @return void + * @static + */ + public static function put($key, $value = null) + { + \Illuminate\Session\Store::put($key, $value); + } + + /** + * Get an item from the session, or store the default value. + * + * @param string $key + * @param \Closure $callback + * @return mixed + * @static + */ + public static function remember($key, $callback) + { + return \Illuminate\Session\Store::remember($key, $callback); + } + + /** + * Push a value onto a session array. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function push($key, $value) + { + \Illuminate\Session\Store::push($key, $value); + } + + /** + * Increment the value of an item in the session. + * + * @param string $key + * @param int $amount + * @return mixed + * @static + */ + public static function increment($key, $amount = 1) + { + return \Illuminate\Session\Store::increment($key, $amount); + } + + /** + * Decrement the value of an item in the session. + * + * @param string $key + * @param int $amount + * @return int + * @static + */ + public static function decrement($key, $amount = 1) + { + return \Illuminate\Session\Store::decrement($key, $amount); + } + + /** + * Flash a key / value pair to the session. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function flash($key, $value) + { + \Illuminate\Session\Store::flash($key, $value); + } + + /** + * Flash a key / value pair to the session for immediate use. + * + * @param string $key + * @param mixed $value + * @return void + * @static + */ + public static function now($key, $value) + { + \Illuminate\Session\Store::now($key, $value); + } + + /** + * Reflash all of the session flash data. + * + * @return void + * @static + */ + public static function reflash() + { + \Illuminate\Session\Store::reflash(); + } + + /** + * Reflash a subset of the current flash data. + * + * @param array|mixed $keys + * @return void + * @static + */ + public static function keep($keys = null) + { + \Illuminate\Session\Store::keep($keys); + } + + /** + * Flash an input array to the session. + * + * @param array $value + * @return void + * @static + */ + public static function flashInput($value) + { + \Illuminate\Session\Store::flashInput($value); + } + + /** + * Remove an item from the session, returning its value. + * + * @param string $key + * @return mixed + * @static + */ + public static function remove($key) + { + return \Illuminate\Session\Store::remove($key); + } + + /** + * Remove one or many items from the session. + * + * @param string|array $keys + * @return void + * @static + */ + public static function forget($keys) + { + \Illuminate\Session\Store::forget($keys); + } + + /** + * Remove all of the items from the session. + * + * @return void + * @static + */ + public static function flush() + { + \Illuminate\Session\Store::flush(); + } + + /** + * Flush the session data and regenerate the ID. + * + * @return bool + * @static + */ + public static function invalidate() + { + return \Illuminate\Session\Store::invalidate(); + } + + /** + * Generate a new session identifier. + * + * @param bool $destroy + * @return bool + * @static + */ + public static function regenerate($destroy = false) + { + return \Illuminate\Session\Store::regenerate($destroy); + } + + /** + * Generate a new session ID for the session. + * + * @param bool $destroy + * @return bool + * @static + */ + public static function migrate($destroy = false) + { + return \Illuminate\Session\Store::migrate($destroy); + } + + /** + * Determine if the session has been started. + * + * @return bool + * @static + */ + public static function isStarted() + { + return \Illuminate\Session\Store::isStarted(); + } + + /** + * Get the name of the session. + * + * @return string + * @static + */ + public static function getName() + { + return \Illuminate\Session\Store::getName(); + } + + /** + * Set the name of the session. + * + * @param string $name + * @return void + * @static + */ + public static function setName($name) + { + \Illuminate\Session\Store::setName($name); + } + + /** + * Get the current session ID. + * + * @return string + * @static + */ + public static function getId() + { + return \Illuminate\Session\Store::getId(); + } + + /** + * Set the session ID. + * + * @param string $id + * @return void + * @static + */ + public static function setId($id) + { + \Illuminate\Session\Store::setId($id); + } + + /** + * Determine if this is a valid session ID. + * + * @param string $id + * @return bool + * @static + */ + public static function isValidId($id) + { + return \Illuminate\Session\Store::isValidId($id); + } + + /** + * Set the existence of the session on the handler if applicable. + * + * @param bool $value + * @return void + * @static + */ + public static function setExists($value) + { + \Illuminate\Session\Store::setExists($value); + } + + /** + * Get the CSRF token value. + * + * @return string + * @static + */ + public static function token() + { + return \Illuminate\Session\Store::token(); + } + + /** + * Regenerate the CSRF token value. + * + * @return void + * @static + */ + public static function regenerateToken() + { + \Illuminate\Session\Store::regenerateToken(); + } + + /** + * Get the previous URL from the session. + * + * @return string|null + * @static + */ + public static function previousUrl() + { + return \Illuminate\Session\Store::previousUrl(); + } + + /** + * Set the "previous" URL in the session. + * + * @param string $url + * @return void + * @static + */ + public static function setPreviousUrl($url) + { + \Illuminate\Session\Store::setPreviousUrl($url); + } + + /** + * Get the underlying session handler implementation. + * + * @return \SessionHandlerInterface + * @static + */ + public static function getHandler() + { + return \Illuminate\Session\Store::getHandler(); + } + + /** + * Determine if the session handler needs a request. + * + * @return bool + * @static + */ + public static function handlerNeedsRequest() + { + return \Illuminate\Session\Store::handlerNeedsRequest(); + } + + /** + * Set the request on the handler instance. + * + * @param \Illuminate\Http\Request $request + * @return void + * @static + */ + public static function setRequestOnHandler($request) + { + \Illuminate\Session\Store::setRequestOnHandler($request); + } + } + + + class Storage extends \Illuminate\Support\Facades\Storage + { + /** + * Get a filesystem instance. + * + * @param string $name + * @return \Illuminate\Filesystem\FilesystemAdapter + * @static + */ + public static function drive($name = null) + { + return \Illuminate\Filesystem\FilesystemManager::drive($name); + } + + /** + * Get a filesystem instance. + * + * @param string $name + * @return \Illuminate\Filesystem\FilesystemAdapter + * @static + */ + public static function disk($name = null) + { + return \Illuminate\Filesystem\FilesystemManager::disk($name); + } + + /** + * Get a default cloud filesystem instance. + * + * @return \Illuminate\Filesystem\FilesystemAdapter + * @static + */ + public static function cloud() + { + return \Illuminate\Filesystem\FilesystemManager::cloud(); + } + + /** + * Create an instance of the local driver. + * + * @param array $config + * @return \Illuminate\Filesystem\FilesystemAdapter + * @static + */ + public static function createLocalDriver($config) + { + return \Illuminate\Filesystem\FilesystemManager::createLocalDriver($config); + } + + /** + * Create an instance of the ftp driver. + * + * @param array $config + * @return \Illuminate\Filesystem\FilesystemAdapter + * @static + */ + public static function createFtpDriver($config) + { + return \Illuminate\Filesystem\FilesystemManager::createFtpDriver($config); + } + + /** + * Create an instance of the Amazon S3 driver. + * + * @param array $config + * @return \Illuminate\Contracts\Filesystem\Cloud + * @static + */ + public static function createS3Driver($config) + { + return \Illuminate\Filesystem\FilesystemManager::createS3Driver($config); + } + + /** + * Create an instance of the Rackspace driver. + * + * @param array $config + * @return \Illuminate\Contracts\Filesystem\Cloud + * @static + */ + public static function createRackspaceDriver($config) + { + return \Illuminate\Filesystem\FilesystemManager::createRackspaceDriver($config); + } + + /** + * Get the default driver name. + * + * @return string + * @static + */ + public static function getDefaultDriver() + { + return \Illuminate\Filesystem\FilesystemManager::getDefaultDriver(); + } + + /** + * Get the default cloud driver name. + * + * @return string + * @static + */ + public static function getDefaultCloudDriver() + { + return \Illuminate\Filesystem\FilesystemManager::getDefaultCloudDriver(); + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + * @static + */ + public static function extend($driver, $callback) + { + return \Illuminate\Filesystem\FilesystemManager::extend($driver, $callback); + } + + /** + * Determine if a file exists. + * + * @param string $path + * @return bool + * @static + */ + public static function exists($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::exists($path); + } + + /** + * Get the contents of a file. + * + * @param string $path + * @return string + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @static + */ + public static function get($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::get($path); + } + + /** + * Write the contents of a file. + * + * @param string $path + * @param string|resource $contents + * @param array $options + * @return bool + * @static + */ + public static function put($path, $contents, $options = []) + { + return \Illuminate\Filesystem\FilesystemAdapter::put($path, $contents, $options); + } + + /** + * Store the uploaded file on the disk. + * + * @param string $path + * @param \Illuminate\Http\UploadedFile $file + * @param array $options + * @return string|false + * @static + */ + public static function putFile($path, $file, $options = []) + { + return \Illuminate\Filesystem\FilesystemAdapter::putFile($path, $file, $options); + } + + /** + * Store the uploaded file on the disk with a given name. + * + * @param string $path + * @param \Illuminate\Http\File|\Illuminate\Http\UploadedFile $file + * @param string $name + * @param array $options + * @return string|false + * @static + */ + public static function putFileAs($path, $file, $name, $options = []) + { + return \Illuminate\Filesystem\FilesystemAdapter::putFileAs($path, $file, $name, $options); + } + + /** + * Get the visibility for the given path. + * + * @param string $path + * @return string + * @static + */ + public static function getVisibility($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::getVisibility($path); + } + + /** + * Set the visibility for the given path. + * + * @param string $path + * @param string $visibility + * @return void + * @static + */ + public static function setVisibility($path, $visibility) + { + \Illuminate\Filesystem\FilesystemAdapter::setVisibility($path, $visibility); + } + + /** + * Prepend to a file. + * + * @param string $path + * @param string $data + * @param string $separator + * @return int + * @static + */ + public static function prepend($path, $data, $separator = '') + { + return \Illuminate\Filesystem\FilesystemAdapter::prepend($path, $data, $separator); + } + + /** + * Append to a file. + * + * @param string $path + * @param string $data + * @param string $separator + * @return int + * @static + */ + public static function append($path, $data, $separator = '') + { + return \Illuminate\Filesystem\FilesystemAdapter::append($path, $data, $separator); + } + + /** + * Delete the file at a given path. + * + * @param string|array $paths + * @return bool + * @static + */ + public static function delete($paths) + { + return \Illuminate\Filesystem\FilesystemAdapter::delete($paths); + } + + /** + * Copy a file to a new location. + * + * @param string $from + * @param string $to + * @return bool + * @static + */ + public static function copy($from, $to) + { + return \Illuminate\Filesystem\FilesystemAdapter::copy($from, $to); + } + + /** + * Move a file to a new location. + * + * @param string $from + * @param string $to + * @return bool + * @static + */ + public static function move($from, $to) + { + return \Illuminate\Filesystem\FilesystemAdapter::move($from, $to); + } + + /** + * Get the file size of a given file. + * + * @param string $path + * @return int + * @static + */ + public static function size($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::size($path); + } + + /** + * Get the mime-type of a given file. + * + * @param string $path + * @return string|false + * @static + */ + public static function mimeType($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::mimeType($path); + } + + /** + * Get the file's last modification time. + * + * @param string $path + * @return int + * @static + */ + public static function lastModified($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::lastModified($path); + } + + /** + * Get the URL for the file at the given path. + * + * @param string $path + * @return string + * @static + */ + public static function url($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::url($path); + } + + /** + * Get an array of all files in a directory. + * + * @param string|null $directory + * @param bool $recursive + * @return array + * @static + */ + public static function files($directory = null, $recursive = false) + { + return \Illuminate\Filesystem\FilesystemAdapter::files($directory, $recursive); + } + + /** + * Get all of the files from the given directory (recursive). + * + * @param string|null $directory + * @return array + * @static + */ + public static function allFiles($directory = null) + { + return \Illuminate\Filesystem\FilesystemAdapter::allFiles($directory); + } + + /** + * Get all of the directories within a given directory. + * + * @param string|null $directory + * @param bool $recursive + * @return array + * @static + */ + public static function directories($directory = null, $recursive = false) + { + return \Illuminate\Filesystem\FilesystemAdapter::directories($directory, $recursive); + } + + /** + * Get all (recursive) of the directories within a given directory. + * + * @param string|null $directory + * @return array + * @static + */ + public static function allDirectories($directory = null) + { + return \Illuminate\Filesystem\FilesystemAdapter::allDirectories($directory); + } + + /** + * Create a directory. + * + * @param string $path + * @return bool + * @static + */ + public static function makeDirectory($path) + { + return \Illuminate\Filesystem\FilesystemAdapter::makeDirectory($path); + } + + /** + * Recursively delete a directory. + * + * @param string $directory + * @return bool + * @static + */ + public static function deleteDirectory($directory) + { + return \Illuminate\Filesystem\FilesystemAdapter::deleteDirectory($directory); + } + + /** + * Get the Flysystem driver. + * + * @return \League\Flysystem\FilesystemInterface + * @static + */ + public static function getDriver() + { + return \Illuminate\Filesystem\FilesystemAdapter::getDriver(); + } + } + + + class URL extends \Illuminate\Support\Facades\URL + { + /** + * Get the full URL for the current request. + * + * @return string + * @static + */ + public static function full() + { + return \Illuminate\Routing\UrlGenerator::full(); + } + + /** + * Get the current URL for the request. + * + * @return string + * @static + */ + public static function current() + { + return \Illuminate\Routing\UrlGenerator::current(); + } + + /** + * Get the URL for the previous request. + * + * @param mixed $fallback + * @return string + * @static + */ + public static function previous($fallback = false) + { + return \Illuminate\Routing\UrlGenerator::previous($fallback); + } + + /** + * Generate an absolute URL to the given path. + * + * @param string $path + * @param mixed $extra + * @param bool|null $secure + * @return string + * @static + */ + public static function to($path, $extra = [], $secure = null) + { + return \Illuminate\Routing\UrlGenerator::to($path, $extra, $secure); + } + + /** + * Generate a secure, absolute URL to the given path. + * + * @param string $path + * @param array $parameters + * @return string + * @static + */ + public static function secure($path, $parameters = []) + { + return \Illuminate\Routing\UrlGenerator::secure($path, $parameters); + } + + /** + * Generate the URL to an application asset. + * + * @param string $path + * @param bool|null $secure + * @return string + * @static + */ + public static function asset($path, $secure = null) + { + return \Illuminate\Routing\UrlGenerator::asset($path, $secure); + } + + /** + * Generate the URL to a secure asset. + * + * @param string $path + * @return string + * @static + */ + public static function secureAsset($path) + { + return \Illuminate\Routing\UrlGenerator::secureAsset($path); + } + + /** + * Generate the URL to an asset from a custom root domain such as CDN, etc. + * + * @param string $root + * @param string $path + * @param bool|null $secure + * @return string + * @static + */ + public static function assetFrom($root, $path, $secure = null) + { + return \Illuminate\Routing\UrlGenerator::assetFrom($root, $path, $secure); + } + + /** + * Get the default scheme for a raw URL. + * + * @param bool|null $secure + * @return string + * @static + */ + public static function formatScheme($secure) + { + return \Illuminate\Routing\UrlGenerator::formatScheme($secure); + } + + /** + * Get the URL to a named route. + * + * @param string $name + * @param mixed $parameters + * @param bool $absolute + * @return string + * @throws \InvalidArgumentException + * @static + */ + public static function route($name, $parameters = [], $absolute = true) + { + return \Illuminate\Routing\UrlGenerator::route($name, $parameters, $absolute); + } + + /** + * Get the URL to a controller action. + * + * @param string $action + * @param mixed $parameters + * @param bool $absolute + * @return string + * @throws \InvalidArgumentException + * @static + */ + public static function action($action, $parameters = [], $absolute = true) + { + return \Illuminate\Routing\UrlGenerator::action($action, $parameters, $absolute); + } + + /** + * Format the array of URL parameters. + * + * @param mixed|array $parameters + * @return array + * @static + */ + public static function formatParameters($parameters) + { + return \Illuminate\Routing\UrlGenerator::formatParameters($parameters); + } + + /** + * Get the base URL for the request. + * + * @param string $scheme + * @param string $root + * @return string + * @static + */ + public static function formatRoot($scheme, $root = null) + { + return \Illuminate\Routing\UrlGenerator::formatRoot($scheme, $root); + } + + /** + * Format the given URL segments into a single URL. + * + * @param string $root + * @param string $path + * @return string + * @static + */ + public static function format($root, $path) + { + return \Illuminate\Routing\UrlGenerator::format($root, $path); + } + + /** + * Determine if the given path is a valid URL. + * + * @param string $path + * @return bool + * @static + */ + public static function isValidUrl($path) + { + return \Illuminate\Routing\UrlGenerator::isValidUrl($path); + } + + /** + * Set the default named parameters used by the URL generator. + * + * @param array $defaults + * @return void + * @static + */ + public static function defaults($defaults) + { + \Illuminate\Routing\UrlGenerator::defaults($defaults); + } + + /** + * Force the scheme for URLs. + * + * @param string $schema + * @return void + * @static + */ + public static function forceScheme($schema) + { + \Illuminate\Routing\UrlGenerator::forceScheme($schema); + } + + /** + * Set the forced root URL. + * + * @param string $root + * @return void + * @static + */ + public static function forceRootUrl($root) + { + \Illuminate\Routing\UrlGenerator::forceRootUrl($root); + } + + /** + * Set a callback to be used to format the host of generated URLs. + * + * @param \Closure $callback + * @return $this + * @static + */ + public static function formatHostUsing($callback) + { + return \Illuminate\Routing\UrlGenerator::formatHostUsing($callback); + } + + /** + * Set a callback to be used to format the path of generated URLs. + * + * @param \Closure $callback + * @return $this + * @static + */ + public static function formatPathUsing($callback) + { + return \Illuminate\Routing\UrlGenerator::formatPathUsing($callback); + } + + /** + * Get the path formatter being used by the URL generator. + * + * @return \Closure + * @static + */ + public static function pathFormatter() + { + return \Illuminate\Routing\UrlGenerator::pathFormatter(); + } + + /** + * Get the request instance. + * + * @return \Illuminate\Http\Request + * @static + */ + public static function getRequest() + { + return \Illuminate\Routing\UrlGenerator::getRequest(); + } + + /** + * Set the current request instance. + * + * @param \Illuminate\Http\Request $request + * @return void + * @static + */ + public static function setRequest($request) + { + \Illuminate\Routing\UrlGenerator::setRequest($request); + } + + /** + * Set the route collection. + * + * @param \Illuminate\Routing\RouteCollection $routes + * @return $this + * @static + */ + public static function setRoutes($routes) + { + return \Illuminate\Routing\UrlGenerator::setRoutes($routes); + } + + /** + * Set the session resolver for the generator. + * + * @param callable $sessionResolver + * @return $this + * @static + */ + public static function setSessionResolver($sessionResolver) + { + return \Illuminate\Routing\UrlGenerator::setSessionResolver($sessionResolver); + } + + /** + * Set the root controller namespace. + * + * @param string $rootNamespace + * @return $this + * @static + */ + public static function setRootControllerNamespace($rootNamespace) + { + return \Illuminate\Routing\UrlGenerator::setRootControllerNamespace($rootNamespace); + } + + /** + * Register a custom macro. + * + * @param string $name + * @param callable $macro + * @return void + * @static + */ + public static function macro($name, $macro) + { + \Illuminate\Routing\UrlGenerator::macro($name, $macro); + } + + /** + * Checks if macro is registered. + * + * @param string $name + * @return bool + * @static + */ + public static function hasMacro($name) + { + return \Illuminate\Routing\UrlGenerator::hasMacro($name); + } + } + + + class Validator extends \Illuminate\Support\Facades\Validator + { + /** + * Create a new Validator instance. + * + * @param array $data + * @param array $rules + * @param array $messages + * @param array $customAttributes + * @return \Illuminate\Validation\Validator + * @static + */ + public static function make($data, $rules, $messages = [], $customAttributes = []) + { + return \Illuminate\Validation\Factory::make($data, $rules, $messages, $customAttributes); + } + + /** + * Validate the given data against the provided rules. + * + * @param array $data + * @param array $rules + * @param array $messages + * @param array $customAttributes + * @return void + * @throws \Illuminate\Validation\ValidationException + * @static + */ + public static function validate($data, $rules, $messages = [], $customAttributes = []) + { + \Illuminate\Validation\Factory::validate($data, $rules, $messages, $customAttributes); + } + + /** + * Register a custom validator extension. + * + * @param string $rule + * @param \Closure|string $extension + * @param string $message + * @return void + * @static + */ + public static function extend($rule, $extension, $message = null) + { + \Illuminate\Validation\Factory::extend($rule, $extension, $message); + } + + /** + * Register a custom implicit validator extension. + * + * @param string $rule + * @param \Closure|string $extension + * @param string $message + * @return void + * @static + */ + public static function extendImplicit($rule, $extension, $message = null) + { + \Illuminate\Validation\Factory::extendImplicit($rule, $extension, $message); + } + + /** + * Register a custom implicit validator message replacer. + * + * @param string $rule + * @param \Closure|string $replacer + * @return void + * @static + */ + public static function replacer($rule, $replacer) + { + \Illuminate\Validation\Factory::replacer($rule, $replacer); + } + + /** + * Set the Validator instance resolver. + * + * @param \Closure $resolver + * @return void + * @static + */ + public static function resolver($resolver) + { + \Illuminate\Validation\Factory::resolver($resolver); + } + + /** + * Get the Translator implementation. + * + * @return \Illuminate\Contracts\Translation\Translator + * @static + */ + public static function getTranslator() + { + return \Illuminate\Validation\Factory::getTranslator(); + } + + /** + * Get the Presence Verifier implementation. + * + * @return \Illuminate\Validation\PresenceVerifierInterface + * @static + */ + public static function getPresenceVerifier() + { + return \Illuminate\Validation\Factory::getPresenceVerifier(); + } + + /** + * Set the Presence Verifier implementation. + * + * @param \Illuminate\Validation\PresenceVerifierInterface $presenceVerifier + * @return void + * @static + */ + public static function setPresenceVerifier($presenceVerifier) + { + \Illuminate\Validation\Factory::setPresenceVerifier($presenceVerifier); + } + } + + + class View extends \Illuminate\Support\Facades\View + { + /** + * Get the evaluated view contents for the given view. + * + * @param string $path + * @param array $data + * @param array $mergeData + * @return \Illuminate\Contracts\View\View + * @static + */ + public static function file($path, $data = [], $mergeData = []) + { + return \Illuminate\View\Factory::file($path, $data, $mergeData); + } + + /** + * Get the evaluated view contents for the given view. + * + * @param string $view + * @param array $data + * @param array $mergeData + * @return \Illuminate\Contracts\View\View + * @static + */ + public static function make($view, $data = [], $mergeData = []) + { + return \Illuminate\View\Factory::make($view, $data, $mergeData); + } + + /** + * Get the rendered contents of a partial from a loop. + * + * @param string $view + * @param array $data + * @param string $iterator + * @param string $empty + * @return string + * @static + */ + public static function renderEach($view, $data, $iterator, $empty = 'raw|') + { + return \Illuminate\View\Factory::renderEach($view, $data, $iterator, $empty); + } + + /** + * Determine if a given view exists. + * + * @param string $view + * @return bool + * @static + */ + public static function exists($view) + { + return \Illuminate\View\Factory::exists($view); + } + + /** + * Get the appropriate view engine for the given path. + * + * @param string $path + * @return \Illuminate\View\Engines\EngineInterface + * @throws \InvalidArgumentException + * @static + */ + public static function getEngineFromPath($path) + { + return \Illuminate\View\Factory::getEngineFromPath($path); + } + + /** + * Add a piece of shared data to the environment. + * + * @param array|string $key + * @param mixed $value + * @return mixed + * @static + */ + public static function share($key, $value = null) + { + return \Illuminate\View\Factory::share($key, $value); + } + + /** + * Increment the rendering counter. + * + * @return void + * @static + */ + public static function incrementRender() + { + \Illuminate\View\Factory::incrementRender(); + } + + /** + * Decrement the rendering counter. + * + * @return void + * @static + */ + public static function decrementRender() + { + \Illuminate\View\Factory::decrementRender(); + } + + /** + * Check if there are no active render operations. + * + * @return bool + * @static + */ + public static function doneRendering() + { + return \Illuminate\View\Factory::doneRendering(); + } + + /** + * Add a location to the array of view locations. + * + * @param string $location + * @return void + * @static + */ + public static function addLocation($location) + { + \Illuminate\View\Factory::addLocation($location); + } + + /** + * Add a new namespace to the loader. + * + * @param string $namespace + * @param string|array $hints + * @return $this + * @static + */ + public static function addNamespace($namespace, $hints) + { + return \Illuminate\View\Factory::addNamespace($namespace, $hints); + } + + /** + * Prepend a new namespace to the loader. + * + * @param string $namespace + * @param string|array $hints + * @return $this + * @static + */ + public static function prependNamespace($namespace, $hints) + { + return \Illuminate\View\Factory::prependNamespace($namespace, $hints); + } + + /** + * Replace the namespace hints for the given namespace. + * + * @param string $namespace + * @param string|array $hints + * @return $this + * @static + */ + public static function replaceNamespace($namespace, $hints) + { + return \Illuminate\View\Factory::replaceNamespace($namespace, $hints); + } + + /** + * Register a valid view extension and its engine. + * + * @param string $extension + * @param string $engine + * @param \Closure $resolver + * @return void + * @static + */ + public static function addExtension($extension, $engine, $resolver = null) + { + \Illuminate\View\Factory::addExtension($extension, $engine, $resolver); + } + + /** + * Flush all of the factory state like sections and stacks. + * + * @return void + * @static + */ + public static function flushState() + { + \Illuminate\View\Factory::flushState(); + } + + /** + * Flush all of the section contents if done rendering. + * + * @return void + * @static + */ + public static function flushStateIfDoneRendering() + { + \Illuminate\View\Factory::flushStateIfDoneRendering(); + } + + /** + * Get the extension to engine bindings. + * + * @return array + * @static + */ + public static function getExtensions() + { + return \Illuminate\View\Factory::getExtensions(); + } + + /** + * Get the engine resolver instance. + * + * @return \Illuminate\View\Engines\EngineResolver + * @static + */ + public static function getEngineResolver() + { + return \Illuminate\View\Factory::getEngineResolver(); + } + + /** + * Get the view finder instance. + * + * @return \Illuminate\View\ViewFinderInterface + * @static + */ + public static function getFinder() + { + return \Illuminate\View\Factory::getFinder(); + } + + /** + * Set the view finder instance. + * + * @param \Illuminate\View\ViewFinderInterface $finder + * @return void + * @static + */ + public static function setFinder($finder) + { + \Illuminate\View\Factory::setFinder($finder); + } + + /** + * Flush the cache of views located by the finder. + * + * @return void + * @static + */ + public static function flushFinderCache() + { + \Illuminate\View\Factory::flushFinderCache(); + } + + /** + * Get the event dispatcher instance. + * + * @return \Illuminate\Contracts\Events\Dispatcher + * @static + */ + public static function getDispatcher() + { + return \Illuminate\View\Factory::getDispatcher(); + } + + /** + * Set the event dispatcher instance. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + * @static + */ + public static function setDispatcher($events) + { + \Illuminate\View\Factory::setDispatcher($events); + } + + /** + * Get the IoC container instance. + * + * @return \Illuminate\Contracts\Container\Container + * @static + */ + public static function getContainer() + { + return \Illuminate\View\Factory::getContainer(); + } + + /** + * Set the IoC container instance. + * + * @param \Illuminate\Contracts\Container\Container $container + * @return void + * @static + */ + public static function setContainer($container) + { + \Illuminate\View\Factory::setContainer($container); + } + + /** + * Get an item from the shared data. + * + * @param string $key + * @param mixed $default + * @return mixed + * @static + */ + public static function shared($key, $default = null) + { + return \Illuminate\View\Factory::shared($key, $default); + } + + /** + * Get all of the shared data for the environment. + * + * @return array + * @static + */ + public static function getShared() + { + return \Illuminate\View\Factory::getShared(); + } + + /** + * Start a component rendering process. + * + * @param string $name + * @param array $data + * @return void + * @static + */ + public static function startComponent($name, $data = []) + { + \Illuminate\View\Factory::startComponent($name, $data); + } + + /** + * Render the current component. + * + * @return string + * @static + */ + public static function renderComponent() + { + return \Illuminate\View\Factory::renderComponent(); + } + + /** + * Start the slot rendering process. + * + * @param string $name + * @param string|null $content + * @return void + * @static + */ + public static function slot($name, $content = null) + { + \Illuminate\View\Factory::slot($name, $content); + } + + /** + * Save the slot content for rendering. + * + * @return void + * @static + */ + public static function endSlot() + { + \Illuminate\View\Factory::endSlot(); + } + + /** + * Register a view creator event. + * + * @param array|string $views + * @param \Closure|string $callback + * @return array + * @static + */ + public static function creator($views, $callback) + { + return \Illuminate\View\Factory::creator($views, $callback); + } + + /** + * Register multiple view composers via an array. + * + * @param array $composers + * @return array + * @static + */ + public static function composers($composers) + { + return \Illuminate\View\Factory::composers($composers); + } + + /** + * Register a view composer event. + * + * @param array|string $views + * @param \Closure|string $callback + * @return array + * @static + */ + public static function composer($views, $callback) + { + return \Illuminate\View\Factory::composer($views, $callback); + } + + /** + * Call the composer for a given view. + * + * @param \Illuminate\Contracts\View\View $view + * @return void + * @static + */ + public static function callComposer($view) + { + \Illuminate\View\Factory::callComposer($view); + } + + /** + * Call the creator for a given view. + * + * @param \Illuminate\Contracts\View\View $view + * @return void + * @static + */ + public static function callCreator($view) + { + \Illuminate\View\Factory::callCreator($view); + } + + /** + * Start injecting content into a section. + * + * @param string $section + * @param string|null $content + * @return void + * @static + */ + public static function startSection($section, $content = null) + { + \Illuminate\View\Factory::startSection($section, $content); + } + + /** + * Inject inline content into a section. + * + * @param string $section + * @param string $content + * @return void + * @static + */ + public static function inject($section, $content) + { + \Illuminate\View\Factory::inject($section, $content); + } + + /** + * Stop injecting content into a section and return its contents. + * + * @return string + * @static + */ + public static function yieldSection() + { + return \Illuminate\View\Factory::yieldSection(); + } + + /** + * Stop injecting content into a section. + * + * @param bool $overwrite + * @return string + * @throws \InvalidArgumentException + * @static + */ + public static function stopSection($overwrite = false) + { + return \Illuminate\View\Factory::stopSection($overwrite); + } + + /** + * Stop injecting content into a section and append it. + * + * @return string + * @throws \InvalidArgumentException + * @static + */ + public static function appendSection() + { + return \Illuminate\View\Factory::appendSection(); + } + + /** + * Get the string contents of a section. + * + * @param string $section + * @param string $default + * @return string + * @static + */ + public static function yieldContent($section, $default = '') + { + return \Illuminate\View\Factory::yieldContent($section, $default); + } + + /** + * Get the parent placeholder for the current request. + * + * @param string $section + * @return string + * @static + */ + public static function parentPlaceholder($section = '') + { + return \Illuminate\View\Factory::parentPlaceholder($section); + } + + /** + * Check if section exists. + * + * @param string $name + * @return bool + * @static + */ + public static function hasSection($name) + { + return \Illuminate\View\Factory::hasSection($name); + } + + /** + * Get the entire array of sections. + * + * @return array + * @static + */ + public static function getSections() + { + return \Illuminate\View\Factory::getSections(); + } + + /** + * Flush all of the sections. + * + * @return void + * @static + */ + public static function flushSections() + { + \Illuminate\View\Factory::flushSections(); + } + + /** + * Add new loop to the stack. + * + * @param \Countable|array $data + * @return void + * @static + */ + public static function addLoop($data) + { + \Illuminate\View\Factory::addLoop($data); + } + + /** + * Increment the top loop's indices. + * + * @return void + * @static + */ + public static function incrementLoopIndices() + { + \Illuminate\View\Factory::incrementLoopIndices(); + } + + /** + * Pop a loop from the top of the loop stack. + * + * @return void + * @static + */ + public static function popLoop() + { + \Illuminate\View\Factory::popLoop(); + } + + /** + * Get an instance of the last loop in the stack. + * + * @return \StdClass|null + * @static + */ + public static function getLastLoop() + { + return \Illuminate\View\Factory::getLastLoop(); + } + + /** + * Get the entire loop stack. + * + * @return array + * @static + */ + public static function getLoopStack() + { + return \Illuminate\View\Factory::getLoopStack(); + } + + /** + * Start injecting content into a push section. + * + * @param string $section + * @param string $content + * @return void + * @static + */ + public static function startPush($section, $content = '') + { + \Illuminate\View\Factory::startPush($section, $content); + } + + /** + * Stop injecting content into a push section. + * + * @return string + * @throws \InvalidArgumentException + * @static + */ + public static function stopPush() + { + return \Illuminate\View\Factory::stopPush(); + } + + /** + * Get the string contents of a push section. + * + * @param string $section + * @param string $default + * @return string + * @static + */ + public static function yieldPushContent($section, $default = '') + { + return \Illuminate\View\Factory::yieldPushContent($section, $default); + } + + /** + * Flush all of the stacks. + * + * @return void + * @static + */ + public static function flushStacks() + { + \Illuminate\View\Factory::flushStacks(); + } + + /** + * Start a translation block. + * + * @param array $replacements + * @return void + * @static + */ + public static function startTranslation($replacements = []) + { + \Illuminate\View\Factory::startTranslation($replacements); + } + + /** + * Render the current translation. + * + * @return string + * @static + */ + public static function renderTranslation() + { + return \Illuminate\View\Factory::renderTranslation(); + } + } + + + class JWTAuth extends \Tymon\JWTAuth\Facades\JWTAuth + { + /** + * Find a user using the user identifier in the subject claim. + * + * @param bool|string $token + * @return mixed + * @static + */ + public static function toUser($token = false) + { + return \Tymon\JWTAuth\JWTAuth::toUser($token); + } + + /** + * Generate a token using the user identifier as the subject claim. + * + * @param mixed $user + * @param array $customClaims + * @return string + * @static + */ + public static function fromUser($user, $customClaims = []) + { + return \Tymon\JWTAuth\JWTAuth::fromUser($user, $customClaims); + } + + /** + * Attempt to authenticate the user and return the token. + * + * @param array $credentials + * @param array $customClaims + * @return false|string + * @static + */ + public static function attempt($credentials = [], $customClaims = []) + { + return \Tymon\JWTAuth\JWTAuth::attempt($credentials, $customClaims); + } + + /** + * Authenticate a user via a token. + * + * @param mixed $token + * @return mixed + * @static + */ + public static function authenticate($token = false) + { + return \Tymon\JWTAuth\JWTAuth::authenticate($token); + } + + /** + * Refresh an expired token. + * + * @param mixed $token + * @return string + * @static + */ + public static function refresh($token = false) + { + return \Tymon\JWTAuth\JWTAuth::refresh($token); + } + + /** + * Invalidate a token (add it to the blacklist). + * + * @param mixed $token + * @return bool + * @static + */ + public static function invalidate($token = false) + { + return \Tymon\JWTAuth\JWTAuth::invalidate($token); + } + + /** + * Get the token. + * + * @return bool|string + * @static + */ + public static function getToken() + { + return \Tymon\JWTAuth\JWTAuth::getToken(); + } + + /** + * Get the raw Payload instance. + * + * @param mixed $token + * @return \Tymon\JWTAuth\Payload + * @static + */ + public static function getPayload($token = false) + { + return \Tymon\JWTAuth\JWTAuth::getPayload($token); + } + + /** + * Parse the token from the request. + * + * @param string $query + * @return \JWTAuth + * @static + */ + public static function parseToken($method = 'bearer', $header = 'authorization', $query = 'token') + { + return \Tymon\JWTAuth\JWTAuth::parseToken($method, $header, $query); + } + + /** + * Set the identifier. + * + * @param string $identifier + * @return $this + * @static + */ + public static function setIdentifier($identifier) + { + return \Tymon\JWTAuth\JWTAuth::setIdentifier($identifier); + } + + /** + * Get the identifier. + * + * @return string + * @static + */ + public static function getIdentifier() + { + return \Tymon\JWTAuth\JWTAuth::getIdentifier(); + } + + /** + * Set the token. + * + * @param string $token + * @return $this + * @static + */ + public static function setToken($token) + { + return \Tymon\JWTAuth\JWTAuth::setToken($token); + } + + /** + * Set the request instance. + * + * @param \Request $request + * @static + */ + public static function setRequest($request) + { + return \Tymon\JWTAuth\JWTAuth::setRequest($request); + } + + /** + * Get the JWTManager instance. + * + * @return \Tymon\JWTAuth\JWTManager + * @static + */ + public static function manager() + { + return \Tymon\JWTAuth\JWTAuth::manager(); + } + } + + +} diff --git a/crater/app/Console/Commands/CheckEstimateStatus.php b/crater/app/Console/Commands/CheckEstimateStatus.php new file mode 100644 index 0000000..12bbc64 --- /dev/null +++ b/crater/app/Console/Commands/CheckEstimateStatus.php @@ -0,0 +1,52 @@ +whereDate('expiry_date', '<', $date)->get(); + + foreach ($estimates as $estimate) { + $estimate->status = Estimate::STATUS_EXPIRED; + printf("Estimate %s is EXPIRED \n", $estimate->estimate_number); + $estimate->save(); + } + } +} diff --git a/crater/app/Console/Commands/CheckInvoiceStatus.php b/crater/app/Console/Commands/CheckInvoiceStatus.php new file mode 100644 index 0000000..0eeb272 --- /dev/null +++ b/crater/app/Console/Commands/CheckInvoiceStatus.php @@ -0,0 +1,54 @@ +where('overdue', false) + ->whereDate('due_date', '<', $date) + ->get(); + + foreach ($invoices as $invoice) { + $invoice->overdue = true; + printf("Invoice %s is OVERDUE \n", $invoice->invoice_number); + $invoice->save(); + } + } +} diff --git a/crater/app/Console/Commands/CreateTemplateCommand.php b/crater/app/Console/Commands/CreateTemplateCommand.php new file mode 100644 index 0000000..0cb9ed4 --- /dev/null +++ b/crater/app/Console/Commands/CreateTemplateCommand.php @@ -0,0 +1,64 @@ +argument('name'); + $type = $this->option('type'); + + if (! $type) { + $type = $this->choice('Create a template for?', ['invoice', 'estimate']); + } + + if (Storage::disk('views')->exists("/app/pdf/{$type}/{$templateName}.blade.php")) { + $this->info("Template with given name already exists."); + + return 0; + } + + Storage::disk('views')->copy("/app/pdf/{$type}/{$type}1.blade.php", "/app/pdf/{$type}/{$templateName}.blade.php"); + copy(public_path("/build/img/PDF/{$type}1.png"), public_path("/build/img/PDF/{$templateName}.png")); + copy(resource_path("/static/img/PDF/{$type}1.png"), resource_path("/static/img/PDF/{$templateName}.png")); + + $path = resource_path("views/app/pdf/{$type}/{$templateName}.blade.php"); + $type = ucfirst($type); + $this->info("{$type} Template created successfully at ".$path); + + return 0; + } +} diff --git a/crater/app/Console/Commands/InstallModuleCommand.php b/crater/app/Console/Commands/InstallModuleCommand.php new file mode 100644 index 0000000..54c60e5 --- /dev/null +++ b/crater/app/Console/Commands/InstallModuleCommand.php @@ -0,0 +1,45 @@ +argument('module'), $this->argument('version')); + + return Command::SUCCESS; + } +} diff --git a/crater/app/Console/Commands/ResetApp.php b/crater/app/Console/Commands/ResetApp.php new file mode 100644 index 0000000..20d48a1 --- /dev/null +++ b/crater/app/Console/Commands/ResetApp.php @@ -0,0 +1,68 @@ +confirmToProceed()) { + return; + } + + $this->info('Running migrate:fresh'); + + Artisan::call('migrate:fresh --seed --force'); + + $this->info('Seeding database'); + + Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]); + + $path = base_path('.env'); + + if (file_exists($path)) { + file_put_contents($path, str_replace( + 'APP_DEBUG=true', + 'APP_DEBUG=false', + file_get_contents($path) + )); + } + + $this->info('App has been reset successfully'); + } +} diff --git a/crater/app/Console/Commands/UpdateCommand.php b/crater/app/Console/Commands/UpdateCommand.php new file mode 100644 index 0000000..9229d0a --- /dev/null +++ b/crater/app/Console/Commands/UpdateCommand.php @@ -0,0 +1,241 @@ +installed = $this->getInstalledVersion(); + $this->response = $this->getLatestVersionResponse(); + $this->version = ($this->response) ? $this->response->version : false; + + if ($this->response == 'extension_required') { + $this->info('Sorry! Your system does not meet the minimum requirements for this update.'); + $this->info('Please retry after installing the required version/extensions.'); + + return; + } + + if (! $this->version) { + $this->info('No Update Available! You are already on the latest version.'); + + return; + } + + if (! $this->confirm("Do you wish to update to {$this->version}?")) { + return; + } + + if (! $path = $this->download()) { + return; + } + + if (! $path = $this->unzip($path)) { + return; + } + + if (! $this->copyFiles($path)) { + return; + } + + if (isset($this->response->deleted_files) && ! empty($this->response->deleted_files)) { + if (! $this->deleteFiles($this->response->deleted_files)) { + return; + } + } + + if (! $this->migrateUpdate()) { + return; + } + + if (! $this->finish()) { + return; + } + + $this->info('Successfully updated to '.$this->version); + } + + public function getInstalledVersion() + { + return Setting::getSetting('version'); + } + + public function getLatestVersionResponse() + { + $this->info('Your currently installed version is '.$this->installed); + $this->line(''); + $this->info('Checking for update...'); + + try { + $response = Updater::checkForUpdate($this->installed); + + if ($response->success) { + $extensions = $response->version->extensions; + + $is_required = false; + + foreach ($extensions as $key => $extension) { + if (! $extension) { + $is_required = true; + $this->info('❌ '.$key); + } + + $this->info('✅ '.$key); + } + + if ($is_required) { + return 'extension_required'; + } + + return $response->version; + } + + return false; + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + } + + public function download() + { + $this->info('Downloading update...'); + + try { + $path = Updater::download($this->version, 1); + if (! is_string($path)) { + $this->error('Download exception'); + + return false; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return $path; + } + + public function unzip($path) + { + $this->info('Unzipping update package...'); + + try { + $path = Updater::unzip($path); + if (! is_string($path)) { + $this->error('Unzipping exception'); + + return false; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return $path; + } + + public function copyFiles($path) + { + $this->info('Copying update files...'); + + try { + Updater::copyFiles($path); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } + + public function deleteFiles($files) + { + $this->info('Deleting unused old files...'); + + try { + Updater::deleteFiles($files); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } + + public function migrateUpdate() + { + $this->info('Running Migrations...'); + + try { + Updater::migrateUpdate(); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } + + public function finish() + { + $this->info('Finishing update...'); + + try { + Updater::finishUpdate($this->installed, $this->version); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } +} diff --git a/crater/app/Console/Kernel.php b/crater/app/Console/Kernel.php new file mode 100644 index 0000000..c6de89c --- /dev/null +++ b/crater/app/Console/Kernel.php @@ -0,0 +1,60 @@ +has('database_created')) { + $schedule->command('check:invoices:status') + ->daily(); + + $schedule->command('check:estimates:status') + ->daily(); + + $recurringInvoices = RecurringInvoice::where('status', 'ACTIVE')->get(); + foreach ($recurringInvoices as $recurringInvoice) { + $timeZone = CompanySetting::getSetting('time_zone', $recurringInvoice->company_id); + + $schedule->call(function () use ($recurringInvoice) { + $recurringInvoice->generateInvoice(); + })->cron($recurringInvoice->frequency)->timezone($timeZone); + } + } + } + + /** + * Register the Closure based commands for the application. + * + * @return void + */ + protected function commands() + { + $this->load(__DIR__.'/Commands'); + require base_path('routes/console.php'); + } +} diff --git a/crater/app/Events/ModuleDisabledEvent.php b/crater/app/Events/ModuleDisabledEvent.php new file mode 100644 index 0000000..2bbdb41 --- /dev/null +++ b/crater/app/Events/ModuleDisabledEvent.php @@ -0,0 +1,26 @@ +module = $module; + } +} diff --git a/crater/app/Events/ModuleEnabledEvent.php b/crater/app/Events/ModuleEnabledEvent.php new file mode 100644 index 0000000..a996a50 --- /dev/null +++ b/crater/app/Events/ModuleEnabledEvent.php @@ -0,0 +1,26 @@ +module = $module; + } +} diff --git a/crater/app/Events/ModuleInstalledEvent.php b/crater/app/Events/ModuleInstalledEvent.php new file mode 100644 index 0000000..d80fc0e --- /dev/null +++ b/crater/app/Events/ModuleInstalledEvent.php @@ -0,0 +1,26 @@ +module = $module; + } +} diff --git a/crater/app/Events/UpdateFinished.php b/crater/app/Events/UpdateFinished.php new file mode 100644 index 0000000..7eccbff --- /dev/null +++ b/crater/app/Events/UpdateFinished.php @@ -0,0 +1,25 @@ +old = $old; + $this->new = $new; + } +} diff --git a/crater/app/Exceptions/Handler.php b/crater/app/Exceptions/Handler.php new file mode 100644 index 0000000..01dea2a --- /dev/null +++ b/crater/app/Exceptions/Handler.php @@ -0,0 +1,53 @@ +getBasePath($media).'/'; + } + + public function getPathForConversions(Media $media): string + { + return $this->getBasePath($media).'/conversations/'; + } + + public function getPathForResponsiveImages(Media $media): string + { + return $this->getBasePath($media).'/responsive-images/'; + } + + /* + * Get a unique base path for the given media. + */ + protected function getBasePath(Media $media): string + { + $folderName = null; + + if ($media->model_type == Invoice::class) { + $folderName = 'Invoices'; + } elseif ($media->model_type == Estimate::class) { + $folderName = 'Estimates'; + } elseif ($media->model_type == Payment::class) { + $folderName = 'Payments'; + } else { + $folderName = $media->getKey(); + } + + return $folderName; + } +} diff --git a/crater/app/Http/Controllers/AppVersionController.php b/crater/app/Http/Controllers/AppVersionController.php new file mode 100644 index 0000000..5c4bad0 --- /dev/null +++ b/crater/app/Http/Controllers/AppVersionController.php @@ -0,0 +1,24 @@ +json([ + 'version' => $version, + ]); + } +} diff --git a/crater/app/Http/Controllers/Controller.php b/crater/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..f580f89 --- /dev/null +++ b/crater/app/Http/Controllers/Controller.php @@ -0,0 +1,15 @@ +middleware('auth'); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Auth/ForgotPasswordController.php b/crater/app/Http/Controllers/V1/Admin/Auth/ForgotPasswordController.php new file mode 100644 index 0000000..39122e7 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Auth/ForgotPasswordController.php @@ -0,0 +1,52 @@ +json([ + 'message' => 'Password reset email sent.', + 'data' => $response, + ]); + } + + /** + * Get the response for a failed password reset link. + * + * @param \Illuminate\Http\Request $request + * @param string $response + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + */ + protected function sendResetLinkFailedResponse(Request $request, $response) + { + return response()->json([ + 'error' => 'Email could not be sent to this email address.' + ], 403); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Auth/LoginController.php b/crater/app/Http/Controllers/V1/Admin/Auth/LoginController.php new file mode 100644 index 0000000..385dee3 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Auth/LoginController.php @@ -0,0 +1,40 @@ +middleware('guest')->except('logout'); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Auth/RegisterController.php b/crater/app/Http/Controllers/V1/Admin/Auth/RegisterController.php new file mode 100644 index 0000000..5967231 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Auth/RegisterController.php @@ -0,0 +1,72 @@ +middleware('guest'); + } + + /** + * Get a validator for an incoming registration request. + * + * @param array $data + * @return \Illuminate\Contracts\Validation\Validator + */ + protected function validator(array $data) + { + return Validator::make($data, [ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], + 'password' => ['required', 'string', 'min:8', 'confirmed'], + ]); + } + + /** + * Create a new user instance after a valid registration. + * + * @param array $data + * @return \App\User + */ + protected function create(array $data) + { + return User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => $data['password'], + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Auth/ResetPasswordController.php b/crater/app/Http/Controllers/V1/Admin/Auth/ResetPasswordController.php new file mode 100644 index 0000000..268e1f7 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Auth/ResetPasswordController.php @@ -0,0 +1,77 @@ +json([ + 'message' => 'Password reset successfully.', + ]); + } + + /** + * Reset the given user's password. + * + * @param \Illuminate\Contracts\Auth\CanResetPassword $user + * @param string $password + * @return void + */ + protected function resetPassword($user, $password) + { + $user->password = $password; + + $user->setRememberToken(Str::random(60)); + + $user->save(); + + event(new PasswordReset($user)); + } + + /** + * Get the response for a failed password reset. + * + * @param \Illuminate\Http\Request $request + * @param string $response + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + */ + protected function sendResetFailedResponse(Request $request, $response) + { + return response('Failed, Invalid Token.', 403); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Auth/VerificationController.php b/crater/app/Http/Controllers/V1/Admin/Auth/VerificationController.php new file mode 100644 index 0000000..c6a9149 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Auth/VerificationController.php @@ -0,0 +1,42 @@ +middleware('auth'); + $this->middleware('signed')->only('verify'); + $this->middleware('throttle:6,1')->only('verify', 'resend'); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Backup/ApiController.php b/crater/app/Http/Controllers/V1/Admin/Backup/ApiController.php new file mode 100644 index 0000000..12f660e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Backup/ApiController.php @@ -0,0 +1,22 @@ +json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Backup/BackupsController.php b/crater/app/Http/Controllers/V1/Admin/Backup/BackupsController.php new file mode 100644 index 0000000..2297655 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Backup/BackupsController.php @@ -0,0 +1,99 @@ +authorize('manage backups'); + + $configuredBackupDisks = config('backup.backup.destination.disks'); + + try { + $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); + + $backups = Cache::remember("backups-{$request->file_disk_id}", now()->addSeconds(4), function () use ($backupDestination) { + return $backupDestination + ->backups() + ->map(function (Backup $backup) { + return [ + 'path' => $backup->path(), + 'created_at' => $backup->date()->format('Y-m-d H:i:s'), + 'size' => Format::humanReadableSize($backup->size()), + ]; + }) + ->toArray(); + }); + + return response()->json([ + 'backups' => $backups, + 'disks' => $configuredBackupDisks, + ]); + } catch (\Exception $e) { + return response()->json([ + 'backups' => [], + 'error' => 'invalid_disk_credentials', + 'error_message' => $e->getMessage(), + 'disks' => $configuredBackupDisks, + ]); + } + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return JsonResponse + */ + public function store(Request $request) + { + $this->authorize('manage backups'); + + dispatch(new CreateBackupJob($request->all()))->onQueue(config('backup.queue.name')); + + return $this->respondSuccess(); + } + + /** + * Remove the specified resource from storage. + * + * @param \Illuminate\Http\Request $request + * @return JsonResponse + */ + public function destroy($disk, Request $request) + { + $this->authorize('manage backups'); + + $validated = $request->validate([ + 'path' => ['required', new PathToZip()], + ]); + + $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); + + $backupDestination + ->backups() + ->first(function (Backup $backup) use ($validated) { + return $backup->path() === $validated['path']; + }) + ->delete(); + + return $this->respondSuccess(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Backup/DownloadBackupController.php b/crater/app/Http/Controllers/V1/Admin/Backup/DownloadBackupController.php new file mode 100644 index 0000000..734c74e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Backup/DownloadBackupController.php @@ -0,0 +1,59 @@ +authorize('manage backups'); + + $validated = $request->validate([ + 'path' => ['required', new PathToZip()], + ]); + + $backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name')); + + $backup = $backupDestination->backups()->first(function (Backup $backup) use ($validated) { + return $backup->path() === $validated['path']; + }); + + if (! $backup) { + return response('Backup not found', Response::HTTP_UNPROCESSABLE_ENTITY); + } + + return $this->respondWithBackupStream($backup); + } + + public function respondWithBackupStream(Backup $backup): StreamedResponse + { + $fileName = pathinfo($backup->path(), PATHINFO_BASENAME); + + $downloadHeaders = [ + 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', + 'Content-Type' => 'application/zip', + 'Content-Length' => $backup->size(), + 'Content-Disposition' => 'attachment; filename="'.$fileName.'"', + 'Pragma' => 'public', + ]; + + return response()->stream(function () use ($backup) { + $stream = $backup->stream(); + + fpassthru($stream); + + if (is_resource($stream)) { + fclose($stream); + } + }, 200, $downloadHeaders); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Company/CompaniesController.php b/crater/app/Http/Controllers/V1/Admin/Company/CompaniesController.php new file mode 100644 index 0000000..55f4353 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Company/CompaniesController.php @@ -0,0 +1,85 @@ +authorize('create company'); + + $user = $request->user(); + + $company = Company::create($request->getCompanyPayload()); + $company->unique_hash = Hashids::connection(Company::class)->encode($company->id); + $company->save(); + $company->setupDefaultData(); + $user->companies()->attach($company->id); + $user->assign('super admin'); + + if ($request->address) { + $company->address()->create($request->address); + } + + return new CompanyResource($company); + } + + public function destroy(Request $request) + { + $company = Company::find($request->header('company')); + + $this->authorize('delete company', $company); + + $user = $request->user(); + + if ($request->name !== $company->name) { + return respondJson('company_name_must_match_with_given_name', 'Company name must match with given name'); + } + + if ($user->loadCount('companies')->companies_count <= 1) { + return respondJson('You_cannot_delete_all_companies', 'You cannot delete all companies'); + } + + $company->deleteCompany($user); + + return response()->json([ + 'success' => true + ]); + } + + public function transferOwnership(Request $request, User $user) + { + $company = Company::find($request->header('company')); + $this->authorize('transfer company ownership', $company); + + if ($user->hasCompany($company->id)) { + return response()->json([ + 'success' => false, + 'message' => 'User does not belongs to this company.' + ]); + } + + $company->update(['owner_id' => $user->id]); + BouncerFacade::sync($user)->roles(['super admin']); + + return response()->json([ + 'success' => true + ]); + } + + public function getUserCompanies(Request $request) + { + $companies = $request->user()->companies; + + return CompanyResource::collection($companies); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Company/CompanyController.php b/crater/app/Http/Controllers/V1/Admin/Company/CompanyController.php new file mode 100644 index 0000000..7c6893d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Company/CompanyController.php @@ -0,0 +1,24 @@ +header('company')); + + return new CompanyResource($company); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Config/FiscalYearsController.php b/crater/app/Http/Controllers/V1/Admin/Config/FiscalYearsController.php new file mode 100644 index 0000000..4a77f63 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Config/FiscalYearsController.php @@ -0,0 +1,22 @@ +json([ + 'fiscal_years' => config('crater.fiscal_years'), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Config/LanguagesController.php b/crater/app/Http/Controllers/V1/Admin/Config/LanguagesController.php new file mode 100644 index 0000000..f9ffc6e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Config/LanguagesController.php @@ -0,0 +1,22 @@ +json([ + 'languages' => config('crater.languages'), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Config/RetrospectiveEditsController.php b/crater/app/Http/Controllers/V1/Admin/Config/RetrospectiveEditsController.php new file mode 100644 index 0000000..da5f145 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Config/RetrospectiveEditsController.php @@ -0,0 +1,22 @@ +json([ + 'retrospective_edits' => config('crater.retrospective_edits'), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/CustomField/CustomFieldsController.php b/crater/app/Http/Controllers/V1/Admin/CustomField/CustomFieldsController.php new file mode 100644 index 0000000..273fe9d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/CustomField/CustomFieldsController.php @@ -0,0 +1,96 @@ +authorize('viewAny', CustomField::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $customFields = CustomField::applyFilters($request->all()) + ->whereCompany() + ->latest() + ->paginateData($limit); + + return CustomFieldResource::collection($customFields); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\CustomFieldRequest $request + * @return \Illuminate\Http\Response + */ + public function store(CustomFieldRequest $request) + { + $this->authorize('create', CustomField::class); + + $customField = CustomField::createCustomField($request); + + return new CustomFieldResource($customField); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function show(CustomField $customField) + { + $this->authorize('view', $customField); + + return new CustomFieldResource($customField); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response + */ + public function update(CustomFieldRequest $request, CustomField $customField) + { + $this->authorize('update', $customField); + + $customField->updateCustomField($request); + + return new CustomFieldResource($customField); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function destroy(CustomField $customField) + { + $this->authorize('delete', $customField); + + if ($customField->customFieldValues()->exists()) { + $customField->customFieldValues()->delete(); + } + + $customField->forceDelete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Customer/CustomerStatsController.php b/crater/app/Http/Controllers/V1/Admin/Customer/CustomerStatsController.php new file mode 100644 index 0000000..1d2c041 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Customer/CustomerStatsController.php @@ -0,0 +1,142 @@ +authorize('view', $customer); + + $i = 0; + $months = []; + $invoiceTotals = []; + $expenseTotals = []; + $receiptTotals = []; + $netProfits = []; + $monthCounter = 0; + $fiscalYear = CompanySetting::getSetting('fiscal_year', $request->header('company')); + $startDate = Carbon::now(); + $start = Carbon::now(); + $end = Carbon::now(); + $terms = explode('-', $fiscalYear); + + if ($terms[0] <= $start->month) { + $startDate->month($terms[0])->startOfMonth(); + $start->month($terms[0])->startOfMonth(); + $end->month($terms[0])->endOfMonth(); + } else { + $startDate->subYear()->month($terms[0])->startOfMonth(); + $start->subYear()->month($terms[0])->startOfMonth(); + $end->subYear()->month($terms[0])->endOfMonth(); + } + + if ($request->has('previous_year')) { + $startDate->subYear()->startOfMonth(); + $start->subYear()->startOfMonth(); + $end->subYear()->endOfMonth(); + } + while ($monthCounter < 12) { + array_push( + $invoiceTotals, + Invoice::whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->whereCustomer($customer->id) + ->sum('total') ?? 0 + ); + array_push( + $expenseTotals, + Expense::whereBetween( + 'expense_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->whereUser($customer->id) + ->sum('amount') ?? 0 + ); + array_push( + $receiptTotals, + Payment::whereBetween( + 'payment_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->whereCustomer($customer->id) + ->sum('amount') ?? 0 + ); + array_push( + $netProfits, + ($receiptTotals[$i] - $expenseTotals[$i]) + ); + $i++; + array_push($months, $start->format('M')); + $monthCounter++; + $end->startOfMonth(); + $start->addMonth()->startOfMonth(); + $end->addMonth()->endOfMonth(); + } + + $start->subMonth()->endOfMonth(); + + $salesTotal = Invoice::whereBetween( + 'invoice_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->whereCustomer($customer->id) + ->sum('total'); + $totalReceipts = Payment::whereBetween( + 'payment_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->whereCustomer($customer->id) + ->sum('amount'); + $totalExpenses = Expense::whereBetween( + 'expense_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->whereUser($customer->id) + ->sum('amount'); + $netProfit = (int) $totalReceipts - (int) $totalExpenses; + + $chartData = [ + 'months' => $months, + 'invoiceTotals' => $invoiceTotals, + 'expenseTotals' => $expenseTotals, + 'receiptTotals' => $receiptTotals, + 'netProfit' => $netProfit, + 'netProfits' => $netProfits, + 'salesTotal' => $salesTotal, + 'totalReceipts' => $totalReceipts, + 'totalExpenses' => $totalExpenses, + ]; + + $customer = Customer::find($customer->id); + + return (new CustomerResource($customer)) + ->additional(['meta' => [ + 'chartData' => $chartData + ]]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Customer/CustomersController.php b/crater/app/Http/Controllers/V1/Admin/Customer/CustomersController.php new file mode 100644 index 0000000..55a011d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Customer/CustomersController.php @@ -0,0 +1,108 @@ +authorize('viewAny', Customer::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $customers = Customer::with('creator') + ->whereCompany() + ->applyFilters($request->all()) + ->select( + 'customers.*', + DB::raw('sum(invoices.base_due_amount) as base_due_amount'), + DB::raw('sum(invoices.due_amount) as due_amount'), + ) + ->groupBy('customers.id') + ->leftJoin('invoices', 'customers.id', '=', 'invoices.customer_id') + ->paginateData($limit); + + return (CustomerResource::collection($customers)) + ->additional(['meta' => [ + 'customer_total_count' => Customer::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(Requests\CustomerRequest $request) + { + $this->authorize('create', Customer::class); + + $customer = Customer::createCustomer($request); + + return new CustomerResource($customer); + } + + /** + * Display the specified resource. + * + * @param Customer $customer + * @return \Illuminate\Http\JsonResponse + */ + public function show(Customer $customer) + { + $this->authorize('view', $customer); + + return new CustomerResource($customer); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\Customer $customer + * @return \Illuminate\Http\JsonResponse + */ + public function update(Requests\CustomerRequest $request, Customer $customer) + { + $this->authorize('update', $customer); + + $customer = Customer::updateCustomer($request, $customer); + + if (is_string($customer)) { + return respondJson('you_cannot_edit_currency', 'Cannot change currency once transactions created'); + } + + return new CustomerResource($customer); + } + + /** + * Remove a list of Customers along side all their resources (ie. Estimates, Invoices, Payments and Addresses) + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteCustomersRequest $request) + { + $this->authorize('delete multiple customers'); + + Customer::deleteCustomers($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Dashboard/DashboardController.php b/crater/app/Http/Controllers/V1/Admin/Dashboard/DashboardController.php new file mode 100644 index 0000000..4e1d96d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Dashboard/DashboardController.php @@ -0,0 +1,166 @@ +header('company')); + + $this->authorize('view dashboard', $company); + + $invoice_totals = []; + $expense_totals = []; + $receipt_totals = []; + $net_income_totals = []; + + $i = 0; + $months = []; + $monthCounter = 0; + $fiscalYear = CompanySetting::getSetting('fiscal_year', $request->header('company')); + $startDate = Carbon::now(); + $start = Carbon::now(); + $end = Carbon::now(); + $terms = explode('-', $fiscalYear); + + if ($terms[0] <= $start->month) { + $startDate->month($terms[0])->startOfMonth(); + $start->month($terms[0])->startOfMonth(); + $end->month($terms[0])->endOfMonth(); + } else { + $startDate->subYear()->month($terms[0])->startOfMonth(); + $start->subYear()->month($terms[0])->startOfMonth(); + $end->subYear()->month($terms[0])->endOfMonth(); + } + + if ($request->has('previous_year')) { + $startDate->subYear()->startOfMonth(); + $start->subYear()->startOfMonth(); + $end->subYear()->endOfMonth(); + } + + while ($monthCounter < 12) { + array_push( + $invoice_totals, + Invoice::whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_total') + ); + array_push( + $expense_totals, + Expense::whereBetween( + 'expense_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_amount') + ); + array_push( + $receipt_totals, + Payment::whereBetween( + 'payment_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_amount') + ); + array_push( + $net_income_totals, + ($receipt_totals[$i] - $expense_totals[$i]) + ); + $i++; + array_push($months, $start->format('M')); + $monthCounter++; + $end->startOfMonth(); + $start->addMonth()->startOfMonth(); + $end->addMonth()->endOfMonth(); + } + + $start->subMonth()->endOfMonth(); + + $total_sales = Invoice::whereBetween( + 'invoice_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_total'); + + $total_receipts = Payment::whereBetween( + 'payment_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_amount'); + + $total_expenses = Expense::whereBetween( + 'expense_date', + [$startDate->format('Y-m-d'), $start->format('Y-m-d')] + ) + ->whereCompany() + ->sum('base_amount'); + + $total_net_income = (int)$total_receipts - (int)$total_expenses; + + $chart_data = [ + 'months' => $months, + 'invoice_totals' => $invoice_totals, + 'expense_totals' => $expense_totals, + 'receipt_totals' => $receipt_totals, + 'net_income_totals' => $net_income_totals, + ]; + + $total_customer_count = Customer::whereCompany()->count(); + $total_invoice_count = Invoice::whereCompany() + ->count(); + $total_estimate_count = Estimate::whereCompany()->count(); + $total_amount_due = Invoice::whereCompany() + ->sum('base_due_amount'); + + $recent_due_invoices = Invoice::with('customer') + ->whereCompany() + ->where('base_due_amount', '>', 0) + ->take(5) + ->latest() + ->get(); + $recent_estimates = Estimate::with('customer')->whereCompany()->take(5)->latest()->get(); + + return response()->json([ + 'total_amount_due' => $total_amount_due, + 'total_customer_count' => $total_customer_count, + 'total_invoice_count' => $total_invoice_count, + 'total_estimate_count' => $total_estimate_count, + + 'recent_due_invoices' => BouncerFacade::can('view-invoice', Invoice::class) ? $recent_due_invoices : [], + 'recent_estimates' => BouncerFacade::can('view-estimate', Estimate::class) ? $recent_estimates : [], + + 'chart_data' => $chart_data, + + 'total_sales' => $total_sales, + 'total_receipts' => $total_receipts, + 'total_expenses' => $total_expenses, + 'total_net_income' => $total_net_income, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/ChangeEstimateStatusController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/ChangeEstimateStatusController.php new file mode 100644 index 0000000..0e429ac --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/ChangeEstimateStatusController.php @@ -0,0 +1,28 @@ +authorize('send estimate', $estimate); + + $estimate->update($request->only('status')); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/ConvertEstimateController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/ConvertEstimateController.php new file mode 100644 index 0000000..3225d6e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/ConvertEstimateController.php @@ -0,0 +1,134 @@ +authorize('create', Invoice::class); + + $estimate->load(['items', 'items.taxes', 'customer', 'taxes']); + + $invoice_date = Carbon::now(); + $due_date = null; + + $dueDateEnabled = CompanySetting::getSetting( + 'invoice_set_due_date_automatically', + $request->header('company') + ); + + if ($dueDateEnabled === 'YES') { + $dueDateDays = CompanySetting::getSetting( + 'invoice_due_date_days', + $request->header('company') + ); + $due_date = Carbon::now()->addDays($dueDateDays)->format('Y-m-d'); + } + + $serial = (new SerialNumberFormatter()) + ->setModel($invoice) + ->setCompany($estimate->company_id) + ->setCustomer($estimate->customer_id) + ->setNextNumbers(); + + $templateName = $estimate->getInvoiceTemplateName(); + + $exchange_rate = $estimate->exchange_rate; + + $invoice = Invoice::create([ + 'creator_id' => Auth::id(), + 'invoice_date' => $invoice_date->format('Y-m-d'), + 'due_date' => $due_date, + 'invoice_number' => $serial->getNextNumber(), + 'sequence_number' => $serial->nextSequenceNumber, + 'customer_sequence_number' => $serial->nextCustomerSequenceNumber, + 'reference_number' => $serial->getNextNumber(), + 'customer_id' => $estimate->customer_id, + 'company_id' => $request->header('company'), + 'template_name' => $templateName, + 'status' => Invoice::STATUS_DRAFT, + 'paid_status' => Invoice::STATUS_UNPAID, + 'sub_total' => $estimate->sub_total, + 'discount' => $estimate->discount, + 'discount_type' => $estimate->discount_type, + 'discount_val' => $estimate->discount_val, + 'total' => $estimate->total, + 'due_amount' => $estimate->total, + 'tax_per_item' => $estimate->tax_per_item, + 'discount_per_item' => $estimate->discount_per_item, + 'tax' => $estimate->tax, + 'notes' => $estimate->notes, + 'exchange_rate' => $exchange_rate, + 'base_discount_val' => $estimate->discount_val * $exchange_rate, + 'base_sub_total' => $estimate->sub_total * $exchange_rate, + 'base_total' => $estimate->total * $exchange_rate, + 'base_tax' => $estimate->tax * $exchange_rate, + 'currency_id' => $estimate->currency_id, + 'sales_tax_type' => $estimate->sales_tax_type, + 'sales_tax_address_type' => $estimate->sales_tax_address_type, + ]); + + $invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id); + $invoice->save(); + $invoiceItems = $estimate->items->toArray(); + + foreach ($invoiceItems as $invoiceItem) { + $invoiceItem['company_id'] = $request->header('company'); + $invoiceItem['name'] = $invoiceItem['name']; + $estimateItem['exchange_rate'] = $exchange_rate; + $estimateItem['base_price'] = $invoiceItem['price'] * $exchange_rate; + $estimateItem['base_discount_val'] = $invoiceItem['discount_val'] * $exchange_rate; + $estimateItem['base_tax'] = $invoiceItem['tax'] * $exchange_rate; + $estimateItem['base_total'] = $invoiceItem['total'] * $exchange_rate; + + $item = $invoice->items()->create($invoiceItem); + + if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { + foreach ($invoiceItem['taxes'] as $tax) { + $tax['company_id'] = $request->header('company'); + + if ($tax['amount']) { + $item->taxes()->create($tax); + } + } + } + } + + if ($estimate->taxes) { + foreach ($estimate->taxes->toArray() as $tax) { + $tax['company_id'] = $request->header('company'); + $tax['exchange_rate'] = $exchange_rate; + $tax['base_amount'] = $tax['amount'] * $exchange_rate; + $tax['currency_id'] = $estimate->currency_id; + unset($tax['estimate_id']); + + $invoice->taxes()->create($tax); + } + } + + $estimate->checkForEstimateConvertAction(); + + $invoice = Invoice::find($invoice->id); + + return new InvoiceResource($invoice); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/EstimateTemplatesController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/EstimateTemplatesController.php new file mode 100644 index 0000000..ea6adbe --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/EstimateTemplatesController.php @@ -0,0 +1,27 @@ +authorize('viewAny', Estimate::class); + + $estimateTemplates = Estimate::estimateTemplates(); + + return response()->json([ + 'estimateTemplates' => $estimateTemplates + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/EstimatesController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/EstimatesController.php new file mode 100644 index 0000000..e43a4be --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/EstimatesController.php @@ -0,0 +1,77 @@ +authorize('viewAny', Estimate::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $estimates = Estimate::whereCompany() + ->join('customers', 'customers.id', '=', 'estimates.customer_id') + ->applyFilters($request->all()) + ->select('estimates.*', 'customers.name') + ->latest() + ->paginateData($limit); + + return (EstimateResource::collection($estimates)) + ->additional(['meta' => [ + 'estimate_total_count' => Estimate::whereCompany()->count(), + ]]); + } + + public function store(EstimatesRequest $request) + { + $this->authorize('create', Estimate::class); + + $estimate = Estimate::createEstimate($request); + + if ($request->has('estimateSend')) { + $estimate->send($request->title, $request->body); + } + + GenerateEstimatePdfJob::dispatch($estimate); + + return new EstimateResource($estimate); + } + + public function show(Request $request, Estimate $estimate) + { + $this->authorize('view', $estimate); + + return new EstimateResource($estimate); + } + + public function update(EstimatesRequest $request, Estimate $estimate) + { + $this->authorize('update', $estimate); + + $estimate = $estimate->updateEstimate($request); + + GenerateEstimatePdfJob::dispatch($estimate, true); + + return new EstimateResource($estimate); + } + + public function delete(DeleteEstimatesRequest $request) + { + $this->authorize('delete multiple estimates'); + + Estimate::destroy($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimateController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimateController.php new file mode 100644 index 0000000..4e9c823 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimateController.php @@ -0,0 +1,25 @@ +authorize('send estimate', $estimate); + + $response = $estimate->send($request->all()); + + return response()->json($response); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimatePreviewController.php b/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimatePreviewController.php new file mode 100644 index 0000000..af131c9 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Estimate/SendEstimatePreviewController.php @@ -0,0 +1,29 @@ +authorize('send estimate', $estimate); + + $markdown = new Markdown(view(), config('mail.markdown')); + + $data = $estimate->sendEstimateData($request->all()); + $data['url'] = $estimate->estimatePdfUrl; + + return $markdown->render('emails.send.estimate', ['data' => $data]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/ExchangeRate/ExchangeRateProviderController.php b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/ExchangeRateProviderController.php new file mode 100644 index 0000000..04bfc40 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/ExchangeRateProviderController.php @@ -0,0 +1,117 @@ +authorize('viewAny', ExchangeRateProvider::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $exchangeRateProviders = ExchangeRateProvider::whereCompany()->paginate($limit); + + return ExchangeRateProviderResource::collection($exchangeRateProviders); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(ExchangeRateProviderRequest $request) + { + $this->authorize('create', ExchangeRateProvider::class); + + $query = ExchangeRateProvider::checkActiveCurrencies($request); + + if (count($query) !== 0) { + return respondJson('currency_used', 'Currency used.'); + } + + $checkConverterApi = ExchangeRateProvider::checkExchangeRateProviderStatus($request); + + if ($checkConverterApi->status() == 200) { + $exchangeRateProvider = ExchangeRateProvider::createFromRequest($request); + + return new ExchangeRateProviderResource($exchangeRateProvider); + } + + return $checkConverterApi; + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Http\Response + */ + public function show(ExchangeRateProvider $exchangeRateProvider) + { + $this->authorize('view', $exchangeRateProvider); + + return new ExchangeRateProviderResource($exchangeRateProvider); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Http\Response + */ + public function update(ExchangeRateProviderRequest $request, ExchangeRateProvider $exchangeRateProvider) + { + $this->authorize('update', $exchangeRateProvider); + + $query = $exchangeRateProvider->checkUpdateActiveCurrencies($request); + + if (count($query) !== 0) { + return respondJson('currency_used', 'Currency used.'); + } + + $checkConverterApi = ExchangeRateProvider::checkExchangeRateProviderStatus($request); + + if ($checkConverterApi->status() == 200) { + $exchangeRateProvider->updateFromRequest($request); + + return new ExchangeRateProviderResource($exchangeRateProvider); + } + + return $checkConverterApi; + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Http\Response + */ + public function destroy(ExchangeRateProvider $exchangeRateProvider) + { + $this->authorize('delete', $exchangeRateProvider); + + if ($exchangeRateProvider->active == true) { + return respondJson('provider_active', 'Provider Active.'); + } + + $exchangeRateProvider->delete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetActiveProviderController.php b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetActiveProviderController.php new file mode 100644 index 0000000..5c17462 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetActiveProviderController.php @@ -0,0 +1,35 @@ +whereJsonContains('currencies', $currency->code) + ->where('active', true) + ->get(); + + if (count($query) !== 0) { + return response()->json([ + 'success' => true, + 'message' => 'provider_active', + ], 200); + } + + return response()->json([ + 'error' => 'no_active_provider', + ], 200); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetExchangeRateController.php b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetExchangeRateController.php new file mode 100644 index 0000000..b5820bb --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetExchangeRateController.php @@ -0,0 +1,57 @@ +header('company')); + $baseCurrency = Currency::findOrFail($settings['currency']); + + $query = ExchangeRateProvider::whereJsonContains('currencies', $currency->code) + ->where('active', true) + ->get() + ->toArray(); + + $exchange_rate = ExchangeRateLog::where('base_currency_id', $currency->id) + ->where('currency_id', $baseCurrency->id) + ->orderBy('created_at', 'desc') + ->value('exchange_rate'); + + if ($query) { + $filter = Arr::only($query[0], ['key', 'driver', 'driver_config']); + $exchange_rate_value = $this->getExchangeRate($filter, $currency->code, $baseCurrency->code); + + if ($exchange_rate_value->status() == 200) { + return $exchange_rate_value; + } + } + if ($exchange_rate) { + return response()->json([ + 'exchangeRate' => [$exchange_rate], + ], 200); + } + + return response()->json([ + 'error' => 'no_exchange_rate_available', + ], 200); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetSupportedCurrenciesController.php b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetSupportedCurrenciesController.php new file mode 100644 index 0000000..af80f25 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetSupportedCurrenciesController.php @@ -0,0 +1,26 @@ +authorize('viewAny', ExchangeRateProvider::class); + + return $this->getSupportedCurrencies($request); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetUsedCurrenciesController.php b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetUsedCurrenciesController.php new file mode 100644 index 0000000..84545c1 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/ExchangeRate/GetUsedCurrenciesController.php @@ -0,0 +1,55 @@ +authorize('viewAny', ExchangeRateProvider::class); + + $providerId = $request->provider_id; + + $activeExchangeRateProviders = ExchangeRateProvider::where('active', true) + ->whereCompany() + ->when($providerId, function ($query) use ($providerId) { + return $query->where('id', '<>', $providerId); + }) + ->pluck('currencies'); + $activeExchangeRateProvider = []; + + foreach ($activeExchangeRateProviders as $data) { + if (is_array($data)) { + for ($limit = 0; $limit < count($data); $limit++) { + $activeExchangeRateProvider[] = $data[$limit]; + } + } + } + + $allExchangeRateProviders = ExchangeRateProvider::whereCompany()->pluck('currencies'); + $allExchangeRateProvider = []; + + foreach ($allExchangeRateProviders as $data) { + if (is_array($data)) { + for ($limit = 0; $limit < count($data); $limit++) { + $allExchangeRateProvider[] = $data[$limit]; + } + } + } + + return response()->json([ + 'allUsedCurrencies' => $allExchangeRateProvider ? $allExchangeRateProvider : [], + 'activeUsedCurrencies' => $activeExchangeRateProvider ? $activeExchangeRateProvider : [], + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Expense/ExpenseCategoriesController.php b/crater/app/Http/Controllers/V1/Admin/Expense/ExpenseCategoriesController.php new file mode 100644 index 0000000..6c4c5d8 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Expense/ExpenseCategoriesController.php @@ -0,0 +1,96 @@ +authorize('viewAny', ExpenseCategory::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $categories = ExpenseCategory::applyFilters($request->all()) + ->whereCompany() + ->latest() + ->paginateData($limit); + + return ExpenseCategoryResource::collection($categories); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(ExpenseCategoryRequest $request) + { + $this->authorize('create', ExpenseCategory::class); + + $category = ExpenseCategory::create($request->getExpenseCategoryPayload()); + + return new ExpenseCategoryResource($category); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\ExpenseCategory $category + * @return \Illuminate\Http\Response + */ + public function show(ExpenseCategory $category) + { + $this->authorize('view', $category); + + return new ExpenseCategoryResource($category); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\ExpenseCategory $ExpenseCategory + * @return \Illuminate\Http\Response + */ + public function update(ExpenseCategoryRequest $request, ExpenseCategory $category) + { + $this->authorize('update', $category); + + $category->update($request->getExpenseCategoryPayload()); + + return new ExpenseCategoryResource($category); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\ExpensesCategory $category + * @return \Illuminate\Http\Response + */ + public function destroy(ExpenseCategory $category) + { + $this->authorize('delete', $category); + + if ($category->expenses() && $category->expenses()->count() > 0) { + return respondJson('expense_attached', 'Expense Attached'); + } + + $category->delete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Expense/ExpensesController.php b/crater/app/Http/Controllers/V1/Admin/Expense/ExpensesController.php new file mode 100644 index 0000000..9027a1f --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Expense/ExpensesController.php @@ -0,0 +1,93 @@ +authorize('viewAny', Expense::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $expenses = Expense::with('category', 'creator', 'fields') + ->whereCompany() + ->leftJoin('customers', 'customers.id', '=', 'expenses.customer_id') + ->join('expense_categories', 'expense_categories.id', '=', 'expenses.expense_category_id') + ->applyFilters($request->all()) + ->select('expenses.*', 'expense_categories.name', 'customers.name as user_name') + ->paginateData($limit); + + return (ExpenseResource::collection($expenses)) + ->additional(['meta' => [ + 'expense_total_count' => Expense::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Crater\Http\Requests\ExpenseRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(ExpenseRequest $request) + { + $this->authorize('create', Expense::class); + + $expense = Expense::createExpense($request); + + return new ExpenseResource($expense); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Expense $expense + * @return \Illuminate\Http\JsonResponse + */ + public function show(Expense $expense) + { + $this->authorize('view', $expense); + + return new ExpenseResource($expense); + } + + /** + * Update the specified resource in storage. + * + * @param \Crater\Http\Requests\ExpenseRequest $request + * @param \Crater\Models\Expense $expense + * @return \Illuminate\Http\JsonResponse + */ + public function update(ExpenseRequest $request, Expense $expense) + { + $this->authorize('update', $expense); + + $expense->updateExpense($request); + + return new ExpenseResource($expense); + } + + public function delete(DeleteExpensesRequest $request) + { + $this->authorize('delete multiple expenses'); + + Expense::destroy($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Expense/ShowReceiptController.php b/crater/app/Http/Controllers/V1/Admin/Expense/ShowReceiptController.php new file mode 100644 index 0000000..206b2c0 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Expense/ShowReceiptController.php @@ -0,0 +1,30 @@ +authorize('view', $expense); + + if ($expense) { + $media = $expense->getFirstMedia('receipts'); + + if ($media) { + return response()->file($media->getPath()); + } + + return respondJson('receipt_does_not_exist', 'Receipt does not exist.'); + } + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Expense/UploadReceiptController.php b/crater/app/Http/Controllers/V1/Admin/Expense/UploadReceiptController.php new file mode 100644 index 0000000..f3666a3 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Expense/UploadReceiptController.php @@ -0,0 +1,38 @@ +authorize('update', $expense); + + $data = json_decode($request->attachment_receipt); + + if ($data) { + if ($request->type === 'edit') { + $expense->clearMediaCollection('receipts'); + } + + $expense->addMediaFromBase64($data->data) + ->usingFileName($data->name) + ->toMediaCollection('receipts'); + } + + return response()->json([ + 'success' => 'Expense receipts uploaded successfully', + ], 200); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/BootstrapController.php b/crater/app/Http/Controllers/V1/Admin/General/BootstrapController.php new file mode 100644 index 0000000..b9c72ff --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/BootstrapController.php @@ -0,0 +1,78 @@ +user(); + $current_user_settings = $current_user->getAllSettings(); + + $main_menu = $this->generateMenu('main_menu', $current_user); + + $setting_menu = $this->generateMenu('setting_menu', $current_user); + + $companies = $current_user->companies; + + $current_company = Company::find($request->header('company')); + + if ((! $current_company) || ($current_company && ! $current_user->hasCompany($current_company->id))) { + $current_company = $current_user->companies()->first(); + } + + $current_company_settings = CompanySetting::getAllSettings($current_company->id); + + $current_company_currency = $current_company_settings->has('currency') + ? Currency::find($current_company_settings->get('currency')) + : Currency::first(); + + BouncerFacade::refreshFor($current_user); + + $global_settings = Setting::getSettings([ + 'api_token', + 'admin_portal_theme', + 'admin_portal_logo', + 'login_page_logo', + 'login_page_heading', + 'login_page_description', + 'admin_page_title', + 'copyright_text' + ]); + + return response()->json([ + 'current_user' => new UserResource($current_user), + 'current_user_settings' => $current_user_settings, + 'current_user_abilities' => $current_user->getAbilities(), + 'companies' => CompanyResource::collection($companies), + 'current_company' => new CompanyResource($current_company), + 'current_company_settings' => $current_company_settings, + 'current_company_currency' => $current_company_currency, + 'config' => config('crater'), + 'global_settings' => $global_settings, + 'main_menu' => $main_menu, + 'setting_menu' => $setting_menu, + 'modules' => Module::where('enabled', true)->pluck('name'), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/BulkExchangeRateController.php b/crater/app/Http/Controllers/V1/Admin/General/BulkExchangeRateController.php new file mode 100644 index 0000000..f0e82f6 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/BulkExchangeRateController.php @@ -0,0 +1,128 @@ +header('company')); + + if ($bulkExchangeRate == 'NO') { + if ($request->currencies) { + foreach ($request->currencies as $currency) { + $currency['exchange_rate'] = $currency['exchange_rate'] ?? 1; + + $invoices = Invoice::where('currency_id', $currency['id'])->get(); + + if ($invoices) { + foreach ($invoices as $invoice) { + $invoice->update([ + 'exchange_rate' => $currency['exchange_rate'], + 'base_discount_val' => $invoice->sub_total * $currency['exchange_rate'], + 'base_sub_total' => $invoice->sub_total * $currency['exchange_rate'], + 'base_total' => $invoice->total * $currency['exchange_rate'], + 'base_tax' => $invoice->tax * $currency['exchange_rate'], + 'base_due_amount' => $invoice->due_amount * $currency['exchange_rate'] + ]); + + $this->items($invoice); + } + } + + $estimates = Estimate::where('currency_id', $currency['id'])->get(); + + if ($estimates) { + foreach ($estimates as $estimate) { + $estimate->update([ + 'exchange_rate' => $currency['exchange_rate'], + 'base_discount_val' => $estimate->sub_total * $currency['exchange_rate'], + 'base_sub_total' => $estimate->sub_total * $currency['exchange_rate'], + 'base_total' => $estimate->total * $currency['exchange_rate'], + 'base_tax' => $estimate->tax * $currency['exchange_rate'] + ]); + + $this->items($estimate); + } + } + + $taxes = Tax::where('currency_id', $currency['id'])->get(); + + if ($taxes) { + foreach ($taxes as $tax) { + $tax->base_amount = $tax->base_amount * $currency['exchange_rate']; + $tax->save(); + } + } + + $payments = Payment::where('currency_id', $currency['id'])->get(); + + if ($payments) { + foreach ($payments as $payment) { + $payment->exchange_rate = $currency['exchange_rate']; + $payment->base_amount = $payment->amount * $currency['exchange_rate']; + $payment->save(); + } + } + } + } + + $settings = [ + 'bulk_exchange_rate_configured' => 'YES' + ]; + + CompanySetting::setSettings($settings, $request->header('company')); + + return response()->json([ + 'success' => true + ]); + } + + return response()->json([ + 'error' => false + ]); + } + + public function items($model) + { + foreach ($model->items as $item) { + $item->update([ + 'exchange_rate' => $model->exchange_rate, + 'base_discount_val' => $item->discount_val * $model->exchange_rate, + 'base_price' => $item->price * $model->exchange_rate, + 'base_tax' => $item->tax * $model->exchange_rate, + 'base_total' => $item->total * $model->exchange_rate + ]); + + $this->taxes($item); + } + + $this->taxes($model); + } + + public function taxes($model) + { + if ($model->taxes()->exists()) { + $model->taxes->map(function ($tax) use ($model) { + $tax->update([ + 'exchange_rate' => $model->exchange_rate, + 'base_amount' => $tax->amount * $model->exchange_rate, + ]); + }); + } + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/ConfigController.php b/crater/app/Http/Controllers/V1/Admin/General/ConfigController.php new file mode 100644 index 0000000..b0df1f6 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/ConfigController.php @@ -0,0 +1,22 @@ +json([ + $request->key => config('crater.'.$request->key), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/CountriesController.php b/crater/app/Http/Controllers/V1/Admin/General/CountriesController.php new file mode 100644 index 0000000..a6734db --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/CountriesController.php @@ -0,0 +1,24 @@ +get(); + + return CurrencyResource::collection($currencies); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/DateFormatsController.php b/crater/app/Http/Controllers/V1/Admin/General/DateFormatsController.php new file mode 100644 index 0000000..5cd594f --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/DateFormatsController.php @@ -0,0 +1,23 @@ +json([ + 'date_formats' => DateFormatter::get_list(), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/GetAllUsedCurrenciesController.php b/crater/app/Http/Controllers/V1/Admin/General/GetAllUsedCurrenciesController.php new file mode 100644 index 0000000..91b6b11 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/GetAllUsedCurrenciesController.php @@ -0,0 +1,37 @@ +pluck('currency_id')->toArray(); + + $taxes = Tax::where('exchange_rate', null)->pluck('currency_id')->toArray(); + + $estimates = Estimate::where('exchange_rate', null)->pluck('currency_id')->toArray(); + + $payments = Payment::where('exchange_rate', null)->pluck('currency_id')->toArray(); + + $currencies = array_merge($invoices, $taxes, $estimates, $payments); + + return response()->json([ + 'currencies' => Currency::whereIn('id', $currencies)->get() + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/NextNumberController.php b/crater/app/Http/Controllers/V1/Admin/General/NextNumberController.php new file mode 100644 index 0000000..6f458e8 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/NextNumberController.php @@ -0,0 +1,66 @@ +key; + $nextNumber = null; + $serial = (new SerialNumberFormatter()) + ->setCompany($request->header('company')) + ->setCustomer($request->userId); + + try { + switch ($key) { + case 'invoice': + $nextNumber = $serial->setModel($invoice) + ->setModelObject($request->model_id) + ->getNextNumber(); + + break; + + case 'estimate': + $nextNumber = $serial->setModel($estimate) + ->setModelObject($request->model_id) + ->getNextNumber(); + + break; + + case 'payment': + $nextNumber = $serial->setModel($payment) + ->setModelObject($request->model_id) + ->getNextNumber(); + + break; + + default: + return; + } + } catch (\Exception $exception) { + return response()->json([ + 'success' => false, + 'message' => $exception->getMessage() + ]); + } + + return response()->json([ + 'success' => true, + 'nextNumber' => $nextNumber, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/NotesController.php b/crater/app/Http/Controllers/V1/Admin/General/NotesController.php new file mode 100644 index 0000000..93858d3 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/NotesController.php @@ -0,0 +1,92 @@ +authorize('view notes'); + + $limit = $request->limit ?? 10; + + $notes = Note::latest() + ->whereCompany() + ->applyFilters($request->all()) + ->paginate($limit); + + return NoteResource::collection($notes); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(NotesRequest $request) + { + $this->authorize('manage notes'); + + $note = Note::create($request->getNotesPayload()); + + return new NoteResource($note); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Note $note + * @return \Illuminate\Http\Response + */ + public function show(Note $note) + { + $this->authorize('view notes'); + + return new NoteResource($note); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\Note $note + * @return \Illuminate\Http\Response + */ + public function update(NotesRequest $request, Note $note) + { + $this->authorize('manage notes'); + + $note->update($request->getNotesPayload()); + + return new NoteResource($note); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\Note $note + * @return \Illuminate\Http\Response + */ + public function destroy(Note $note) + { + $this->authorize('manage notes'); + + $note->delete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/NumberPlaceholdersController.php b/crater/app/Http/Controllers/V1/Admin/General/NumberPlaceholdersController.php new file mode 100644 index 0000000..8889837 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/NumberPlaceholdersController.php @@ -0,0 +1,30 @@ +format) { + $placeholders = SerialNumberFormatter::getPlaceholders($request->format); + } else { + $placeholders = []; + } + + return response()->json([ + 'success' => true, + 'placeholders' => $placeholders, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/SearchController.php b/crater/app/Http/Controllers/V1/Admin/General/SearchController.php new file mode 100644 index 0000000..6f02b93 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/SearchController.php @@ -0,0 +1,38 @@ +user(); + + $customers = Customer::applyFilters($request->only(['search'])) + ->whereCompany() + ->latest() + ->paginate(10); + + if ($user->isOwner()) { + $users = User::applyFilters($request->only(['search'])) + ->latest() + ->paginate(10); + } + + return response()->json([ + 'customers' => $customers, + 'users' => $users ?? [], + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/SearchUsersController.php b/crater/app/Http/Controllers/V1/Admin/General/SearchUsersController.php new file mode 100644 index 0000000..098bb2c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/SearchUsersController.php @@ -0,0 +1,27 @@ +authorize('create', User::class); + + $users = User::whereEmail($request->email) + ->latest() + ->paginate(10); + + return response()->json(['users' => $users]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/General/TimezonesController.php b/crater/app/Http/Controllers/V1/Admin/General/TimezonesController.php new file mode 100644 index 0000000..f7a2243 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/General/TimezonesController.php @@ -0,0 +1,23 @@ +json([ + 'time_zones' => TimeZones::get_list(), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/ChangeInvoiceStatusController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/ChangeInvoiceStatusController.php new file mode 100644 index 0000000..efc3043 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/ChangeInvoiceStatusController.php @@ -0,0 +1,36 @@ +authorize('send invoice', $invoice); + + if ($request->status == Invoice::STATUS_SENT) { + $invoice->status = Invoice::STATUS_SENT; + $invoice->sent = true; + $invoice->save(); + } elseif ($request->status == Invoice::STATUS_COMPLETED) { + $invoice->status = Invoice::STATUS_COMPLETED; + $invoice->paid_status = Invoice::STATUS_PAID; + $invoice->due_amount = 0; + $invoice->save(); + } + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/CloneInvoiceController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/CloneInvoiceController.php new file mode 100644 index 0000000..95c47f5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/CloneInvoiceController.php @@ -0,0 +1,133 @@ +authorize('create', Invoice::class); + + $date = Carbon::now(); + + $serial = (new SerialNumberFormatter()) + ->setModel($invoice) + ->setCompany($invoice->company_id) + ->setCustomer($invoice->customer_id) + ->setNextNumbers(); + + $due_date = null; + $dueDateEnabled = CompanySetting::getSetting( + 'invoice_set_due_date_automatically', + $request->header('company') + ); + + if ($dueDateEnabled === 'YES') { + $dueDateDays = CompanySetting::getSetting( + 'invoice_due_date_days', + $request->header('company') + ); + $due_date = Carbon::now()->addDays($dueDateDays)->format('Y-m-d'); + } + + $exchange_rate = $invoice->exchange_rate; + + $newInvoice = Invoice::create([ + 'invoice_date' => $date->format('Y-m-d'), + 'due_date' => $due_date, + 'invoice_number' => $serial->getNextNumber(), + 'sequence_number' => $serial->nextSequenceNumber, + 'customer_sequence_number' => $serial->nextCustomerSequenceNumber, + 'reference_number' => $invoice->reference_number, + 'customer_id' => $invoice->customer_id, + 'company_id' => $request->header('company'), + 'template_name' => $invoice->template_name, + 'status' => Invoice::STATUS_DRAFT, + 'paid_status' => Invoice::STATUS_UNPAID, + 'sub_total' => $invoice->sub_total, + 'discount' => $invoice->discount, + 'discount_type' => $invoice->discount_type, + 'discount_val' => $invoice->discount_val, + 'total' => $invoice->total, + 'due_amount' => $invoice->total, + 'tax_per_item' => $invoice->tax_per_item, + 'discount_per_item' => $invoice->discount_per_item, + 'tax' => $invoice->tax, + 'notes' => $invoice->notes, + 'exchange_rate' => $exchange_rate, + 'base_total' => $invoice->total * $exchange_rate, + 'base_discount_val' => $invoice->discount_val * $exchange_rate, + 'base_sub_total' => $invoice->sub_total * $exchange_rate, + 'base_tax' => $invoice->tax * $exchange_rate, + 'base_due_amount' => $invoice->total * $exchange_rate, + 'currency_id' => $invoice->currency_id, + 'sales_tax_type' => $invoice->sales_tax_type, + 'sales_tax_address_type' => $invoice->sales_tax_address_type, + ]); + + $newInvoice->unique_hash = Hashids::connection(Invoice::class)->encode($newInvoice->id); + $newInvoice->save(); + $invoice->load('items.taxes'); + + $invoiceItems = $invoice->items->toArray(); + + foreach ($invoiceItems as $invoiceItem) { + $invoiceItem['company_id'] = $request->header('company'); + $invoiceItem['name'] = $invoiceItem['name']; + $invoiceItem['exchange_rate'] = $exchange_rate; + $invoiceItem['base_price'] = $invoiceItem['price'] * $exchange_rate; + $invoiceItem['base_discount_val'] = $invoiceItem['discount_val'] * $exchange_rate; + $invoiceItem['base_tax'] = $invoiceItem['tax'] * $exchange_rate; + $invoiceItem['base_total'] = $invoiceItem['total'] * $exchange_rate; + + $item = $newInvoice->items()->create($invoiceItem); + + if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { + foreach ($invoiceItem['taxes'] as $tax) { + $tax['company_id'] = $request->header('company'); + + if ($tax['amount']) { + $item->taxes()->create($tax); + } + } + } + } + + if ($invoice->taxes) { + foreach ($invoice->taxes->toArray() as $tax) { + $tax['company_id'] = $request->header('company'); + $newInvoice->taxes()->create($tax); + } + } + + if ($invoice->fields()->exists()) { + $customFields = []; + + foreach ($invoice->fields as $data) { + $customFields[] = [ + 'id' => $data->custom_field_id, + 'value' => $data->defaultAnswer + ]; + } + + $newInvoice->addCustomFields($customFields); + } + + return new InvoiceResource($newInvoice); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/InvoiceTemplatesController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/InvoiceTemplatesController.php new file mode 100644 index 0000000..a9e0b97 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/InvoiceTemplatesController.php @@ -0,0 +1,27 @@ +authorize('viewAny', Invoice::class); + + $invoiceTemplates = Invoice::invoiceTemplates(); + + return response()->json([ + 'invoiceTemplates' => $invoiceTemplates, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php new file mode 100644 index 0000000..9aeefe5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php @@ -0,0 +1,111 @@ +authorize('viewAny', Invoice::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $invoices = Invoice::whereCompany() + ->join('customers', 'customers.id', '=', 'invoices.customer_id') + ->applyFilters($request->all()) + ->select('invoices.*', 'customers.name') + ->latest() + ->paginateData($limit); + + return (InvoiceResource::collection($invoices)) + ->additional(['meta' => [ + 'invoice_total_count' => Invoice::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(Requests\InvoicesRequest $request) + { + $this->authorize('create', Invoice::class); + + $invoice = Invoice::createInvoice($request); + + if ($request->has('invoiceSend')) { + $invoice->send($request->subject, $request->body); + } + + GenerateInvoicePdfJob::dispatch($invoice); + + return new InvoiceResource($invoice); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Invoice $invoice + * @return \Illuminate\Http\JsonResponse + */ + public function show(Request $request, Invoice $invoice) + { + $this->authorize('view', $invoice); + + return new InvoiceResource($invoice); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param Invoice $invoice + * @return \Illuminate\Http\JsonResponse + */ + public function update(Requests\InvoicesRequest $request, Invoice $invoice) + { + $this->authorize('update', $invoice); + + $invoice = $invoice->updateInvoice($request); + + if (is_string($invoice)) { + return respondJson($invoice, $invoice); + } + + GenerateInvoicePdfJob::dispatch($invoice, true); + + return new InvoiceResource($invoice); + } + + /** + * delete the specified resources in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteInvoiceRequest $request) + { + $this->authorize('delete multiple invoices'); + + Invoice::deleteInvoices($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoiceController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoiceController.php new file mode 100644 index 0000000..1ebcc4b --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoiceController.php @@ -0,0 +1,27 @@ +authorize('send invoice', $invoice); + + $invoice->send($request->all()); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoicePreviewController.php b/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoicePreviewController.php new file mode 100644 index 0000000..b01b920 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Invoice/SendInvoicePreviewController.php @@ -0,0 +1,29 @@ +authorize('send invoice', $invoice); + + $markdown = new Markdown(view(), config('mail.markdown')); + + $data = $invoice->sendInvoiceData($request->all()); + $data['url'] = $invoice->invoicePdfUrl; + + return $markdown->render('emails.send.invoice', ['data' => $data]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Item/ItemsController.php b/crater/app/Http/Controllers/V1/Admin/Item/ItemsController.php new file mode 100644 index 0000000..8e74c69 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Item/ItemsController.php @@ -0,0 +1,101 @@ +authorize('viewAny', Item::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $items = Item::whereCompany() + ->leftJoin('units', 'units.id', '=', 'items.unit_id') + ->applyFilters($request->all()) + ->select('items.*', 'units.name as unit_name') + ->latest() + ->paginateData($limit); + + return (ItemResource::collection($items)) + ->additional(['meta' => [ + 'tax_types' => TaxType::whereCompany()->latest()->get(), + 'item_total_count' => Item::whereCompany()->count(), + ]]); + } + + /** + * Create Item. + * + * @param Crater\Http\Requests\ItemsRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(Requests\ItemsRequest $request) + { + $this->authorize('create', Item::class); + + $item = Item::createItem($request); + + return new ItemResource($item); + } + + /** + * get an existing Item. + * + * @param Item $item + * @return \Illuminate\Http\JsonResponse + */ + public function show(Item $item) + { + $this->authorize('view', $item); + + return new ItemResource($item); + } + + /** + * Update an existing Item. + * + * @param Crater\Http\Requests\ItemsRequest $request + * @param \Crater\Models\Item $item + * @return \Illuminate\Http\JsonResponse + */ + public function update(Requests\ItemsRequest $request, Item $item) + { + $this->authorize('update', $item); + + $item = $item->updateItem($request); + + return new ItemResource($item); + } + + /** + * Delete a list of existing Items. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteItemsRequest $request) + { + $this->authorize('delete multiple items'); + + Item::destroy($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Item/UnitsController.php b/crater/app/Http/Controllers/V1/Admin/Item/UnitsController.php new file mode 100644 index 0000000..70a78ea --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Item/UnitsController.php @@ -0,0 +1,96 @@ +authorize('viewAny', Unit::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $units = Unit::applyFilters($request->all()) + ->whereCompany() + ->latest() + ->paginateData($limit); + + return UnitResource::collection($units); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(UnitRequest $request) + { + $this->authorize('create', Unit::class); + + $unit = Unit::create($request->getUnitPayload()); + + return new UnitResource($unit); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Unit $unit + * @return \Illuminate\Http\Response + */ + public function show(Unit $unit) + { + $this->authorize('view', $unit); + + return new UnitResource($unit); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\Unit $unit + * @return \Illuminate\Http\Response + */ + public function update(UnitRequest $request, Unit $unit) + { + $this->authorize('update', $unit); + + $unit->update($request->getUnitPayload()); + + return new UnitResource($unit); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\Unit $unit + * @return \Illuminate\Http\Response + */ + public function destroy(Unit $unit) + { + $this->authorize('delete', $unit); + + if ($unit->items()->exists()) { + return respondJson('items_attached', 'Items Attached'); + } + + $unit->delete(); + + return response()->json([ + 'success' => 'Unit deleted successfully', + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Mobile/AuthController.php b/crater/app/Http/Controllers/V1/Admin/Mobile/AuthController.php new file mode 100644 index 0000000..e7603e5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Mobile/AuthController.php @@ -0,0 +1,44 @@ +username)->first(); + + if (! $user || ! Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'email' => ['The provided credentials are incorrect.'], + ]); + } + + return response()->json([ + 'type' => 'Bearer', + 'token' => $user->createToken($request->device_name)->plainTextToken, + ]); + } + + public function logout(Request $request) + { + $request->user()->currentAccessToken()->delete(); + + return response()->json([ + 'success' => true, + ]); + } + + public function check() + { + return Auth::check(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/ApiTokenController.php b/crater/app/Http/Controllers/V1/Admin/Modules/ApiTokenController.php new file mode 100644 index 0000000..af9f2e4 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/ApiTokenController.php @@ -0,0 +1,25 @@ +authorize('manage modules'); + + $response = ModuleInstaller::checkToken($request->api_token); + + return $response; + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/CompleteModuleInstallationController.php b/crater/app/Http/Controllers/V1/Admin/Modules/CompleteModuleInstallationController.php new file mode 100644 index 0000000..ef29369 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/CompleteModuleInstallationController.php @@ -0,0 +1,27 @@ +authorize('manage modules'); + + $response = ModuleInstaller::complete($request->module, $request->version); + + return response()->json([ + 'success' => $response + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/CopyModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/CopyModuleController.php new file mode 100644 index 0000000..17bcf48 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/CopyModuleController.php @@ -0,0 +1,27 @@ +authorize('manage modules'); + + $response = ModuleInstaller::copyFiles($request->module, $request->path); + + return response()->json([ + 'success' => $response + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/DisableModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/DisableModuleController.php new file mode 100644 index 0000000..50bbb57 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/DisableModuleController.php @@ -0,0 +1,32 @@ +authorize('manage modules'); + + $module = ModelsModule::where('name', $module)->first(); + $module->update(['enabled' => false]); + $installedModule = Module::find($module->name); + $installedModule->disable(); + + ModuleDisabledEvent::dispatch($module); + + return response()->json(['success' => true]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/DownloadModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/DownloadModuleController.php new file mode 100644 index 0000000..73ae6fa --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/DownloadModuleController.php @@ -0,0 +1,25 @@ +authorize('manage modules'); + + $response = ModuleInstaller::download($request->module, $request->version); + + return response()->json($response); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/EnableModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/EnableModuleController.php new file mode 100644 index 0000000..1fa168b --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/EnableModuleController.php @@ -0,0 +1,32 @@ +authorize('manage modules'); + + $module = ModelsModule::where('name', $module)->first(); + $module->update(['enabled' => true]); + $installedModule = Module::find($module->name); + $installedModule->enable(); + + ModuleEnabledEvent::dispatch($module); + + return response()->json(['success' => true]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/ModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/ModuleController.php new file mode 100644 index 0000000..3984044 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/ModuleController.php @@ -0,0 +1,33 @@ +authorize('manage modules'); + + $response = ModuleInstaller::getModule($module); + + if (! $response->success) { + return response()->json($response); + } + + return (new ModuleResource($response->module)) + ->additional(['meta' => [ + 'modules' => ModuleResource::collection(collect($response->modules)) + ]]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/ModulesController.php b/crater/app/Http/Controllers/V1/Admin/Modules/ModulesController.php new file mode 100644 index 0000000..0cb35a2 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/ModulesController.php @@ -0,0 +1,25 @@ +authorize('manage modules'); + + $response = ModuleInstaller::getModules(); + + return $response; + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/UnzipModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/UnzipModuleController.php new file mode 100644 index 0000000..ddffd7c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/UnzipModuleController.php @@ -0,0 +1,28 @@ +authorize('manage modules'); + + $path = ModuleInstaller::unzip($request->module, $request->path); + + return response()->json([ + 'success' => true, + 'path' => $path + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Modules/UploadModuleController.php b/crater/app/Http/Controllers/V1/Admin/Modules/UploadModuleController.php new file mode 100644 index 0000000..34b1068 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Modules/UploadModuleController.php @@ -0,0 +1,25 @@ +authorize('manage modules'); + + $response = ModuleInstaller::upload($request); + + return response()->json($response); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Payment/PaymentMethodsController.php b/crater/app/Http/Controllers/V1/Admin/Payment/PaymentMethodsController.php new file mode 100644 index 0000000..53fbcb9 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Payment/PaymentMethodsController.php @@ -0,0 +1,101 @@ +authorize('viewAny', PaymentMethod::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $paymentMethods = PaymentMethod::applyFilters($request->all()) + ->where('type', PaymentMethod::TYPE_GENERAL) + ->whereCompany() + ->latest() + ->paginateData($limit); + + return PaymentMethodResource::collection($paymentMethods); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(PaymentMethodRequest $request) + { + $this->authorize('create', PaymentMethod::class); + + $paymentMethod = PaymentMethod::createPaymentMethod($request); + + return new PaymentMethodResource($paymentMethod); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return \Illuminate\Http\Response + */ + public function show(PaymentMethod $paymentMethod) + { + $this->authorize('view', $paymentMethod); + + return new PaymentMethodResource($paymentMethod); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return \Illuminate\Http\Response + */ + public function update(PaymentMethodRequest $request, PaymentMethod $paymentMethod) + { + $this->authorize('update', $paymentMethod); + + $paymentMethod->update($request->getPaymentMethodPayload()); + + return new PaymentMethodResource($paymentMethod); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return \Illuminate\Http\Response + */ + public function destroy(PaymentMethod $paymentMethod) + { + $this->authorize('delete', $paymentMethod); + + if ($paymentMethod->payments()->exists()) { + return respondJson('payments_attached', 'Payments Attached.'); + } + + if ($paymentMethod->expenses()->exists()) { + return respondJson('expenses_attached', 'Expenses Attached.'); + } + + $paymentMethod->delete(); + + return response()->json([ + 'success' => 'Payment method deleted successfully', + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Payment/PaymentsController.php b/crater/app/Http/Controllers/V1/Admin/Payment/PaymentsController.php new file mode 100644 index 0000000..85119f7 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Payment/PaymentsController.php @@ -0,0 +1,81 @@ +authorize('viewAny', Payment::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $payments = Payment::whereCompany() + ->join('customers', 'customers.id', '=', 'payments.customer_id') + ->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id') + ->leftJoin('payment_methods', 'payment_methods.id', '=', 'payments.payment_method_id') + ->applyFilters($request->all()) + ->select('payments.*', 'customers.name', 'invoices.invoice_number', 'payment_methods.name as payment_mode') + ->latest() + ->paginateData($limit); + + return (PaymentResource::collection($payments)) + ->additional(['meta' => [ + 'payment_total_count' => Payment::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(PaymentRequest $request) + { + $this->authorize('create', Payment::class); + + $payment = Payment::createPayment($request); + + return new PaymentResource($payment); + } + + public function show(Request $request, Payment $payment) + { + $this->authorize('view', $payment); + + return new PaymentResource($payment); + } + + public function update(PaymentRequest $request, Payment $payment) + { + $this->authorize('update', $payment); + + $payment = $payment->updatePayment($request); + + return new PaymentResource($payment); + } + + public function delete(DeletePaymentsRequest $request) + { + $this->authorize('delete multiple payments'); + + Payment::deletePayments($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentController.php b/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentController.php new file mode 100644 index 0000000..2bafed3 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentController.php @@ -0,0 +1,25 @@ +authorize('send payment', $payment); + + $response = $payment->send($request->all()); + + return response()->json($response); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentPreviewController.php b/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentPreviewController.php new file mode 100644 index 0000000..347b3cf --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Payment/SendPaymentPreviewController.php @@ -0,0 +1,29 @@ +authorize('send payment', $payment); + + $markdown = new Markdown(view(), config('mail.markdown')); + + $data = $payment->sendPaymentData($request->all()); + $data['url'] = $payment->paymentPdfUrl; + + return $markdown->render('emails.send.payment', ['data' => $data]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceController.php b/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceController.php new file mode 100644 index 0000000..10fc56c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceController.php @@ -0,0 +1,94 @@ +authorize('viewAny', RecurringInvoice::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $recurringInvoices = RecurringInvoice::whereCompany() + ->applyFilters($request->all()) + ->paginateData($limit); + + return (RecurringInvoiceResource::collection($recurringInvoices)) + ->additional(['meta' => [ + 'recurring_invoice_total_count' => RecurringInvoice::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(RecurringInvoiceRequest $request) + { + $this->authorize('create', RecurringInvoice::class); + + $recurringInvoice = RecurringInvoice::createFromRequest($request); + + return new RecurringInvoiceResource($recurringInvoice); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Http\Response + */ + public function show(RecurringInvoice $recurringInvoice) + { + $this->authorize('view', $recurringInvoice); + + return new RecurringInvoiceResource($recurringInvoice); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Http\Response + */ + public function update(RecurringInvoiceRequest $request, RecurringInvoice $recurringInvoice) + { + $this->authorize('update', $recurringInvoice); + + $recurringInvoice->updateFromRequest($request); + + return new RecurringInvoiceResource($recurringInvoice); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Http\Response + */ + public function delete(Request $request) + { + $this->authorize('delete multiple recurring invoices'); + + RecurringInvoice::deleteRecurringInvoice($request->ids); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceFrequencyController.php b/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceFrequencyController.php new file mode 100644 index 0000000..77cce3f --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/RecurringInvoice/RecurringInvoiceFrequencyController.php @@ -0,0 +1,20 @@ +frequency, $request->starts_at); + + return response()->json([ + 'success' => true, + 'next_invoice_at' => $nextInvoiceAt, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Report/CustomerSalesReportController.php b/crater/app/Http/Controllers/V1/Admin/Report/CustomerSalesReportController.php new file mode 100644 index 0000000..fd2740c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Report/CustomerSalesReportController.php @@ -0,0 +1,100 @@ +first(); + + $this->authorize('view report', $company); + + $locale = CompanySetting::getSetting('language', $company->id); + + App::setLocale($locale); + + $start = Carbon::createFromFormat('Y-m-d', $request->from_date); + $end = Carbon::createFromFormat('Y-m-d', $request->to_date); + + $customers = Customer::with(['invoices' => function ($query) use ($start, $end) { + $query->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }]) + ->where('company_id', $company->id) + ->applyInvoiceFilters($request->only(['from_date', 'to_date'])) + ->get(); + + $totalAmount = 0; + foreach ($customers as $customer) { + $customerTotalAmount = 0; + foreach ($customer->invoices as $invoice) { + $customerTotalAmount += $invoice->base_total; + } + $customer->totalAmount = $customerTotalAmount; + $totalAmount += $customerTotalAmount; + } + + $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); + $from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat); + $to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat); + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id)); + + $colors = [ + 'primary_text_color', + 'heading_text_color', + 'section_heading_text_color', + 'border_color', + 'body_text_color', + 'footer_text_color', + 'footer_total_color', + 'footer_bg_color', + 'date_text_color', + ]; + + $colorSettings = CompanySetting::whereIn('option', $colors) + ->whereCompany($company->id) + ->get(); + + view()->share([ + 'customers' => $customers, + 'totalAmount' => $totalAmount, + 'colorSettings' => $colorSettings, + 'company' => $company, + 'from_date' => $from_date, + 'to_date' => $to_date, + 'currency' => $currency, + ]); + + $pdf = PDF::loadView('app.pdf.reports.sales-customers'); + + if ($request->has('preview')) { + return view('app.pdf.reports.sales-customers'); + } + + if ($request->has('download')) { + return $pdf->download(); + } + + return $pdf->stream(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Report/ExpensesReportController.php b/crater/app/Http/Controllers/V1/Admin/Report/ExpensesReportController.php new file mode 100644 index 0000000..3e88e35 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Report/ExpensesReportController.php @@ -0,0 +1,85 @@ +first(); + + $this->authorize('view report', $company); + + $locale = CompanySetting::getSetting('language', $company->id); + + App::setLocale($locale); + + $expenseCategories = Expense::with('category') + ->whereCompanyId($company->id) + ->applyFilters($request->only(['from_date', 'to_date'])) + ->expensesAttributes() + ->get(); + $totalAmount = 0; + foreach ($expenseCategories as $category) { + $totalAmount += $category->total_amount; + } + + $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); + $from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat); + $to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat); + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id)); + + $colors = [ + 'primary_text_color', + 'heading_text_color', + 'section_heading_text_color', + 'border_color', + 'body_text_color', + 'footer_text_color', + 'footer_total_color', + 'footer_bg_color', + 'date_text_color', + ]; + $colorSettings = CompanySetting::whereIn('option', $colors) + ->whereCompany($company->id) + ->get(); + + view()->share([ + 'expenseCategories' => $expenseCategories, + 'colorSettings' => $colorSettings, + 'totalExpense' => $totalAmount, + 'company' => $company, + 'from_date' => $from_date, + 'to_date' => $to_date, + 'currency' => $currency, + ]); + $pdf = PDF::loadView('app.pdf.reports.expenses'); + + if ($request->has('preview')) { + return view('app.pdf.reports.expenses'); + } + + if ($request->has('download')) { + return $pdf->download(); + } + + return $pdf->stream(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Report/ItemSalesReportController.php b/crater/app/Http/Controllers/V1/Admin/Report/ItemSalesReportController.php new file mode 100644 index 0000000..c061626 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Report/ItemSalesReportController.php @@ -0,0 +1,85 @@ +first(); + + $this->authorize('view report', $company); + + $locale = CompanySetting::getSetting('language', $company->id); + + App::setLocale($locale); + + $items = InvoiceItem::whereCompany($company->id) + ->applyInvoiceFilters($request->only(['from_date', 'to_date'])) + ->itemAttributes() + ->get(); + + $totalAmount = 0; + foreach ($items as $item) { + $totalAmount += $item->total_amount; + } + + $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); + $from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat); + $to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat); + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id)); + + $colors = [ + 'primary_text_color', + 'heading_text_color', + 'section_heading_text_color', + 'border_color', + 'body_text_color', + 'footer_text_color', + 'footer_total_color', + 'footer_bg_color', + 'date_text_color', + ]; + $colorSettings = CompanySetting::whereIn('option', $colors) + ->whereCompany($company->id) + ->get(); + + view()->share([ + 'items' => $items, + 'colorSettings' => $colorSettings, + 'totalAmount' => $totalAmount, + 'company' => $company, + 'from_date' => $from_date, + 'to_date' => $to_date, + 'currency' => $currency, + ]); + $pdf = PDF::loadView('app.pdf.reports.sales-items'); + + if ($request->has('preview')) { + return view('app.pdf.reports.sales-items'); + } + + if ($request->has('download')) { + return $pdf->download(); + } + + return $pdf->stream(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Report/ProfitLossReportController.php b/crater/app/Http/Controllers/V1/Admin/Report/ProfitLossReportController.php new file mode 100644 index 0000000..cd225dc --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Report/ProfitLossReportController.php @@ -0,0 +1,94 @@ +first(); + + $this->authorize('view report', $company); + + $locale = CompanySetting::getSetting('language', $company->id); + + App::setLocale($locale); + + $paymentsAmount = Payment::whereCompanyId($company->id) + ->applyFilters($request->only(['from_date', 'to_date'])) + ->sum('base_amount'); + + $expenseCategories = Expense::with('category') + ->whereCompanyId($company->id) + ->applyFilters($request->only(['from_date', 'to_date'])) + ->expensesAttributes() + ->get(); + + $totalAmount = 0; + foreach ($expenseCategories as $category) { + $totalAmount += $category->total_amount; + } + + $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); + $from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat); + $to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat); + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id)); + + + $colors = [ + 'primary_text_color', + 'heading_text_color', + 'section_heading_text_color', + 'border_color', + 'body_text_color', + 'footer_text_color', + 'footer_total_color', + 'footer_bg_color', + 'date_text_color', + ]; + $colorSettings = CompanySetting::whereIn('option', $colors) + ->whereCompany($company->id) + ->get(); + + view()->share([ + 'company' => $company, + 'income' => $paymentsAmount, + 'expenseCategories' => $expenseCategories, + 'totalExpense' => $totalAmount, + 'colorSettings' => $colorSettings, + 'company' => $company, + 'from_date' => $from_date, + 'to_date' => $to_date, + 'currency' => $currency, + ]); + $pdf = PDF::loadView('app.pdf.reports.profit-loss'); + + if ($request->has('preview')) { + return view('app.pdf.reports.profit-loss'); + } + + if ($request->has('download')) { + return $pdf->download(); + } + + return $pdf->stream(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Report/TaxSummaryReportController.php b/crater/app/Http/Controllers/V1/Admin/Report/TaxSummaryReportController.php new file mode 100644 index 0000000..8fdcf61 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Report/TaxSummaryReportController.php @@ -0,0 +1,89 @@ +first(); + + $this->authorize('view report', $company); + + $locale = CompanySetting::getSetting('language', $company->id); + + App::setLocale($locale); + + $taxTypes = Tax::with('taxType', 'invoice', 'invoiceItem') + ->whereCompany($company->id) + ->whereInvoicesFilters($request->only(['from_date', 'to_date'])) + ->taxAttributes() + ->get(); + + $totalAmount = 0; + foreach ($taxTypes as $taxType) { + $totalAmount += $taxType->total_tax_amount; + } + + $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); + $from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat); + $to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat); + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id)); + + + $colors = [ + 'primary_text_color', + 'heading_text_color', + 'section_heading_text_color', + 'border_color', + 'body_text_color', + 'footer_text_color', + 'footer_total_color', + 'footer_bg_color', + 'date_text_color', + ]; + + $colorSettings = CompanySetting::whereIn('option', $colors) + ->whereCompany($company->id) + ->get(); + + view()->share([ + 'taxTypes' => $taxTypes, + 'totalTaxAmount' => $totalAmount, + 'colorSettings' => $colorSettings, + 'company' => $company, + 'from_date' => $from_date, + 'to_date' => $to_date, + 'currency' => $currency, + ]); + + $pdf = PDF::loadView('app.pdf.reports.tax-summary'); + + if ($request->has('preview')) { + return view('app.pdf.reports.tax-summary'); + } + + if ($request->has('download')) { + return $pdf->download(); + } + + return $pdf->stream(); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Role/AbilitiesController.php b/crater/app/Http/Controllers/V1/Admin/Role/AbilitiesController.php new file mode 100644 index 0000000..5223db7 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Role/AbilitiesController.php @@ -0,0 +1,20 @@ +json(['abilities' => config('abilities.abilities')]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Role/RolesController.php b/crater/app/Http/Controllers/V1/Admin/Role/RolesController.php new file mode 100644 index 0000000..e64abe0 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Role/RolesController.php @@ -0,0 +1,119 @@ +authorize('viewAny', Role::class); + + $roles = Role::when($request->has('orderByField'), function ($query) use ($request) { + return $query->orderBy($request['orderByField'], $request['orderBy']); + }) + ->when($request->company_id, function ($query) use ($request) { + return $query->where('scope', $request->company_id); + }) + ->get(); + + return RoleResource::collection($roles); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(RoleRequest $request) + { + $this->authorize('create', Role::class); + + $role = Role::create($request->getRolePayload()); + + $this->syncAbilities($request, $role); + + return new RoleResource($role); + } + + /** + * Display the specified resource. + * + * @param \Spatie\Permission\Models\Role $role + * @return \Illuminate\Http\Response + */ + public function show(Role $role) + { + $this->authorize('view', $role); + + return new RoleResource($role); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Spatie\Permission\Models\Role $role + * @return \Illuminate\Http\Response + */ + public function update(RoleRequest $request, Role $role) + { + $this->authorize('update', $role); + + $role->update($request->getRolePayload()); + + $this->syncAbilities($request, $role); + + return new RoleResource($role); + } + + /** + * Remove the specified resource from storage. + * + * @param \Spatie\Permission\Models\Role $role + * @return \Illuminate\Http\Response + */ + public function destroy(Role $role) + { + $this->authorize('delete', $role); + + $users = User::whereIs($role->name)->get()->toArray(); + + if (! empty($users)) { + return respondJson('role_attached_to_users', 'Roles Attached to user'); + } + + $role->delete(); + + return response()->json([ + 'success' => true + ]); + } + + private function syncAbilities(RoleRequest $request, $role) + { + foreach (config('abilities.abilities') as $ability) { + $check = array_search($ability['ability'], array_column($request->abilities, 'ability')); + if ($check !== false) { + BouncerFacade::allow($role)->to($ability['ability'], $ability['model']); + } else { + BouncerFacade::disallow($role)->to($ability['ability'], $ability['model']); + } + } + + return true; + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/CompanyController.php b/crater/app/Http/Controllers/V1/Admin/Settings/CompanyController.php new file mode 100644 index 0000000..2538129 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/CompanyController.php @@ -0,0 +1,125 @@ +user()); + } + + /** + * Update the Admin profile. + * Includes name, email and (or) password + * + * @param \Crater\Http\Requests\ProfileRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function updateProfile(ProfileRequest $request) + { + $user = $request->user(); + + $user->update($request->validated()); + + return new UserResource($user); + } + + /** + * Update Admin Company Details + * @param \Crater\Http\Requests\CompanyRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function updateCompany(CompanyRequest $request) + { + $company = Company::find($request->header('company')); + + $this->authorize('manage company', $company); + + $company->update($request->getCompanyPayload()); + + $company->address()->updateOrCreate(['company_id' => $company->id], $request->address); + + return new CompanyResource($company); + } + + /** + * Upload the company logo to storage. + * + * @param \Crater\Http\Requests\CompanyLogoRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function uploadCompanyLogo(CompanyLogoRequest $request) + { + $company = Company::find($request->header('company')); + + $this->authorize('manage company', $company); + + $data = json_decode($request->company_logo); + + if (isset($request->is_company_logo_removed) && (bool) $request->is_company_logo_removed) { + $company->clearMediaCollection('logo'); + } + if ($data) { + $company = Company::find($request->header('company')); + + if ($company) { + $company->clearMediaCollection('logo'); + + $company->addMediaFromBase64($data->data) + ->usingFileName($data->name) + ->toMediaCollection('logo'); + } + } + + return response()->json([ + 'success' => true, + ]); + } + + /** + * Upload the Admin Avatar to public storage. + * + * @param \Crater\Http\Requests\AvatarRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function uploadAvatar(AvatarRequest $request) + { + $user = auth()->user(); + + if (isset($request->is_admin_avatar_removed) && (bool) $request->is_admin_avatar_removed) { + $user->clearMediaCollection('admin_avatar'); + } + if ($user && $request->hasFile('admin_avatar')) { + $user->clearMediaCollection('admin_avatar'); + + $user->addMediaFromRequest('admin_avatar') + ->toMediaCollection('admin_avatar'); + } + + if ($user && $request->has('avatar')) { + $data = json_decode($request->avatar); + $user->clearMediaCollection('admin_avatar'); + + $user->addMediaFromBase64($data->data) + ->usingFileName($data->name) + ->toMediaCollection('admin_avatar'); + } + + return new UserResource($user); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/CompanyCurrencyCheckTransactionsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/CompanyCurrencyCheckTransactionsController.php new file mode 100644 index 0000000..1c9da06 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/CompanyCurrencyCheckTransactionsController.php @@ -0,0 +1,27 @@ +header('company')); + + $this->authorize('manage company', $company); + + return response()->json([ + 'has_transactions' => $company->hasTransactions(), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/DiskController.php b/crater/app/Http/Controllers/V1/Admin/Settings/DiskController.php new file mode 100644 index 0000000..54da868 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/DiskController.php @@ -0,0 +1,187 @@ +authorize('manage file disk'); + + $limit = $request->has('limit') ? $request->limit : 5; + $disks = FileDisk::applyFilters($request->all()) + ->latest() + ->paginateData($limit); + + return FileDiskResource::collection($disks); + } + + /** + * + * @param DiskEnvironmentRequest $request + * @return JsonResponse + */ + public function store(DiskEnvironmentRequest $request) + { + $this->authorize('manage file disk'); + + if (! FileDisk::validateCredentials($request->credentials, $request->driver)) { + return respondJson('invalid_credentials', 'Invalid Credentials.'); + } + + $disk = FileDisk::createDisk($request); + + return new FileDiskResource($disk); + } + + /** + * + * @param Request $request + * @param \Crater\Models\FileDisk $file_disk + * @return JsonResponse + */ + public function update(FileDisk $disk, Request $request) + { + $this->authorize('manage file disk'); + + $credentials = $request->credentials; + $driver = $request->driver; + + if ($credentials && $driver && $disk->type !== 'SYSTEM') { + if (! FileDisk::validateCredentials($credentials, $driver)) { + return respondJson('invalid_credentials', 'Invalid Credentials.'); + } + + $disk->updateDisk($request); + } elseif ($request->set_as_default) { + $disk->setAsDefaultDisk(); + } + + return new FileDiskResource($disk); + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function show($disk) + { + $this->authorize('manage file disk'); + + $diskData = []; + switch ($disk) { + case 'local': + $diskData = [ + 'root' => config('filesystems.disks.local.root'), + ]; + + break; + + + case 's3': + $diskData = [ + 'key' => '', + 'secret' => '', + 'region' => '', + 'bucket' => '', + 'root' => '', + ]; + + break; + + case 'doSpaces': + $diskData = [ + 'key' => '', + 'secret' => '', + 'region' => '', + 'bucket' => '', + 'endpoint' => '', + 'root' => '', + ]; + + break; + + case 'dropbox': + $diskData = [ + 'token' => '', + 'key' => '', + 'secret' => '', + 'app' => '', + 'root' => '', + ]; + + break; + } + + $data = array_merge($diskData); + + return response()->json($data); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\FileDisk $taxType + * @return \Illuminate\Http\Response + */ + public function destroy(FileDisk $disk) + { + $this->authorize('manage file disk'); + + if ($disk->setAsDefault() && $disk->type === 'SYSTEM') { + return respondJson('not_allowed', 'Not Allowed'); + } + + $disk->delete(); + + return response()->json([ + 'success' => true, + ]); + } + + /** + * + * @return JsonResponse + */ + public function getDiskDrivers() + { + $this->authorize('manage file disk'); + + $drivers = [ + [ + 'name' => 'Local', + 'value' => 'local', + ], + [ + 'name' => 'Amazon S3', + 'value' => 's3', + ], + [ + 'name' => 'Digital Ocean Spaces', + 'value' => 'doSpaces', + ], + [ + 'name' => 'Dropbox', + 'value' => 'dropbox', + ], + ]; + + $default = config('filesystems.default'); + + return response()->json([ + 'drivers' => $drivers, + 'default' => $default, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanyMailConfigurationController.php b/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanyMailConfigurationController.php new file mode 100644 index 0000000..6e8b6b7 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanyMailConfigurationController.php @@ -0,0 +1,25 @@ + config('mail.from.name'), + 'from_mail' => config('mail.from.address'), + ]; + + return response()->json($mailConfig); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanySettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanySettingsController.php new file mode 100644 index 0000000..f01c42f --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/GetCompanySettingsController.php @@ -0,0 +1,23 @@ +settings, $request->header('company')); + + return response()->json($settings); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/GetSettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/GetSettingsController.php new file mode 100644 index 0000000..7dea798 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/GetSettingsController.php @@ -0,0 +1,28 @@ +authorize('manage settings'); + + $setting = Setting::getSetting($request->key); + + return response()->json([ + $request->key => $setting + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/GetUserSettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/GetUserSettingsController.php new file mode 100644 index 0000000..4416257 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/GetUserSettingsController.php @@ -0,0 +1,22 @@ +user(); + + return response()->json($user->getSettings($request->settings)); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php b/crater/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php new file mode 100755 index 0000000..29794d5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php @@ -0,0 +1,107 @@ +environmentManager = $environmentManager; + } + + /** + * + * @param MailEnvironmentRequest $request + * @return JsonResponse + */ + public function saveMailEnvironment(MailEnvironmentRequest $request) + { + $this->authorize('manage email config'); + + $setting = Setting::getSetting('profile_complete'); + $results = $this->environmentManager->saveMailVariables($request); + + if ($setting !== 'COMPLETED') { + Setting::setSetting('profile_complete', 4); + } + + return response()->json($results); + } + + public function getMailEnvironment() + { + $this->authorize('manage email config'); + + $MailData = [ + 'mail_driver' => config('mail.driver'), + 'mail_host' => config('mail.host'), + 'mail_port' => config('mail.port'), + 'mail_username' => config('mail.username'), + 'mail_password' => config('mail.password'), + 'mail_encryption' => config('mail.encryption'), + 'from_name' => config('mail.from.name'), + 'from_mail' => config('mail.from.address'), + 'mail_mailgun_endpoint' => config('services.mailgun.endpoint'), + 'mail_mailgun_domain' => config('services.mailgun.domain'), + 'mail_mailgun_secret' => config('services.mailgun.secret'), + 'mail_ses_key' => config('services.ses.key'), + 'mail_ses_secret' => config('services.ses.secret'), + ]; + + + return response()->json($MailData); + } + + /** + * + * @return JsonResponse + */ + public function getMailDrivers() + { + $this->authorize('manage email config'); + + $drivers = [ + 'smtp', + 'mail', + 'sendmail', + 'mailgun', + 'ses', + ]; + + return response()->json($drivers); + } + + public function testEmailConfig(Request $request) + { + $this->authorize('manage email config'); + + $this->validate($request, [ + 'to' => 'required|email', + 'subject' => 'required', + 'message' => 'required', + ]); + + Mail::to($request->to)->send(new TestMail($request->subject, $request->message)); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/TaxTypesController.php b/crater/app/Http/Controllers/V1/Admin/Settings/TaxTypesController.php new file mode 100644 index 0000000..d5a3d1e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/TaxTypesController.php @@ -0,0 +1,97 @@ +authorize('viewAny', TaxType::class); + + $limit = $request->has('limit') ? $request->limit : 5; + + $taxTypes = TaxType::applyFilters($request->all()) + ->where('type', TaxType::TYPE_GENERAL) + ->whereCompany() + ->latest() + ->paginateData($limit); + + return TaxTypeResource::collection($taxTypes); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(TaxTypeRequest $request) + { + $this->authorize('create', TaxType::class); + + $taxType = TaxType::create($request->getTaxTypePayload()); + + return new TaxTypeResource($taxType); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\TaxType $taxType + * @return \Illuminate\Http\Response + */ + public function show(TaxType $taxType) + { + $this->authorize('view', $taxType); + + return new TaxTypeResource($taxType); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\TaxType $taxType + * @return \Illuminate\Http\Response + */ + public function update(TaxTypeRequest $request, TaxType $taxType) + { + $this->authorize('update', $taxType); + + $taxType->update($request->getTaxTypePayload()); + + return new TaxTypeResource($taxType); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\TaxType $taxType + * @return \Illuminate\Http\Response + */ + public function destroy(TaxType $taxType) + { + $this->authorize('delete', $taxType); + + if ($taxType->taxes() && $taxType->taxes()->count() > 0) { + return respondJson('taxes_attached', 'Taxes Attached.'); + } + + $taxType->delete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/UpdateCompanySettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateCompanySettingsController.php new file mode 100644 index 0000000..4bfcec8 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateCompanySettingsController.php @@ -0,0 +1,43 @@ +header('company')); + $this->authorize('manage company', $company); + + $data = $request->settings; + + if ( + Arr::exists($data, 'currency') && + (CompanySetting::getSetting('currency', $company->id) !== $data['currency']) && + $company->hasTransactions() + ) { + return response()->json([ + 'success' => false, + 'message' => 'Cannot update company currency after transactions are created.' + ]); + } + + CompanySetting::setSettings($data, $request->header('company')); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/UpdateSettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateSettingsController.php new file mode 100644 index 0000000..a6a297d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateSettingsController.php @@ -0,0 +1,29 @@ +authorize('manage settings'); + + Setting::setSettings($request->settings); + + return response()->json([ + 'success' => true, + $request->settings + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Settings/UpdateUserSettingsController.php b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateUserSettingsController.php new file mode 100644 index 0000000..9bf317b --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Settings/UpdateUserSettingsController.php @@ -0,0 +1,26 @@ +user(); + + $user->setSettings($request->settings); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/CheckVersionController.php b/crater/app/Http/Controllers/V1/Admin/Update/CheckVersionController.php new file mode 100644 index 0000000..5ae9622 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/CheckVersionController.php @@ -0,0 +1,33 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + set_time_limit(600); // 10 minutes + + $json = Updater::checkForUpdate(Setting::getSetting('version')); + + return response()->json($json); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/CopyFilesController.php b/crater/app/Http/Controllers/V1/Admin/Update/CopyFilesController.php new file mode 100644 index 0000000..81c2116 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/CopyFilesController.php @@ -0,0 +1,37 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + $request->validate([ + 'path' => 'required', + ]); + + $path = Updater::copyFiles($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/DeleteFilesController.php b/crater/app/Http/Controllers/V1/Admin/Update/DeleteFilesController.php new file mode 100644 index 0000000..750700c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/DeleteFilesController.php @@ -0,0 +1,34 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + if (isset($request->deleted_files) && ! empty($request->deleted_files)) { + Updater::deleteFiles($request->deleted_files); + } + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/DownloadUpdateController.php b/crater/app/Http/Controllers/V1/Admin/Update/DownloadUpdateController.php new file mode 100644 index 0000000..7005faf --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/DownloadUpdateController.php @@ -0,0 +1,37 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + $request->validate([ + 'version' => 'required', + ]); + + $path = Updater::download($request->version); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/FinishUpdateController.php b/crater/app/Http/Controllers/V1/Admin/Update/FinishUpdateController.php new file mode 100644 index 0000000..cb5de42 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/FinishUpdateController.php @@ -0,0 +1,35 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + $request->validate([ + 'installed' => 'required', + 'version' => 'required', + ]); + + $json = Updater::finishUpdate($request->installed, $request->version); + + return response()->json($json); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/MigrateUpdateController.php b/crater/app/Http/Controllers/V1/Admin/Update/MigrateUpdateController.php new file mode 100644 index 0000000..6b8e0aa --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/MigrateUpdateController.php @@ -0,0 +1,32 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + Updater::migrateUpdate(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/UnzipUpdateController.php b/crater/app/Http/Controllers/V1/Admin/Update/UnzipUpdateController.php new file mode 100644 index 0000000..e96279d --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/UnzipUpdateController.php @@ -0,0 +1,44 @@ +user()) || (! $request->user()->isOwner())) { + return response()->json([ + 'success' => false, + 'message' => 'You are not allowed to update this app.' + ], 401); + } + + $request->validate([ + 'path' => 'required', + ]); + + try { + $path = Updater::unzip($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'error' => $e->getMessage(), + ], 500); + } + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Update/UpdateController.php b/crater/app/Http/Controllers/V1/Admin/Update/UpdateController.php new file mode 100644 index 0000000..36ec300 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Update/UpdateController.php @@ -0,0 +1,102 @@ +authorize('manage update app'); + + $request->validate([ + 'version' => 'required', + ]); + + $path = Updater::download($request->version); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } + + public function unzip(Request $request) + { + $this->authorize('manage update app'); + + $request->validate([ + 'path' => 'required', + ]); + + try { + $path = Updater::unzip($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'error' => $e->getMessage(), + ], 500); + } + } + + public function copyFiles(Request $request) + { + $this->authorize('manage update app'); + + $request->validate([ + 'path' => 'required', + ]); + + $path = Updater::copyFiles($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path, + ]); + } + + public function migrate(Request $request) + { + $this->authorize('manage update app'); + + Updater::migrateUpdate(); + + return response()->json([ + 'success' => true, + ]); + } + + public function finishUpdate(Request $request) + { + $this->authorize('manage update app'); + + $request->validate([ + 'installed' => 'required', + 'version' => 'required', + ]); + + $json = Updater::finishUpdate($request->installed, $request->version); + + return response()->json($json); + } + + public function checkLatestVersion(Request $request) + { + $this->authorize('manage update app'); + + set_time_limit(600); // 10 minutes + + $json = Updater::checkForUpdate(Setting::getSetting('version')); + + return response()->json($json); + } +} diff --git a/crater/app/Http/Controllers/V1/Admin/Users/UsersController.php b/crater/app/Http/Controllers/V1/Admin/Users/UsersController.php new file mode 100644 index 0000000..0b08c92 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Admin/Users/UsersController.php @@ -0,0 +1,101 @@ +authorize('viewAny', User::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $user = $request->user(); + + $users = User::applyFilters($request->all()) + ->where('id', '<>', $user->id) + ->latest() + ->paginate($limit); + + return UserResource::collection($users) + ->additional(['meta' => [ + 'user_total_count' => User::count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\UserRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(UserRequest $request) + { + $this->authorize('create', User::class); + + $user = User::createFromRequest($request); + + return new UserResource($user); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\User $user + * @return \Illuminate\Http\JsonResponse + */ + public function show(User $user) + { + $this->authorize('view', $user); + + return new UserResource($user); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\UserRequest $request + * @param \Crater\Models\User $user + * @return \Illuminate\Http\JsonResponse + */ + public function update(UserRequest $request, User $user) + { + $this->authorize('update', $user); + + $user->updateFromRequest($request); + + return new UserResource($user); + } + + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteUserRequest $request) + { + $this->authorize('delete multiple users', User::class); + + if ($request->users) { + User::deleteUsers($request->users); + } + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Auth/ForgotPasswordController.php b/crater/app/Http/Controllers/V1/Customer/Auth/ForgotPasswordController.php new file mode 100644 index 0000000..93caec5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Auth/ForgotPasswordController.php @@ -0,0 +1,56 @@ +json([ + 'message' => 'Password reset email sent.', + 'data' => $response, + ]); + } + + /** + * Get the response for a failed password reset link. + * + * @param \Illuminate\Http\Request $request + * @param string $response + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + */ + protected function sendResetLinkFailedResponse(Request $request, $response) + { + return response('Email could not be sent to this email address.', 403); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Auth/LoginController.php b/crater/app/Http/Controllers/V1/Customer/Auth/LoginController.php new file mode 100644 index 0000000..8595644 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Auth/LoginController.php @@ -0,0 +1,45 @@ +email) + ->where('company_id', $company->id) + ->first(); + + if (! $user || ! Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'email' => ['The provided credentials are incorrect.'], + ]); + } + + if (! $user->enable_portal) { + throw ValidationException::withMessages([ + 'email' => ['Customer portal not available for this user.'], + ]); + } + + Auth::guard('customer')->login($user); + + return response()->json([ + 'success' => true + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Auth/ResetPasswordController.php b/crater/app/Http/Controllers/V1/Customer/Auth/ResetPasswordController.php new file mode 100644 index 0000000..4a8a027 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Auth/ResetPasswordController.php @@ -0,0 +1,83 @@ +json([ + 'message' => 'Password reset successfully.', + ]); + } + + /** + * Reset the given user's password. + * + * @param \Illuminate\Contracts\Auth\CanResetPassword $user + * @param string $password + * @return void + */ + protected function resetPassword($user, $password) + { + $user->password = $password; + + $user->setRememberToken(Str::random(60)); + + $user->save(); + + event(new PasswordReset($user)); + } + + /** + * Get the response for a failed password reset. + * + * @param \Illuminate\Http\Request $request + * @param string $response + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + */ + protected function sendResetFailedResponse(Request $request, $response) + { + return response('Failed, Invalid Token.', 403); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Estimate/AcceptEstimateController.php b/crater/app/Http/Controllers/V1/Customer/Estimate/AcceptEstimateController.php new file mode 100644 index 0000000..ec078a9 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Estimate/AcceptEstimateController.php @@ -0,0 +1,36 @@ +estimates() + ->whereCustomer(Auth::guard('customer')->id()) + ->where('id', $id) + ->first(); + + if (! $estimate) { + return response()->json(['error' => 'estimate_not_found'], 404); + } + + $estimate->update($request->only('status')); + + return new EstimateResource($estimate); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Estimate/EstimatesController.php b/crater/app/Http/Controllers/V1/Customer/Estimate/EstimatesController.php new file mode 100644 index 0000000..cf4d1cf --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Estimate/EstimatesController.php @@ -0,0 +1,67 @@ +has('limit') ? $request->limit : 10; + + $estimates = Estimate::with([ + 'items', + 'customer', + 'taxes', + 'creator', + ]) + ->where('status', '<>', 'DRAFT') + ->whereCustomer(Auth::guard('customer')->id()) + ->applyFilters($request->only([ + 'status', + 'estimate_number', + 'from_date', + 'to_date', + 'orderByField', + 'orderBy', + ])) + ->latest() + ->paginateData($limit); + + return (EstimateResource::collection($estimates)) + ->additional(['meta' => [ + 'estimateTotalCount' => Estimate::where('status', '<>', 'DRAFT')->whereCustomer(Auth::guard('customer')->id())->count(), + ]]); + } + + /** + * Display the specified resource. + * + * @param Estimate $estimate + * @return \Illuminate\Http\Response + */ + public function show(Company $company, $id) + { + $estimate = $company->estimates() + ->whereCustomer(Auth::guard('customer')->id()) + ->where('id', $id) + ->first(); + + if (! $estimate) { + return response()->json(['error' => 'estimate_not_found'], 404); + } + + return new EstimateResource($estimate); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/EstimatePdfController.php b/crater/app/Http/Controllers/V1/Customer/EstimatePdfController.php new file mode 100644 index 0000000..9cba60c --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/EstimatePdfController.php @@ -0,0 +1,53 @@ +mailable_id); + + if (! $emailLog->isExpired()) { + if ($estimate && ($estimate->status == Estimate::STATUS_SENT || $estimate->status == Estimate::STATUS_DRAFT)) { + $estimate->status = Estimate::STATUS_VIEWED; + $estimate->save(); + $notifyEstimateViewed = CompanySetting::getSetting( + 'notify_estimate_viewed', + $estimate->company_id + ); + + if ($notifyEstimateViewed == 'YES') { + $data['estimate'] = Estimate::findOrFail($estimate->id)->toArray(); + $data['user'] = Customer::find($estimate->customer_id)->toArray(); + $notificationEmail = CompanySetting::getSetting( + 'notification_email', + $estimate->company_id + ); + + \Mail::to($notificationEmail)->send(new EstimateViewedMail($data)); + } + } + + return $estimate->getGeneratedPDFOrStream('estimate'); + } + + abort(403, 'Link Expired.'); + } + + public function getEstimate(EmailLog $emailLog) + { + $estimate = Estimate::find($emailLog->mailable_id); + + return new EstimateResource($estimate); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Expense/ExpensesController.php b/crater/app/Http/Controllers/V1/Customer/Expense/ExpensesController.php new file mode 100644 index 0000000..837cf93 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Expense/ExpensesController.php @@ -0,0 +1,59 @@ +has('limit') ? $request->limit : 10; + + $expenses = Expense::with('category', 'creator', 'fields') + ->whereUser(Auth::guard('customer')->id()) + ->applyFilters($request->only([ + 'expense_category_id', + 'from_date', + 'to_date', + 'orderByField', + 'orderBy', + ])) + ->paginateData($limit); + + return (ExpenseResource::collection($expenses)) + ->additional(['meta' => [ + 'expenseTotalCount' => Expense::whereCustomer(Auth::guard('customer')->id())->count(), + ]]); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Expense $expense + * @return \Illuminate\Http\Response + */ + public function show(Company $company, $id) + { + $expense = $company->expenses() + ->whereUser(Auth::guard('customer')->id()) + ->where('id', $id) + ->first(); + + if (! $expense) { + return response()->json(['error' => 'expense_not_found'], 404); + } + + return new ExpenseResource($expense); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/General/BootstrapController.php b/crater/app/Http/Controllers/V1/Customer/General/BootstrapController.php new file mode 100644 index 0000000..2c4cca2 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/General/BootstrapController.php @@ -0,0 +1,40 @@ +user(); + + foreach (\Menu::get('customer_portal_menu')->items->toArray() as $data) { + if ($customer) { + $menu[] = [ + 'title' => $data->title, + 'link' => $data->link->path['url'], + ]; + } + } + + return (new CustomerResource($customer)) + ->additional(['meta' => [ + 'menu' => $menu, + 'current_customer_currency' => Currency::find($customer->currency_id), + 'modules' => Module::where('enabled', true)->pluck('name'), + ]]); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/General/DashboardController.php b/crater/app/Http/Controllers/V1/Customer/General/DashboardController.php new file mode 100644 index 0000000..aeb86bf --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/General/DashboardController.php @@ -0,0 +1,45 @@ +user(); + + $amountDue = Invoice::whereCustomer($user->id) + ->where('status', '<>', 'DRAFT') + ->sum('due_amount'); + $invoiceCount = Invoice::whereCustomer($user->id) + ->where('status', '<>', 'DRAFT') + ->count(); + $estimatesCount = Estimate::whereCustomer($user->id) + ->where('status', '<>', 'DRAFT') + ->count(); + $paymentCount = Payment::whereCustomer($user->id) + ->count(); + + return response()->json([ + 'due_amount' => $amountDue, + 'recentInvoices' => Invoice::whereCustomer($user->id)->where('status', '<>', 'DRAFT')->take(5)->latest()->get(), + 'recentEstimates' => Estimate::whereCustomer($user->id)->where('status', '<>', 'DRAFT')->take(5)->latest()->get(), + 'invoice_count' => $invoiceCount, + 'estimate_count' => $estimatesCount, + 'payment_count' => $paymentCount, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/General/ProfileController.php b/crater/app/Http/Controllers/V1/Customer/General/ProfileController.php new file mode 100644 index 0000000..711e120 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/General/ProfileController.php @@ -0,0 +1,49 @@ +user(); + + $customer->update($request->validated()); + + if (isset($request->is_customer_avatar_removed) && (bool) $request->is_customer_avatar_removed) { + $customer->clearMediaCollection('customer_avatar'); + } + if ($customer && $request->hasFile('customer_avatar')) { + $customer->clearMediaCollection('customer_avatar'); + + $customer->addMediaFromRequest('customer_avatar') + ->toMediaCollection('customer_avatar'); + } + + if ($request->billing !== null) { + $customer->shippingAddress()->delete(); + $customer->addresses()->create($request->getShippingAddress()); + } + + if ($request->shipping !== null) { + $customer->billingAddress()->delete(); + $customer->addresses()->create($request->getBillingAddress()); + } + + return new CustomerResource($customer); + } + + public function getUser(Request $request) + { + $customer = Auth::guard('customer')->user(); + + return new CustomerResource($customer); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Invoice/InvoicesController.php b/crater/app/Http/Controllers/V1/Customer/Invoice/InvoicesController.php new file mode 100644 index 0000000..f17cd76 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Invoice/InvoicesController.php @@ -0,0 +1,49 @@ +has('limit') ? $request->limit : 10; + + $invoices = Invoice::with(['items', 'customer', 'creator', 'taxes']) + ->where('status', '<>', 'DRAFT') + ->applyFilters($request->all()) + ->whereCustomer(Auth::guard('customer')->id()) + ->latest() + ->paginateData($limit); + + return (InvoiceResource::collection($invoices)) + ->additional(['meta' => [ + 'invoiceTotalCount' => Invoice::where('status', '<>', 'DRAFT')->whereCustomer(Auth::guard('customer')->id())->count(), + ]]); + } + + public function show(Company $company, $id) + { + $invoice = $company->invoices() + ->whereCustomer(Auth::guard('customer')->id()) + ->where('id', $id) + ->first(); + + if (! $invoice) { + return response()->json(['error' => 'invoice_not_found'], 404); + } + + return new InvoiceResource($invoice); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/InvoicePdfController.php b/crater/app/Http/Controllers/V1/Customer/InvoicePdfController.php new file mode 100644 index 0000000..ad94403 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/InvoicePdfController.php @@ -0,0 +1,61 @@ +mailable_id); + + if (! $emailLog->isExpired()) { + if ($invoice && ($invoice->status == Invoice::STATUS_SENT || $invoice->status == Invoice::STATUS_DRAFT)) { + $invoice->status = Invoice::STATUS_VIEWED; + $invoice->viewed = true; + $invoice->save(); + $notifyInvoiceViewed = CompanySetting::getSetting( + 'notify_invoice_viewed', + $invoice->company_id + ); + + if ($notifyInvoiceViewed == 'YES') { + $data['invoice'] = Invoice::findOrFail($invoice->id)->toArray(); + $data['user'] = Customer::find($invoice->customer_id)->toArray(); + $notificationEmail = CompanySetting::getSetting( + 'notification_email', + $invoice->company_id + ); + + \Mail::to($notificationEmail)->send(new InvoiceViewedMail($data)); + } + } + + if ($request->has('pdf')) { + return $invoice->getGeneratedPDFOrStream('invoice'); + } + + return view('app')->with([ + 'customer_logo' => get_company_setting('customer_portal_logo', $invoice->company_id), + 'current_theme' => get_company_setting('customer_portal_theme', $invoice->company_id) + ]); + } + + abort(403, 'Link Expired.'); + } + + public function getInvoice(EmailLog $emailLog) + { + $invoice = Invoice::find($emailLog->mailable_id); + + return new CustomerInvoiceResource($invoice); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Payment/PaymentMethodController.php b/crater/app/Http/Controllers/V1/Customer/Payment/PaymentMethodController.php new file mode 100644 index 0000000..ce15718 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Payment/PaymentMethodController.php @@ -0,0 +1,23 @@ +id)->get()); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/Payment/PaymentsController.php b/crater/app/Http/Controllers/V1/Customer/Payment/PaymentsController.php new file mode 100644 index 0000000..d34ba84 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/Payment/PaymentsController.php @@ -0,0 +1,61 @@ +has('limit') ? $request->limit : 10; + + $payments = Payment::with(['customer', 'invoice', 'paymentMethod', 'creator']) + ->whereCustomer(Auth::guard('customer')->id()) + ->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id') + ->applyFilters($request->only([ + 'payment_number', + 'payment_method_id', + 'orderByField', + 'orderBy', + ])) + ->select('payments.*', 'invoices.invoice_number') + ->latest() + ->paginateData($limit); + + return (PaymentResource::collection($payments)) + ->additional(['meta' => [ + 'paymentTotalCount' => Payment::whereCustomer(Auth::guard('customer')->id())->count(), + ]]); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\Payment $payment + * @return \Illuminate\Http\Response + */ + public function show(Company $company, $id) + { + $payment = $company->payments() + ->whereCustomer(Auth::guard('customer')->id()) + ->where('id', $id) + ->first(); + + if (! $payment) { + return response()->json(['error' => 'payment_not_found'], 404); + } + + return new PaymentResource($payment); + } +} diff --git a/crater/app/Http/Controllers/V1/Customer/PaymentPdfController.php b/crater/app/Http/Controllers/V1/Customer/PaymentPdfController.php new file mode 100644 index 0000000..d7fca06 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Customer/PaymentPdfController.php @@ -0,0 +1,28 @@ +isExpired()) { + return $emailLog->mailable->getGeneratedPDFOrStream('payment'); + } + + abort(403, 'Link Expired.'); + } + + public function getPayment(EmailLog $emailLog) + { + $payment = Payment::find($emailLog->mailable_id); + + return new PaymentResource($payment); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/AppDomainController.php b/crater/app/Http/Controllers/V1/Installation/AppDomainController.php new file mode 100644 index 0000000..30b871e --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/AppDomainController.php @@ -0,0 +1,32 @@ +saveDomainVariables($request); + + if (in_array('error', $results)) { + return response()->json($results); + } + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php b/crater/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php new file mode 100644 index 0000000..718c5b0 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php @@ -0,0 +1,88 @@ +environmentManager = $environmentManager; + } + + /** + * + * @param DatabaseEnvironmentRequest $request + */ + public function saveDatabaseEnvironment(DatabaseEnvironmentRequest $request) + { + Artisan::call('config:clear'); + Artisan::call('cache:clear'); + + $results = $this->environmentManager->saveDatabaseVariables($request); + + if (array_key_exists("success", $results)) { + Artisan::call('key:generate --force'); + Artisan::call('optimize:clear'); + Artisan::call('config:clear'); + Artisan::call('cache:clear'); + Artisan::call('storage:link'); + Artisan::call('migrate --seed --force'); + } + + return response()->json($results); + } + + public function getDatabaseEnvironment(Request $request) + { + $databaseData = []; + + switch ($request->connection) { + case 'sqlite': + $databaseData = [ + 'database_connection' => 'sqlite', + 'database_name' => database_path('database.sqlite'), + ]; + + break; + + case 'pgsql': + $databaseData = [ + 'database_connection' => 'pgsql', + 'database_host' => '127.0.0.1', + 'database_port' => 5432, + ]; + + break; + + case 'mysql': + $databaseData = [ + 'database_connection' => 'mysql', + 'database_host' => '127.0.0.1', + 'database_port' => 3306, + ]; + + break; + + } + + + return response()->json([ + 'config' => $databaseData, + 'success' => true, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/FilePermissionsController.php b/crater/app/Http/Controllers/V1/Installation/FilePermissionsController.php new file mode 100644 index 0000000..8740f71 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/FilePermissionsController.php @@ -0,0 +1,39 @@ +permissions = $checker; + } + + /** + * Display the permissions check page. + * + * @return JsonResponse + */ + public function permissions() + { + $permissions = $this->permissions->check( + config('installer.permissions') + ); + + return response()->json([ + 'permissions' => $permissions, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/FinishController.php b/crater/app/Http/Controllers/V1/Installation/FinishController.php new file mode 100644 index 0000000..2db8af1 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/FinishController.php @@ -0,0 +1,22 @@ +put('database_created', 'database_created'); + + return response()->json(['success' => true]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/LoginController.php b/crater/app/Http/Controllers/V1/Installation/LoginController.php new file mode 100644 index 0000000..a770ff5 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/LoginController.php @@ -0,0 +1,29 @@ +first(); + Auth::login($user); + + return response()->json([ + 'success' => true, + 'user' => $user, + 'company' => $user->companies()->first() + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/OnboardingWizardController.php b/crater/app/Http/Controllers/V1/Installation/OnboardingWizardController.php new file mode 100644 index 0000000..c353b84 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/OnboardingWizardController.php @@ -0,0 +1,46 @@ +has('database_created')) { + return response()->json([ + 'profile_complete' => 0, + ]); + } + + return response()->json([ + 'profile_complete' => Setting::getSetting('profile_complete'), + ]); + } + + public function updateStep(Request $request) + { + $setting = Setting::getSetting('profile_complete'); + + if ($setting === 'COMPLETED') { + return response()->json([ + 'profile_complete' => $setting, + ]); + } + + Setting::setSetting('profile_complete', $request->profile_complete); + + return response()->json([ + 'profile_complete' => Setting::getSetting('profile_complete'), + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Installation/RequirementsController.php b/crater/app/Http/Controllers/V1/Installation/RequirementsController.php new file mode 100755 index 0000000..cb2af82 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Installation/RequirementsController.php @@ -0,0 +1,44 @@ +requirements = $checker; + } + + /** + * Display the requirements page. + * + * @return JsonResponse + */ + public function requirements() + { + $phpSupportInfo = $this->requirements->checkPHPVersion( + config('installer.core.minPhpVersion') + ); + + $requirements = $this->requirements->check( + config('installer.requirements') + ); + + return response()->json([ + 'phpSupportInfo' => $phpSupportInfo, + 'requirements' => $requirements, + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/Modules/ScriptController.php b/crater/app/Http/Controllers/V1/Modules/ScriptController.php new file mode 100644 index 0000000..8b85553 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Modules/ScriptController.php @@ -0,0 +1,35 @@ + 'application/javascript', + ] + )->setLastModified(DateTime::createFromFormat('U', filemtime($path))); + } +} diff --git a/crater/app/Http/Controllers/V1/Modules/StyleController.php b/crater/app/Http/Controllers/V1/Modules/StyleController.php new file mode 100644 index 0000000..25645dd --- /dev/null +++ b/crater/app/Http/Controllers/V1/Modules/StyleController.php @@ -0,0 +1,35 @@ + 'text/css', + ] + )->setLastModified(DateTime::createFromFormat('U', filemtime($path))); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/DownloadInvoicePdfController.php b/crater/app/Http/Controllers/V1/PDF/DownloadInvoicePdfController.php new file mode 100644 index 0000000..fdd26ce --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/DownloadInvoicePdfController.php @@ -0,0 +1,22 @@ +id.'.pdf'); + + return response()->download($path); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/DownloadPaymentPdfController.php b/crater/app/Http/Controllers/V1/PDF/DownloadPaymentPdfController.php new file mode 100644 index 0000000..6e9c089 --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/DownloadPaymentPdfController.php @@ -0,0 +1,22 @@ +id.'.pdf'); + + return response()->download($path); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/DownloadReceiptController.php b/crater/app/Http/Controllers/V1/PDF/DownloadReceiptController.php new file mode 100644 index 0000000..51f6469 --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/DownloadReceiptController.php @@ -0,0 +1,39 @@ +authorize('view', $expense); + + if ($expense) { + $media = $expense->getFirstMedia('receipts'); + if ($media) { + $imagePath = $media->getPath(); + $response = \Response::download($imagePath, $media->file_name); + if (ob_get_contents()) { + ob_end_clean(); + } + + return $response; + } + } + + return response()->json([ + 'error' => 'receipt_not_found', + ]); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/EstimatePdfController.php b/crater/app/Http/Controllers/V1/PDF/EstimatePdfController.php new file mode 100644 index 0000000..64f65cc --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/EstimatePdfController.php @@ -0,0 +1,26 @@ +has('preview')) { + return $estimate->getPDFData(); + } + + + return $estimate->getGeneratedPDFOrStream('estimate'); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/InvoicePdfController.php b/crater/app/Http/Controllers/V1/PDF/InvoicePdfController.php new file mode 100644 index 0000000..6f3c47c --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/InvoicePdfController.php @@ -0,0 +1,25 @@ +has('preview')) { + return $invoice->getPDFData(); + } + + return $invoice->getGeneratedPDFOrStream('invoice'); + } +} diff --git a/crater/app/Http/Controllers/V1/PDF/PaymentPdfController.php b/crater/app/Http/Controllers/V1/PDF/PaymentPdfController.php new file mode 100644 index 0000000..fec5fa4 --- /dev/null +++ b/crater/app/Http/Controllers/V1/PDF/PaymentPdfController.php @@ -0,0 +1,25 @@ +has('preview')) { + return view('app.pdf.payment.payment'); + } + + return $payment->getGeneratedPDFOrStream('payment'); + } +} diff --git a/crater/app/Http/Controllers/V1/Webhook/CronJobController.php b/crater/app/Http/Controllers/V1/Webhook/CronJobController.php new file mode 100644 index 0000000..7acd8e6 --- /dev/null +++ b/crater/app/Http/Controllers/V1/Webhook/CronJobController.php @@ -0,0 +1,23 @@ +json(['success' => true]); + } +} diff --git a/crater/app/Http/Kernel.php b/crater/app/Http/Kernel.php new file mode 100644 index 0000000..093ae7b --- /dev/null +++ b/crater/app/Http/Kernel.php @@ -0,0 +1,92 @@ + [ + \Crater\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + // \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Crater\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + + 'api' => [ + EnsureFrontendRequestsAreStateful::class, + 'throttle:180,1', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + ]; + + /** + * The application's route middleware. + * + * These middleware may be assigned to groups or used individually. + * + * @var array + */ + protected $routeMiddleware = [ + 'auth' => \Crater\Http\Middleware\Authenticate::class, + 'bouncer' => \Crater\Http\Middleware\ScopeBouncer::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \Crater\Http\Middleware\RedirectIfAuthenticated::class, + 'customer' => \Crater\Http\Middleware\CustomerRedirectIfAuthenticated::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'install' => \Crater\Http\Middleware\InstallationMiddleware::class, + 'redirect-if-installed' => \Crater\Http\Middleware\RedirectIfInstalled::class, + 'redirect-if-unauthenticated' => \Crater\Http\Middleware\RedirectIfUnauthorized::class, + 'customer-guest' => \Crater\Http\Middleware\CustomerGuest::class, + 'company' => \Crater\Http\Middleware\CompanyMiddleware::class, + 'pdf-auth' => \Crater\Http\Middleware\PdfMiddleware::class, + 'cron-job' => \Crater\Http\Middleware\CronJobMiddleware::class, + 'customer-portal' => \Crater\Http\Middleware\CustomerPortalMiddleware::class, + ]; + + /** + * The priority-sorted list of middleware. + * + * This forces the listed middleware to always be in the given order. + * + * @var array + */ + protected $middlewarePriority = [ + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Crater\Http\Middleware\Authenticate::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Auth\Middleware\Authorize::class, + ]; +} diff --git a/crater/app/Http/Middleware/AdminMiddleware.php b/crater/app/Http/Middleware/AdminMiddleware.php new file mode 100644 index 0000000..83cfeec --- /dev/null +++ b/crater/app/Http/Middleware/AdminMiddleware.php @@ -0,0 +1,30 @@ +guest() || ! Auth::user()->isSuperAdminOrAdmin()) { + if ($request->ajax() || $request->wantsJson()) { + return response('Unauthorized.', 401); + } else { + return response()->json(['error' => 'user_is_not_admin'], 404); + } + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/Authenticate.php b/crater/app/Http/Middleware/Authenticate.php new file mode 100644 index 0000000..41de850 --- /dev/null +++ b/crater/app/Http/Middleware/Authenticate.php @@ -0,0 +1,21 @@ +expectsJson()) { + return route('login'); + } + } +} diff --git a/crater/app/Http/Middleware/CompanyMiddleware.php b/crater/app/Http/Middleware/CompanyMiddleware.php new file mode 100644 index 0000000..dd37eca --- /dev/null +++ b/crater/app/Http/Middleware/CompanyMiddleware.php @@ -0,0 +1,30 @@ +user(); + + if ((! $request->header('company')) || (! $user->hasCompany($request->header('company')))) { + $request->headers->set('company', $user->companies()->first()->id); + } + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/ConfigMiddleware.php b/crater/app/Http/Middleware/ConfigMiddleware.php new file mode 100644 index 0000000..2bcfc07 --- /dev/null +++ b/crater/app/Http/Middleware/ConfigMiddleware.php @@ -0,0 +1,33 @@ +has('database_created')) { + if ($request->has('file_disk_id')) { + $file_disk = FileDisk::find($request->file_disk_id); + } else { + $file_disk = FileDisk::whereSetAsDefault(true)->first(); + } + + if ($file_disk) { + $file_disk->setConfig(); + } + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/CronJobMiddleware.php b/crater/app/Http/Middleware/CronJobMiddleware.php new file mode 100644 index 0000000..379b3ab --- /dev/null +++ b/crater/app/Http/Middleware/CronJobMiddleware.php @@ -0,0 +1,25 @@ +header('x-authorization-token') && $request->header('x-authorization-token') == config('services.cron_job.auth_token')) { + return $next($request); + } + + return response()->json(['unauthorized'], 401); + } +} diff --git a/crater/app/Http/Middleware/CustomerPortalMiddleware.php b/crater/app/Http/Middleware/CustomerPortalMiddleware.php new file mode 100644 index 0000000..9ca09dc --- /dev/null +++ b/crater/app/Http/Middleware/CustomerPortalMiddleware.php @@ -0,0 +1,30 @@ +user(); + + if (! $user->enable_portal) { + Auth::guard('customer')->logout(); + + return response('Unauthorized.', 401); + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/EncryptCookies.php b/crater/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 0000000..4a5ba50 --- /dev/null +++ b/crater/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,24 @@ +has('database_created')) { + return redirect('/installation'); + } + + if (\Storage::disk('local')->has('database_created')) { + if (Setting::getSetting('profile_complete') !== 'COMPLETED') { + return redirect('/installation'); + } + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/PdfMiddleware.php b/crater/app/Http/Middleware/PdfMiddleware.php new file mode 100644 index 0000000..a348590 --- /dev/null +++ b/crater/app/Http/Middleware/PdfMiddleware.php @@ -0,0 +1,26 @@ +check() || Auth::guard('sanctum')->check() || Auth::guard('customer')->check()) { + return $next($request); + } + + return redirect('/login'); + } +} diff --git a/crater/app/Http/Middleware/RedirectIfAuthenticated.php b/crater/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 0000000..60b61a2 --- /dev/null +++ b/crater/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,27 @@ +check()) { + return redirect(RouteServiceProvider::HOME); + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/RedirectIfInstalled.php b/crater/app/Http/Middleware/RedirectIfInstalled.php new file mode 100644 index 0000000..7b0f331 --- /dev/null +++ b/crater/app/Http/Middleware/RedirectIfInstalled.php @@ -0,0 +1,27 @@ +has('database_created')) { + if (Setting::getSetting('profile_complete') === 'COMPLETED') { + return redirect('login'); + } + } + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/RedirectIfUnauthorized.php b/crater/app/Http/Middleware/RedirectIfUnauthorized.php new file mode 100644 index 0000000..79ba72e --- /dev/null +++ b/crater/app/Http/Middleware/RedirectIfUnauthorized.php @@ -0,0 +1,26 @@ +check()) { + return $next($request); + } + + return redirect('/login'); + } +} diff --git a/crater/app/Http/Middleware/ScopeBouncer.php b/crater/app/Http/Middleware/ScopeBouncer.php new file mode 100644 index 0000000..6d3f4d9 --- /dev/null +++ b/crater/app/Http/Middleware/ScopeBouncer.php @@ -0,0 +1,45 @@ +bouncer = $bouncer; + } + + /** + * Set the proper Bouncer scope for the incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + $user = $request->user(); + $tenantId = $request->header('company') + ? $request->header('company') + : $user->companies()->first()->id; + + $this->bouncer->scope()->to($tenantId); + + return $next($request); + } +} diff --git a/crater/app/Http/Middleware/TrimStrings.php b/crater/app/Http/Middleware/TrimStrings.php new file mode 100644 index 0000000..8336b47 --- /dev/null +++ b/crater/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,18 @@ + [ + 'nullable', + 'file', + 'mimes:gif,jpg,png', + 'max:20000' + ], + 'avatar' => [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +} diff --git a/crater/app/Http/Requests/BulkExchangeRateRequest.php b/crater/app/Http/Requests/BulkExchangeRateRequest.php new file mode 100644 index 0000000..3e52344 --- /dev/null +++ b/crater/app/Http/Requests/BulkExchangeRateRequest.php @@ -0,0 +1,39 @@ + [ + 'required' + ], + 'currencies.*.id' => [ + 'required', + 'numeric' + ], + 'currencies.*.exchange_rate' => [ + 'required' + ] + ]; + } +} diff --git a/crater/app/Http/Requests/CompaniesRequest.php b/crater/app/Http/Requests/CompaniesRequest.php new file mode 100644 index 0000000..5394592 --- /dev/null +++ b/crater/app/Http/Requests/CompaniesRequest.php @@ -0,0 +1,79 @@ + [ + 'required', + Rule::unique('companies'), + 'string' + ], + 'currency' => [ + 'required' + ], + 'address.name' => [ + 'nullable', + ], + 'address.address_street_1' => [ + 'nullable', + ], + 'address.address_street_2' => [ + 'nullable', + ], + 'address.city' => [ + 'nullable', + ], + 'address.state' => [ + 'nullable', + ], + 'address.country_id' => [ + 'required', + ], + 'address.zip' => [ + 'nullable', + ], + 'address.phone' => [ + 'nullable', + ], + 'address.fax' => [ + 'nullable', + ], + ]; + } + + public function getCompanyPayload() + { + return collect($this->validated()) + ->only([ + 'name' + ]) + ->merge([ + 'owner_id' => $this->user()->id, + 'slug' => Str::slug($this->name) + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/CompanyLogoRequest.php b/crater/app/Http/Requests/CompanyLogoRequest.php new file mode 100644 index 0000000..3ff0c68 --- /dev/null +++ b/crater/app/Http/Requests/CompanyLogoRequest.php @@ -0,0 +1,34 @@ + [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +} diff --git a/crater/app/Http/Requests/CompanyRequest.php b/crater/app/Http/Requests/CompanyRequest.php new file mode 100644 index 0000000..c86cd64 --- /dev/null +++ b/crater/app/Http/Requests/CompanyRequest.php @@ -0,0 +1,50 @@ + [ + 'required', + Rule::unique('companies')->ignore($this->header('company'), 'id'), + ], + 'slug' => [ + 'nullable' + ], + 'address.country_id' => [ + 'required', + ], + ]; + } + + public function getCompanyPayload() + { + return collect($this->validated()) + ->only([ + 'name', + 'slug' + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/CompanySettingRequest.php b/crater/app/Http/Requests/CompanySettingRequest.php new file mode 100644 index 0000000..306deae --- /dev/null +++ b/crater/app/Http/Requests/CompanySettingRequest.php @@ -0,0 +1,47 @@ + [ + 'required', + ], + 'time_zone' => [ + 'required', + ], + 'language' => [ + 'required', + ], + 'fiscal_year' => [ + 'required', + ], + 'moment_date_format' => [ + 'required', + ], + 'carbon_date_format' => [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/CustomFieldRequest.php b/crater/app/Http/Requests/CustomFieldRequest.php new file mode 100644 index 0000000..45ee7d4 --- /dev/null +++ b/crater/app/Http/Requests/CustomFieldRequest.php @@ -0,0 +1,37 @@ + 'required', + 'label' => 'required', + 'model_type' => 'required', + 'order' => 'required', + 'type' => 'required', + 'is_required' => 'required|boolean', + 'options' => 'array', + 'placeholder' => 'string|nullable', + ]; + } +} diff --git a/crater/app/Http/Requests/Customer/CustomerLoginRequest.php b/crater/app/Http/Requests/Customer/CustomerLoginRequest.php new file mode 100644 index 0000000..4d48c7b --- /dev/null +++ b/crater/app/Http/Requests/Customer/CustomerLoginRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + 'string' + ], + 'password' => [ + 'required', + 'string' + ] + ]; + } +} diff --git a/crater/app/Http/Requests/Customer/CustomerProfileRequest.php b/crater/app/Http/Requests/Customer/CustomerProfileRequest.php new file mode 100644 index 0000000..efaa3a5 --- /dev/null +++ b/crater/app/Http/Requests/Customer/CustomerProfileRequest.php @@ -0,0 +1,122 @@ + [ + 'nullable', + ], + 'password' => [ + 'nullable', + 'min:8', + ], + 'email' => [ + 'nullable', + 'email', + Rule::unique('customers')->where('company_id', $this->header('company'))->ignore(Auth::id(), 'id'), + ], + 'billing.name' => [ + 'nullable', + ], + 'billing.address_street_1' => [ + 'nullable', + ], + 'billing.address_street_2' => [ + 'nullable', + ], + 'billing.city' => [ + 'nullable', + ], + 'billing.state' => [ + 'nullable', + ], + 'billing.country_id' => [ + 'nullable', + ], + 'billing.zip' => [ + 'nullable', + ], + 'billing.phone' => [ + 'nullable', + ], + 'billing.fax' => [ + 'nullable', + ], + 'shipping.name' => [ + 'nullable', + ], + 'shipping.address_street_1' => [ + 'nullable', + ], + 'shipping.address_street_2' => [ + 'nullable', + ], + 'shipping.city' => [ + 'nullable', + ], + 'shipping.state' => [ + 'nullable', + ], + 'shipping.country_id' => [ + 'nullable', + ], + 'shipping.zip' => [ + 'nullable', + ], + 'shipping.phone' => [ + 'nullable', + ], + 'shipping.fax' => [ + 'nullable', + ], + 'customer_avatar' => [ + 'nullable', + 'file', + 'mimes:gif,jpg,png', + 'max:20000' + ] + ]; + } + + public function getShippingAddress() + { + return collect($this->shipping) + ->merge([ + 'type' => Address::SHIPPING_TYPE + ]) + ->toArray(); + } + + public function getBillingAddress() + { + return collect($this->billing) + ->merge([ + 'type' => Address::BILLING_TYPE + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/CustomerEstimateStatusRequest.php b/crater/app/Http/Requests/CustomerEstimateStatusRequest.php new file mode 100644 index 0000000..57f4d5a --- /dev/null +++ b/crater/app/Http/Requests/CustomerEstimateStatusRequest.php @@ -0,0 +1,33 @@ + [ + 'required', + 'in:ACCEPTED,REJECTED', + ] + ]; + } +} diff --git a/crater/app/Http/Requests/CustomerRequest.php b/crater/app/Http/Requests/CustomerRequest.php new file mode 100644 index 0000000..4b0a4b5 --- /dev/null +++ b/crater/app/Http/Requests/CustomerRequest.php @@ -0,0 +1,181 @@ + [ + 'required', + ], + 'email' => [ + 'email', + 'nullable', + Rule::unique('customers')->where('company_id', $this->header('company')) + ], + 'password' => [ + 'nullable', + ], + 'phone' => [ + 'nullable', + ], + 'company_name' => [ + 'nullable', + ], + 'contact_name' => [ + 'nullable', + ], + 'website' => [ + 'nullable', + ], + 'prefix' => [ + 'nullable', + ], + 'enable_portal' => [ + + 'boolean' + ], + 'currency_id' => [ + 'nullable', + ], + 'billing.name' => [ + 'nullable', + ], + 'billing.address_street_1' => [ + 'nullable', + ], + 'billing.address_street_2' => [ + 'nullable', + ], + 'billing.city' => [ + 'nullable', + ], + 'billing.state' => [ + 'nullable', + ], + 'billing.country_id' => [ + 'nullable', + ], + 'billing.zip' => [ + 'nullable', + ], + 'billing.phone' => [ + 'nullable', + ], + 'billing.fax' => [ + 'nullable', + ], + 'shipping.name' => [ + 'nullable', + ], + 'shipping.address_street_1' => [ + 'nullable', + ], + 'shipping.address_street_2' => [ + 'nullable', + ], + 'shipping.city' => [ + 'nullable', + ], + 'shipping.state' => [ + 'nullable', + ], + 'shipping.country_id' => [ + 'nullable', + ], + 'shipping.zip' => [ + 'nullable', + ], + 'shipping.phone' => [ + 'nullable', + ], + 'shipping.fax' => [ + 'nullable', + ] + ]; + + if ($this->isMethod('PUT') && $this->email != null) { + $rules['email'] = [ + 'email', + 'nullable', + Rule::unique('customers')->where('company_id', $this->header('company'))->ignore($this->route('customer')->id), + ]; + }; + + return $rules; + } + + public function getCustomerPayload() + { + return collect($this->validated()) + ->only([ + 'name', + 'email', + 'currency_id', + 'password', + 'phone', + 'prefix', + 'company_name', + 'contact_name', + 'website', + 'enable_portal', + 'estimate_prefix', + 'payment_prefix', + 'invoice_prefix', + ]) + ->merge([ + 'creator_id' => $this->user()->id, + 'company_id' => $this->header('company'), + ]) + ->toArray(); + } + + public function getShippingAddress() + { + return collect($this->shipping) + ->merge([ + 'type' => Address::SHIPPING_TYPE + ]) + ->toArray(); + } + + public function getBillingAddress() + { + return collect($this->billing) + ->merge([ + 'type' => Address::BILLING_TYPE + ]) + ->toArray(); + } + + public function hasAddress(array $address) + { + $data = Arr::where($address, function ($value, $key) { + return isset($value); + }); + + return $data; + } +} diff --git a/crater/app/Http/Requests/DatabaseEnvironmentRequest.php b/crater/app/Http/Requests/DatabaseEnvironmentRequest.php new file mode 100644 index 0000000..5bfae11 --- /dev/null +++ b/crater/app/Http/Requests/DatabaseEnvironmentRequest.php @@ -0,0 +1,76 @@ +get('database_connection')) { + case 'sqlite': + return [ + 'app_url' => [ + 'required', + 'url', + ], + 'database_connection' => [ + 'required', + 'string', + ], + 'database_name' => [ + 'required', + 'string', + ], + ]; + + break; + default: + return [ + 'app_url' => [ + 'required', + 'url', + ], + 'database_connection' => [ + 'required', + 'string', + ], + 'database_hostname' => [ + 'required', + 'string', + ], + 'database_port' => [ + 'required', + 'numeric', + ], + 'database_name' => [ + 'required', + 'string', + ], + 'database_username' => [ + 'required', + 'string', + ], + ]; + + break; + + } + } +} diff --git a/crater/app/Http/Requests/DeleteCustomersRequest.php b/crater/app/Http/Requests/DeleteCustomersRequest.php new file mode 100644 index 0000000..5e3c4ca --- /dev/null +++ b/crater/app/Http/Requests/DeleteCustomersRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('customers', 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeleteEstimatesRequest.php b/crater/app/Http/Requests/DeleteEstimatesRequest.php new file mode 100644 index 0000000..c1e65d0 --- /dev/null +++ b/crater/app/Http/Requests/DeleteEstimatesRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('estimates', 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeleteExpensesRequest.php b/crater/app/Http/Requests/DeleteExpensesRequest.php new file mode 100644 index 0000000..6a8c1c3 --- /dev/null +++ b/crater/app/Http/Requests/DeleteExpensesRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('expenses', 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeleteInvoiceRequest.php b/crater/app/Http/Requests/DeleteInvoiceRequest.php new file mode 100644 index 0000000..1651547 --- /dev/null +++ b/crater/app/Http/Requests/DeleteInvoiceRequest.php @@ -0,0 +1,40 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('invoices', 'id'), + new RelationNotExist(Invoice::class, 'payments'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeleteItemsRequest.php b/crater/app/Http/Requests/DeleteItemsRequest.php new file mode 100644 index 0000000..3c6bce7 --- /dev/null +++ b/crater/app/Http/Requests/DeleteItemsRequest.php @@ -0,0 +1,42 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('items', 'id'), + new RelationNotExist(Item::class, 'invoiceItems'), + new RelationNotExist(Item::class, 'estimateItems'), + new RelationNotExist(Item::class, 'taxes'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeletePaymentsRequest.php b/crater/app/Http/Requests/DeletePaymentsRequest.php new file mode 100644 index 0000000..29da745 --- /dev/null +++ b/crater/app/Http/Requests/DeletePaymentsRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + ], + 'ids.*' => [ + 'required', + Rule::exists('payments', 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DeleteUserRequest.php b/crater/app/Http/Requests/DeleteUserRequest.php new file mode 100644 index 0000000..5c6cf3d --- /dev/null +++ b/crater/app/Http/Requests/DeleteUserRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + ], + 'users.*' => [ + 'required', + Rule::exists('users', 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/DiskEnvironmentRequest.php b/crater/app/Http/Requests/DiskEnvironmentRequest.php new file mode 100644 index 0000000..f6e66a1 --- /dev/null +++ b/crater/app/Http/Requests/DiskEnvironmentRequest.php @@ -0,0 +1,122 @@ +get('driver')) { + case 's3': + $rules = [ + 'credentials.key' => [ + 'required', + 'string', + ], + 'credentials.secret' => [ + 'required', + 'string', + ], + 'credentials.region' => [ + 'required', + 'string', + ], + 'credentials.bucket' => [ + 'required', + 'string', + ], + 'credentials.root' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'doSpaces': + $rules = [ + 'credentials.key' => [ + 'required', + 'string', + ], + 'credentials.secret' => [ + 'required', + 'string', + ], + 'credentials.region' => [ + 'required', + 'string', + ], + 'credentials.bucket' => [ + 'required', + 'string', + ], + 'credentials.endpoint' => [ + 'required', + 'string', + ], + 'credentials.root' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'dropbox': + $rules = [ + 'credentials.token' => [ + 'required', + 'string', + ], + 'credentials.key' => [ + 'required', + 'string', + ], + 'credentials.secret' => [ + 'required', + 'string', + ], + 'credentials.app' => [ + 'required', + 'string', + ], + 'credentials.root' => [ + 'required', + 'string', + ], + ]; + + break; + } + + $defaultRules = [ + 'name' => [ + 'required', + ], + 'driver' => [ + 'required', + ], + ]; + + return array_merge($rules, $defaultRules); + } +} diff --git a/crater/app/Http/Requests/DomainEnvironmentRequest.php b/crater/app/Http/Requests/DomainEnvironmentRequest.php new file mode 100644 index 0000000..1e2d685 --- /dev/null +++ b/crater/app/Http/Requests/DomainEnvironmentRequest.php @@ -0,0 +1,32 @@ + [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/EstimatesRequest.php b/crater/app/Http/Requests/EstimatesRequest.php new file mode 100644 index 0000000..f43b7e4 --- /dev/null +++ b/crater/app/Http/Requests/EstimatesRequest.php @@ -0,0 +1,134 @@ + [ + 'required', + ], + 'expiry_date' => [ + 'nullable', + ], + 'customer_id' => [ + 'required', + ], + 'estimate_number' => [ + 'required', + Rule::unique('estimates')->where('company_id', $this->header('company')) + ], + 'exchange_rate' => [ + 'nullable' + ], + 'discount' => [ + 'required', + ], + 'discount_val' => [ + 'required', + ], + 'sub_total' => [ + 'required', + ], + 'total' => [ + 'required', + ], + 'tax' => [ + 'required', + ], + 'template_name' => [ + 'required' + ], + 'items' => [ + 'required', + 'array', + ], + 'items.*.description' => [ + 'nullable', + ], + 'items.*' => [ + 'required', + 'max:255', + ], + 'items.*.name' => [ + 'required', + ], + 'items.*.quantity' => [ + 'required', + ], + 'items.*.price' => [ + 'required', + ], + ]; + + $companyCurrency = CompanySetting::getSetting('currency', $this->header('company')); + + $customer = Customer::find($this->customer_id); + + if ($companyCurrency && $customer) { + if ((string)$customer->currency_id !== $companyCurrency) { + $rules['exchange_rate'] = [ + 'required', + ]; + }; + } + + if ($this->isMethod('PUT')) { + $rules['estimate_number'] = [ + 'required', + Rule::unique('estimates') + ->ignore($this->route('estimate')->id) + ->where('company_id', $this->header('company')), + ]; + } + + return $rules; + } + + public function getEstimatePayload() + { + $company_currency = CompanySetting::getSetting('currency', $this->header('company')); + $current_currency = $this->currency_id; + $exchange_rate = $company_currency != $current_currency ? $this->exchange_rate : 1; + $currency = Customer::find($this->customer_id)->currency_id; + + return collect($this->except('items', 'taxes')) + ->merge([ + 'creator_id' => $this->user()->id ?? null, + 'status' => $this->has('estimateSend') ? Estimate::STATUS_SENT : Estimate::STATUS_DRAFT, + 'company_id' => $this->header('company'), + 'tax_per_item' => CompanySetting::getSetting('tax_per_item', $this->header('company')) ?? 'NO ', + 'discount_per_item' => CompanySetting::getSetting('discount_per_item', $this->header('company')) ?? 'NO', + 'exchange_rate' => $exchange_rate, + 'base_discount_val' => $this->discount_val * $exchange_rate, + 'base_sub_total' => $this->sub_total * $exchange_rate, + 'base_total' => $this->total * $exchange_rate, + 'base_tax' => $this->tax * $exchange_rate, + 'currency_id' => $currency, + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/ExchangeRateLogRequest.php b/crater/app/Http/Requests/ExchangeRateLogRequest.php new file mode 100644 index 0000000..5f70504 --- /dev/null +++ b/crater/app/Http/Requests/ExchangeRateLogRequest.php @@ -0,0 +1,53 @@ + [ + 'required', + ], + 'currency_id' => [ + 'required' + ] + ]; + } + + public function getExchangeRateLogPayload() + { + $companyCurrency = CompanySetting::getSetting( + 'currency', + $this->header('company') + ); + + if ($this->currency_id !== $companyCurrency) { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company'), + 'base_currency_id' => $companyCurrency, + ]) + ->toArray(); + } + } +} diff --git a/crater/app/Http/Requests/ExchangeRateProviderRequest.php b/crater/app/Http/Requests/ExchangeRateProviderRequest.php new file mode 100644 index 0000000..773ca86 --- /dev/null +++ b/crater/app/Http/Requests/ExchangeRateProviderRequest.php @@ -0,0 +1,59 @@ + [ + 'required' + ], + 'key' => [ + 'required', + ], + 'currencies' => [ + 'nullable', + ], + 'currencies.*' => [ + 'nullable', + ], + 'driver_config' => [ + 'nullable' + ], + 'active' => [ + 'nullable', + 'boolean' + ], + ]; + + return $rules; + } + + public function getExchangeRateProviderPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company') + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/ExpenseCategoryRequest.php b/crater/app/Http/Requests/ExpenseCategoryRequest.php new file mode 100644 index 0000000..6ba3088 --- /dev/null +++ b/crater/app/Http/Requests/ExpenseCategoryRequest.php @@ -0,0 +1,44 @@ + [ + 'required', + ], + 'description' => [ + 'nullable', + ], + ]; + } + + public function getExpenseCategoryPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company') + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/ExpenseRequest.php b/crater/app/Http/Requests/ExpenseRequest.php new file mode 100644 index 0000000..a8baa72 --- /dev/null +++ b/crater/app/Http/Requests/ExpenseRequest.php @@ -0,0 +1,89 @@ +header('company')); + + $rules = [ + 'expense_date' => [ + 'required', + ], + 'expense_category_id' => [ + 'required', + ], + 'exchange_rate' => [ + 'nullable' + ], + 'payment_method_id' => [ + 'nullable', + ], + 'amount' => [ + 'required', + ], + 'customer_id' => [ + 'nullable', + ], + 'notes' => [ + 'nullable', + ], + 'currency_id' => [ + 'required' + ], + 'attachment_receipt' => [ + 'nullable', + 'file', + 'mimes:jpg,png,pdf,doc,docx,xls,xlsx,ppt,pptx', + 'max:20000' + ] + ]; + + if ($companyCurrency && $this->currency_id) { + if ($companyCurrency !== $this->currency_id) { + $rules['exchange_rate'] = [ + 'required', + ]; + }; + } + + return $rules; + } + + public function getExpensePayload() + { + $company_currency = CompanySetting::getSetting('currency', $this->header('company')); + $current_currency = $this->currency_id; + $exchange_rate = $company_currency != $current_currency ? $this->exchange_rate : 1; + + return collect($this->validated()) + ->merge([ + 'creator_id' => $this->user()->id, + 'company_id' => $this->header('company'), + 'exchange_rate' => $exchange_rate, + 'base_amount' => $this->amount * $exchange_rate, + 'currency_id' => $current_currency + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/GetSettingRequest.php b/crater/app/Http/Requests/GetSettingRequest.php new file mode 100644 index 0000000..edc7dd4 --- /dev/null +++ b/crater/app/Http/Requests/GetSettingRequest.php @@ -0,0 +1,33 @@ + [ + 'required', + 'string' + ] + ]; + } +} diff --git a/crater/app/Http/Requests/GetSettingsRequest.php b/crater/app/Http/Requests/GetSettingsRequest.php new file mode 100644 index 0000000..f0b7729 --- /dev/null +++ b/crater/app/Http/Requests/GetSettingsRequest.php @@ -0,0 +1,36 @@ + [ + 'required', + ], + 'settings.*' => [ + 'required', + 'string', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/InvoicesRequest.php b/crater/app/Http/Requests/InvoicesRequest.php new file mode 100644 index 0000000..84b4173 --- /dev/null +++ b/crater/app/Http/Requests/InvoicesRequest.php @@ -0,0 +1,137 @@ + [ + 'required', + ], + 'due_date' => [ + 'nullable', + ], + 'customer_id' => [ + 'required', + ], + 'invoice_number' => [ + 'required', + Rule::unique('invoices')->where('company_id', $this->header('company')) + ], + 'exchange_rate' => [ + 'nullable' + ], + 'discount' => [ + 'required', + ], + 'discount_val' => [ + 'required', + ], + 'sub_total' => [ + 'required', + ], + 'total' => [ + 'required', + ], + 'tax' => [ + 'required', + ], + 'template_name' => [ + 'required' + ], + 'items' => [ + 'required', + 'array', + ], + 'items.*' => [ + 'required', + 'max:255', + ], + 'items.*.description' => [ + 'nullable', + ], + 'items.*.name' => [ + 'required', + ], + 'items.*.quantity' => [ + 'required', + ], + 'items.*.price' => [ + 'required', + ], + ]; + + $companyCurrency = CompanySetting::getSetting('currency', $this->header('company')); + + $customer = Customer::find($this->customer_id); + + if ($customer && $companyCurrency) { + if ((string)$customer->currency_id !== $companyCurrency) { + $rules['exchange_rate'] = [ + 'required', + ]; + }; + } + + if ($this->isMethod('PUT')) { + $rules['invoice_number'] = [ + 'required', + Rule::unique('invoices') + ->ignore($this->route('invoice')->id) + ->where('company_id', $this->header('company')), + ]; + } + + return $rules; + } + + public function getInvoicePayload() + { + $company_currency = CompanySetting::getSetting('currency', $this->header('company')); + $current_currency = $this->currency_id; + $exchange_rate = $company_currency != $current_currency ? $this->exchange_rate : 1; + $currency = Customer::find($this->customer_id)->currency_id; + + return collect($this->except('items', 'taxes')) + ->merge([ + 'creator_id' => $this->user()->id ?? null, + 'status' => $this->has('invoiceSend') ? Invoice::STATUS_SENT : Invoice::STATUS_DRAFT, + 'paid_status' => Invoice::STATUS_UNPAID, + 'company_id' => $this->header('company'), + 'tax_per_item' => CompanySetting::getSetting('tax_per_item', $this->header('company')) ?? 'NO ', + 'discount_per_item' => CompanySetting::getSetting('discount_per_item', $this->header('company')) ?? 'NO', + 'due_amount' => $this->total, + 'exchange_rate' => $exchange_rate, + 'base_total' => $this->total * $exchange_rate, + 'base_discount_val' => $this->discount_val * $exchange_rate, + 'base_sub_total' => $this->sub_total * $exchange_rate, + 'base_tax' => $this->tax * $exchange_rate, + 'base_due_amount' => $this->total * $exchange_rate, + 'currency_id' => $currency, + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/ItemsRequest.php b/crater/app/Http/Requests/ItemsRequest.php new file mode 100644 index 0000000..ba98aaa --- /dev/null +++ b/crater/app/Http/Requests/ItemsRequest.php @@ -0,0 +1,41 @@ + [ + 'required', + ], + 'price' => [ + 'required', + ], + 'unit_id' => [ + 'nullable', + ], + 'description' => [ + 'nullable', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/LoginRequest.php b/crater/app/Http/Requests/LoginRequest.php new file mode 100644 index 0000000..3d363c8 --- /dev/null +++ b/crater/app/Http/Requests/LoginRequest.php @@ -0,0 +1,38 @@ + [ + 'required', + ], + 'password' => [ + 'required', + ], + 'device_name' => [ + 'required' + ], + ]; + } +} diff --git a/crater/app/Http/Requests/MailEnvironmentRequest.php b/crater/app/Http/Requests/MailEnvironmentRequest.php new file mode 100644 index 0000000..94ffcc4 --- /dev/null +++ b/crater/app/Http/Requests/MailEnvironmentRequest.php @@ -0,0 +1,152 @@ +get('mail_driver')) { + case 'smtp': + return [ + 'mail_driver' => [ + 'required', + 'string', + ], + 'mail_host' => [ + 'required', + 'string', + ], + 'mail_port' => [ + 'required', + ], + 'mail_encryption' => [ + 'required', + 'string', + ], + 'from_name' => [ + 'required', + 'string', + ], + 'from_mail' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'mailgun': + return [ + 'mail_driver' => [ + 'required', + 'string', + ], + 'mail_mailgun_domain' => [ + 'required', + 'string', + ], + 'mail_mailgun_secret' => [ + 'required', + 'string', + ], + 'mail_mailgun_endpoint' => [ + 'required', + 'string', + ], + 'from_name' => [ + 'required', + 'string', + ], + 'from_mail' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'ses': + return [ + 'mail_driver' => [ + 'required', + 'string', + ], + 'mail_host' => [ + 'required', + 'string', + ], + 'mail_port' => [ + 'required', + ], + 'mail_ses_key' => [ + 'required', + 'string', + ], + 'mail_ses_secret' => [ + 'required', + 'string', + ], + 'mail_encryption' => [ + 'nullable', + 'string', + ], + 'from_name' => [ + 'required', + 'string', + ], + 'from_mail' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'mail': + return [ + 'from_name' => [ + 'required', + 'string', + ], + 'from_mail' => [ + 'required', + 'string', + ], + ]; + + break; + + case 'sendmail': + return [ + 'from_name' => [ + 'required', + 'string', + ], + 'from_mail' => [ + 'required', + 'string', + ], + ]; + + break; + } + } +} diff --git a/crater/app/Http/Requests/NotesRequest.php b/crater/app/Http/Requests/NotesRequest.php new file mode 100644 index 0000000..faf0bea --- /dev/null +++ b/crater/app/Http/Requests/NotesRequest.php @@ -0,0 +1,63 @@ + [ + 'required' + ], + 'name' => [ + 'required', + Rule::unique('notes') + ->where('company_id', $this->header('company')) + ->where('type', $this->type) + ], + 'notes' => [ + 'required' + ], + ]; + + if ($this->isMethod('PUT')) { + $rules['name'] = [ + 'required', + Rule::unique('notes') + ->ignore($this->route('note')->id) + ->where('type', $this->type) + ->where('company_id', $this->header('company')) + ]; + } + + return $rules; + } + + public function getNotesPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company') + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/PaymentMethodRequest.php b/crater/app/Http/Requests/PaymentMethodRequest.php new file mode 100644 index 0000000..210471c --- /dev/null +++ b/crater/app/Http/Requests/PaymentMethodRequest.php @@ -0,0 +1,57 @@ + [ + 'required', + Rule::unique('payment_methods') + ->where('company_id', $this->header('company')), + ], + ]; + + if ($this->getMethod() == 'PUT') { + $data['name'] = [ + 'required', + Rule::unique('payment_methods') + ->ignore($this->route('payment_method'), 'id') + ->where('company_id', $this->header('company')), + ]; + } + + return $data; + } + + public function getPaymentMethodPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company'), + 'type' => PaymentMethod::TYPE_GENERAL, + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/PaymentRequest.php b/crater/app/Http/Requests/PaymentRequest.php new file mode 100644 index 0000000..aa3afb7 --- /dev/null +++ b/crater/app/Http/Requests/PaymentRequest.php @@ -0,0 +1,98 @@ + [ + 'required', + ], + 'customer_id' => [ + 'required', + ], + 'exchange_rate' => [ + 'nullable' + ], + 'amount' => [ + 'required', + ], + 'payment_number' => [ + 'required', + Rule::unique('payments')->where('company_id', $this->header('company')) + ], + 'invoice_id' => [ + 'nullable', + ], + 'payment_method_id' => [ + 'nullable', + ], + 'notes' => [ + 'nullable', + ], + ]; + + if ($this->isMethod('PUT')) { + $rules['payment_number'] = [ + 'required', + Rule::unique('payments') + ->ignore($this->route('payment')->id) + ->where('company_id', $this->header('company')), + ]; + } + + $companyCurrency = CompanySetting::getSetting('currency', $this->header('company')); + + $customer = Customer::find($this->customer_id); + + if ($customer && $companyCurrency) { + if ((string)$customer->currency_id !== $companyCurrency) { + $rules['exchange_rate'] = [ + 'required', + ]; + }; + } + + return $rules; + } + + public function getPaymentPayload() + { + $company_currency = CompanySetting::getSetting('currency', $this->header('company')); + $current_currency = $this->currency_id; + $exchange_rate = $company_currency != $current_currency ? $this->exchange_rate : 1; + $currency = Customer::find($this->customer_id)->currency_id; + + return collect($this->validated()) + ->merge([ + 'creator_id' => $this->user()->id, + 'company_id' => $this->header('company'), + 'exchange_rate' => $exchange_rate, + 'base_amount' => $this->amount * $exchange_rate, + 'currency_id' => $currency + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/ProfileRequest.php b/crater/app/Http/Requests/ProfileRequest.php new file mode 100644 index 0000000..d2ddca1 --- /dev/null +++ b/crater/app/Http/Requests/ProfileRequest.php @@ -0,0 +1,43 @@ + [ + 'required', + ], + 'password' => [ + 'nullable', + 'min:8', + ], + 'email' => [ + 'required', + 'email', + Rule::unique('users')->ignore(Auth::id(), 'id'), + ], + ]; + } +} diff --git a/crater/app/Http/Requests/RecurringInvoiceRequest.php b/crater/app/Http/Requests/RecurringInvoiceRequest.php new file mode 100644 index 0000000..f624156 --- /dev/null +++ b/crater/app/Http/Requests/RecurringInvoiceRequest.php @@ -0,0 +1,121 @@ +header('company')); + + $rules = [ + 'starts_at' => [ + 'required' + ], + 'send_automatically' => [ + 'required', + 'boolean' + ], + 'customer_id' => [ + 'required' + ], + 'exchange_rate' => [ + 'nullable' + ], + 'discount' => [ + 'required', + ], + 'discount_val' => [ + 'required', + ], + 'sub_total' => [ + 'required', + ], + 'total' => [ + 'required', + ], + 'tax' => [ + 'required', + ], + 'status' => [ + 'required' + ], + 'exchange_rate' => [ + 'nullable' + ], + 'frequency' => [ + 'required' + ], + 'limit_by' => [ + 'required' + ], + 'limit_count' => [ + 'required_if:limit_by,COUNT', + ], + 'limit_date' => [ + 'required_if:limit_by,DATE', + ], + 'items' => [ + 'required' + ], + 'items.*' => [ + 'required' + ] + ]; + + $customer = Customer::find($this->customer_id); + + if ($customer && $companyCurrency) { + if ((string)$customer->currency_id !== $companyCurrency) { + $rules['exchange_rate'] = [ + 'required', + ]; + }; + } + + return $rules; + } + + public function getRecurringInvoicePayload() + { + $company_currency = CompanySetting::getSetting('currency', $this->header('company')); + $current_currency = $this->currency_id; + $exchange_rate = $company_currency != $current_currency ? $this->exchange_rate : 1; + $currency = Customer::find($this->customer_id)->currency_id; + + $nextInvoiceAt = RecurringInvoice::getNextInvoiceDate($this->frequency, $this->starts_at); + + return collect($this->except('items', 'taxes')) + ->merge([ + 'creator_id' => $this->user()->id, + 'company_id' => $this->header('company'), + 'next_invoice_at' => $nextInvoiceAt, + 'tax_per_item' => CompanySetting::getSetting('tax_per_item', $this->header('company')) ?? 'NO ', + 'discount_per_item' => CompanySetting::getSetting('discount_per_item', $this->header('company')) ?? 'NO', + 'due_amount' => $this->total, + 'exchange_rate' => $exchange_rate, + 'currency_id' => $currency + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/Request.php b/crater/app/Http/Requests/Request.php new file mode 100644 index 0000000..e7df98c --- /dev/null +++ b/crater/app/Http/Requests/Request.php @@ -0,0 +1,10 @@ + [ + 'required', + 'string', + Rule::unique('roles')->where('scope', $this->header('company')) + ], + 'abilities' => [ + 'required' + ], + 'abilities.*' => [ + 'required' + ] + ]; + + if ($this->getMethod() == 'PUT') { + $rules['name'] = [ + 'required', + 'string', + Rule::unique('roles') + ->ignore($this->route('role')->id, 'id') + ->where('scope', $this->header('company')) + ]; + } + + return $rules; + } + + public function getRolePayload() + { + return collect($this->except('abilities')) + ->merge([ + 'scope' => $this->header('company'), + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/SendEstimatesRequest.php b/crater/app/Http/Requests/SendEstimatesRequest.php new file mode 100644 index 0000000..d782648 --- /dev/null +++ b/crater/app/Http/Requests/SendEstimatesRequest.php @@ -0,0 +1,41 @@ + [ + 'required', + ], + 'body' => [ + 'required', + ], + 'from' => [ + 'required', + ], + 'to' => [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/SendInvoiceRequest.php b/crater/app/Http/Requests/SendInvoiceRequest.php new file mode 100644 index 0000000..077e804 --- /dev/null +++ b/crater/app/Http/Requests/SendInvoiceRequest.php @@ -0,0 +1,41 @@ + [ + 'required', + ], + 'subject' => [ + 'required', + ], + 'from' => [ + 'required', + ], + 'to' => [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/SendPaymentRequest.php b/crater/app/Http/Requests/SendPaymentRequest.php new file mode 100644 index 0000000..87745b9 --- /dev/null +++ b/crater/app/Http/Requests/SendPaymentRequest.php @@ -0,0 +1,41 @@ + [ + 'required', + ], + 'body' => [ + 'required', + ], + 'from' => [ + 'required', + ], + 'to' => [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/SettingKeyRequest.php b/crater/app/Http/Requests/SettingKeyRequest.php new file mode 100644 index 0000000..582b3c0 --- /dev/null +++ b/crater/app/Http/Requests/SettingKeyRequest.php @@ -0,0 +1,32 @@ + [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/SettingRequest.php b/crater/app/Http/Requests/SettingRequest.php new file mode 100644 index 0000000..2cca4d4 --- /dev/null +++ b/crater/app/Http/Requests/SettingRequest.php @@ -0,0 +1,32 @@ + [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/TaxTypeRequest.php b/crater/app/Http/Requests/TaxTypeRequest.php new file mode 100644 index 0000000..bc55076 --- /dev/null +++ b/crater/app/Http/Requests/TaxTypeRequest.php @@ -0,0 +1,71 @@ + [ + 'required', + Rule::unique('tax_types') + ->where('type', TaxType::TYPE_GENERAL) + ->where('company_id', $this->header('company')) + ], + 'percent' => [ + 'required', + ], + 'description' => [ + 'nullable', + ], + 'compound_tax' => [ + 'nullable', + ], + 'collective_tax' => [ + 'nullable', + ], + ]; + + if ($this->isMethod('PUT')) { + $rules['name'] = [ + 'required', + Rule::unique('tax_types') + ->ignore($this->route('tax_type')->id) + ->where('type', TaxType::TYPE_GENERAL) + ->where('company_id', $this->header('company')) + ]; + } + + return $rules; + } + + public function getTaxTypePayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company'), + 'type' => TaxType::TYPE_GENERAL + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/UnitRequest.php b/crater/app/Http/Requests/UnitRequest.php new file mode 100644 index 0000000..c57d6e2 --- /dev/null +++ b/crater/app/Http/Requests/UnitRequest.php @@ -0,0 +1,55 @@ + [ + 'required', + Rule::unique('units') + ->where('company_id', $this->header('company')), + ], + ]; + + if ($this->getMethod() == 'PUT') { + $data['name'] = [ + 'required', + Rule::unique('units') + ->ignore($this->route('unit'), 'id') + ->where('company_id', $this->header('company')), + ]; + } + + return $data; + } + + public function getUnitPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company') + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Requests/UnzipUpdateRequest.php b/crater/app/Http/Requests/UnzipUpdateRequest.php new file mode 100644 index 0000000..1557d6f --- /dev/null +++ b/crater/app/Http/Requests/UnzipUpdateRequest.php @@ -0,0 +1,37 @@ + [ + 'required', + 'regex:/^[\.\/\w\-]+$/' + ], + 'module' => [ + 'required', + 'string' + ] + ]; + } +} diff --git a/crater/app/Http/Requests/UpdateSettingsRequest.php b/crater/app/Http/Requests/UpdateSettingsRequest.php new file mode 100644 index 0000000..80447e7 --- /dev/null +++ b/crater/app/Http/Requests/UpdateSettingsRequest.php @@ -0,0 +1,32 @@ + [ + 'required', + ], + ]; + } +} diff --git a/crater/app/Http/Requests/UploadExpenseReceiptRequest.php b/crater/app/Http/Requests/UploadExpenseReceiptRequest.php new file mode 100644 index 0000000..2e79e79 --- /dev/null +++ b/crater/app/Http/Requests/UploadExpenseReceiptRequest.php @@ -0,0 +1,34 @@ + [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +} diff --git a/crater/app/Http/Requests/UploadModuleRequest.php b/crater/app/Http/Requests/UploadModuleRequest.php new file mode 100644 index 0000000..da59294 --- /dev/null +++ b/crater/app/Http/Requests/UploadModuleRequest.php @@ -0,0 +1,40 @@ + [ + 'required', + 'file', + 'mimes:zip', + 'max:20000' + ], + 'module' => [ + 'required', + 'string', + 'max:100' + ] + ]; + } +} diff --git a/crater/app/Http/Requests/UserRequest.php b/crater/app/Http/Requests/UserRequest.php new file mode 100644 index 0000000..523e734 --- /dev/null +++ b/crater/app/Http/Requests/UserRequest.php @@ -0,0 +1,77 @@ + [ + 'required', + ], + 'email' => [ + 'required', + 'email', + Rule::unique('users'), + ], + 'phone' => [ + 'nullable', + ], + 'password' => [ + 'required', + 'min:8', + ], + 'companies' => [ + 'required', + ], + 'companies.*.id' => [ + 'required', + ], + 'companies.*.role' => [ + 'required', + ], + ]; + + if ($this->getMethod() == 'PUT') { + $rules['email'] = [ + 'required', + 'email', + Rule::unique('users')->ignore($this->user), + ]; + $rules['password'] = [ + 'nullable', + 'min:8', + ]; + } + + return $rules; + } + + public function getUserPayload() + { + return collect($this->validated()) + ->merge([ + 'creator_id' => $this->user()->id, + ]) + ->toArray(); + } +} diff --git a/crater/app/Http/Resources/AbilityCollection.php b/crater/app/Http/Resources/AbilityCollection.php new file mode 100644 index 0000000..b743f41 --- /dev/null +++ b/crater/app/Http/Resources/AbilityCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'entity_id' => $this->entity_id, + 'entity_type' => $this->entity_type, + 'only_owned' => $this->only_owned, + 'options' => $this->options, + 'scope' => $this->scope, + ]; + } +} diff --git a/crater/app/Http/Resources/AddressCollection.php b/crater/app/Http/Resources/AddressCollection.php new file mode 100644 index 0000000..50df325 --- /dev/null +++ b/crater/app/Http/Resources/AddressCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'address_street_1' => $this->address_street_1, + 'address_street_2' => $this->address_street_2, + 'city' => $this->city, + 'state' => $this->state, + 'country_id' => $this->country_id, + 'zip' => $this->zip, + 'phone' => $this->phone, + 'fax' => $this->fax, + 'type' => $this->type, + 'user_id' => $this->user_id, + 'company_id' => $this->company_id, + 'customer_id' => $this->customer_id, + 'country' => $this->when($this->country()->exists(), function () { + return new CountryResource($this->country); + }), + 'user' => $this->when($this->user()->exists(), function () { + return new UserResource($this->user); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/CompanyCollection.php b/crater/app/Http/Resources/CompanyCollection.php new file mode 100644 index 0000000..364728c --- /dev/null +++ b/crater/app/Http/Resources/CompanyCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'logo' => $this->logo, + 'logo_path' => $this->logo_path, + 'unique_hash' => $this->unique_hash, + 'owner_id' => $this->owner_id, + 'slug' => $this->slug, + 'address' => $this->when($this->address()->exists(), function () { + return new AddressResource($this->address); + }), + 'roles' => RoleResource::collection($this->roles) + ]; + } +} diff --git a/crater/app/Http/Resources/CountryCollection.php b/crater/app/Http/Resources/CountryCollection.php new file mode 100644 index 0000000..9d6fe40 --- /dev/null +++ b/crater/app/Http/Resources/CountryCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'code' => $this->code, + 'name' => $this->name, + 'phone_code' => $this->phone_code, + ]; + } +} diff --git a/crater/app/Http/Resources/CurrencyCollection.php b/crater/app/Http/Resources/CurrencyCollection.php new file mode 100644 index 0000000..3abb044 --- /dev/null +++ b/crater/app/Http/Resources/CurrencyCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'code' => $this->code, + 'symbol' => $this->symbol, + 'precision' => $this->precision, + 'thousand_separator' => $this->thousand_separator, + 'decimal_separator' => $this->decimal_separator, + 'swap_currency_symbol' => $this->swap_currency_symbol, + 'exchange_rate' => $this->exchange_rate + ]; + } +} diff --git a/crater/app/Http/Resources/CustomFieldCollection.php b/crater/app/Http/Resources/CustomFieldCollection.php new file mode 100644 index 0000000..057154e --- /dev/null +++ b/crater/app/Http/Resources/CustomFieldCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'slug' => $this->slug, + 'label' => $this->label, + 'model_type' => $this->model_type, + 'type' => $this->type, + 'placeholder' => $this->placeholder, + 'options' => $this->options, + 'boolean_answer' => $this->boolean_answer, + 'date_answer' => $this->date_answer, + 'time_answer' => $this->time_answer, + 'string_answer' => $this->string_answer, + 'number_answer' => $this->number_answer, + 'date_time_answer' => $this->date_time_answer, + 'is_required' => $this->is_required, + 'in_use' => $this->in_use, + 'order' => $this->order, + 'company_id' => $this->company_id, + 'default_answer' => $this->default_answer, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/CustomFieldValueCollection.php b/crater/app/Http/Resources/CustomFieldValueCollection.php new file mode 100644 index 0000000..9397539 --- /dev/null +++ b/crater/app/Http/Resources/CustomFieldValueCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'custom_field_valuable_type' => $this->custom_field_valuable_type, + 'custom_field_valuable_id' => $this->custom_field_valuable_id, + 'type' => $this->type, + 'boolean_answer' => $this->boolean_answer, + 'date_answer' => $this->date_answer, + 'time_answer' => $this->time_answer, + 'string_answer' => $this->string_answer, + 'number_answer' => $this->number_answer, + 'date_time_answer' => $this->date_time_answer, + 'custom_field_id' => $this->custom_field_id, + 'company_id' => $this->company_id, + 'default_answer' => $this->defaultAnswer, + 'default_formatted_answer' => $this->dateTimeFormat(), + 'custom_field' => $this->when($this->customField()->exists(), function () { + return new CustomFieldResource($this->customField); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } + + public function dateTimeFormat() + { + $key = getCustomFieldValueKey($this->type); + + $answer = $this->default_answer; + if (! $answer) { + return null; + } + + if ($key == 'date_time_answer') { + return $answer->format('Y-m-d H:i'); + } + + if ($key == 'date_answer') { + return $answer->format(CompanySetting::getSetting('carbon_date_format', $this->company_id)); + } + + return $answer; + } +} diff --git a/crater/app/Http/Resources/Customer/AddressCollection.php b/crater/app/Http/Resources/Customer/AddressCollection.php new file mode 100644 index 0000000..7983f1f --- /dev/null +++ b/crater/app/Http/Resources/Customer/AddressCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'address_street_1' => $this->address_street_1, + 'address_street_2' => $this->address_street_2, + 'city' => $this->city, + 'state' => $this->state, + 'country_id' => $this->country_id, + 'zip' => $this->zip, + 'phone' => $this->phone, + 'fax' => $this->fax, + 'type' => $this->type, + 'user_id' => $this->user_id, + 'company_id' => $this->company_id, + 'customer_id' => $this->customer_id, + 'country' => $this->when($this->country()->exists(), function () { + return new CountryResource($this->country); + }), + 'user' => $this->when($this->user()->exists(), function () { + return new UserResource($this->user); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CompanyResource.php b/crater/app/Http/Resources/Customer/CompanyResource.php new file mode 100644 index 0000000..c1f0ff1 --- /dev/null +++ b/crater/app/Http/Resources/Customer/CompanyResource.php @@ -0,0 +1,30 @@ + $this->id, + 'name' => $this->name, + 'slug' => $this->slug, + 'logo' => $this->logo, + 'logo_path' => $this->logo_path, + 'unique_hash' => $this->unique_hash, + 'owner_id' => $this->owner_id, + 'address' => $this->when($this->address()->exists(), function () { + return new AddressResource($this->address); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CountryCollection.php b/crater/app/Http/Resources/Customer/CountryCollection.php new file mode 100644 index 0000000..569caee --- /dev/null +++ b/crater/app/Http/Resources/Customer/CountryCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'code' => $this->code, + 'name' => $this->name, + 'phonecode' => $this->phonecode, + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CurrencyCollection.php b/crater/app/Http/Resources/Customer/CurrencyCollection.php new file mode 100644 index 0000000..6cdf0c6 --- /dev/null +++ b/crater/app/Http/Resources/Customer/CurrencyCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'code' => $this->code, + 'symbol' => $this->symbol, + 'precision' => $this->precision, + 'thousand_separator' => $this->thousand_separator, + 'decimal_separator' => $this->decimal_separator, + 'swap_currency_symbol' => $this->swap_currency_symbol, + 'exchange_rate' => $this->exchange_rate + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CustomFieldCollection.php b/crater/app/Http/Resources/Customer/CustomFieldCollection.php new file mode 100644 index 0000000..ef306ed --- /dev/null +++ b/crater/app/Http/Resources/Customer/CustomFieldCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'slug' => $this->slug, + 'label' => $this->label, + 'model_type' => $this->model_type, + 'type' => $this->type, + 'placeholder' => $this->placeholder, + 'options' => $this->options, + 'boolean_answer' => $this->boolean_answer, + 'date_answer' => $this->date_answer, + 'time_answer' => $this->time_answer, + 'string_answer' => $this->string_answer, + 'number_answer' => $this->number_answer, + 'date_time_answer' => $this->date_time_answer, + 'is_required' => $this->is_required, + 'in_use' => $this->in_use, + 'order' => $this->order, + 'company_id' => $this->company_id, + 'default_answer' => $this->default_answer, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CustomFieldValueCollection.php b/crater/app/Http/Resources/Customer/CustomFieldValueCollection.php new file mode 100644 index 0000000..7b86565 --- /dev/null +++ b/crater/app/Http/Resources/Customer/CustomFieldValueCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'custom_field_valuable_type' => $this->custom_field_valuable_type, + 'custom_field_valuable_id' => $this->custom_field_valuable_id, + 'type' => $this->type, + 'boolean_answer' => $this->boolean_answer, + 'date_answer' => $this->date_answer, + 'time_answer' => $this->time_answer, + 'string_answer' => $this->string_answer, + 'number_answer' => $this->number_answer, + 'date_time_answer' => $this->date_time_answer, + 'custom_field_id' => $this->custom_field_id, + 'company_id' => $this->company_id, + 'default_answer' => $this->defaultAnswer, + 'custom_field' => $this->when($this->customField()->exists(), function () { + return new CustomFieldResource($this->customField); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/CustomerCollection.php b/crater/app/Http/Resources/Customer/CustomerCollection.php new file mode 100644 index 0000000..84606e5 --- /dev/null +++ b/crater/app/Http/Resources/Customer/CustomerCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'phone' => $this->phone, + 'contact_name' => $this->contact_name, + 'company_name' => $this->company_name, + 'website' => $this->website, + 'enable_portal' => $this->enable_portal, + 'currency_id' => $this->currency_id, + 'company_id' => $this->company_id, + 'facebook_id' => $this->facebook_id, + 'google_id' => $this->google_id, + 'github_id' => $this->github_id, + 'formatted_created_at' => $this->formattedCreatedAt, + 'avatar' => $this->avatar, + 'prefix' => $this->prefix, + 'billing' => $this->when($this->billingAddress()->exists(), function () { + return new AddressResource($this->billingAddress); + }), + 'shipping' => $this->when($this->shippingAddress()->exists(), function () { + return new AddressResource($this->shippingAddress); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/EstimateCollection.php b/crater/app/Http/Resources/Customer/EstimateCollection.php new file mode 100644 index 0000000..b0a9e2d --- /dev/null +++ b/crater/app/Http/Resources/Customer/EstimateCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'discount_type' => $this->discount_type, + 'quantity' => $this->quantity, + 'unit_name' => $this->unit_name, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'price' => $this->price, + 'tax' => $this->tax, + 'total' => $this->total, + 'item_id' => $this->item_id, + 'estimate_id' => $this->estimate_id, + 'company_id' => $this->company_id, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_price' => $this->base_price, + 'base_tax' => $this->base_tax, + 'base_total' => $this->base_total, + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/EstimateResource.php b/crater/app/Http/Resources/Customer/EstimateResource.php new file mode 100644 index 0000000..f9ec11a --- /dev/null +++ b/crater/app/Http/Resources/Customer/EstimateResource.php @@ -0,0 +1,65 @@ + $this->id, + 'estimate_date' => $this->estimate_date, + 'expiry_date' => $this->expiry_date, + 'estimate_number' => $this->estimate_number, + 'status' => $this->status, + 'reference_number' => $this->reference_number, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->notes, + 'discount' => $this->discount, + 'discount_type' => $this->discount_type, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'unique_hash' => $this->unique_hash, + 'template_name' => $this->template_name, + 'customer_id' => $this->customer_id, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_sub_total' => $this->base_sub_total, + 'base_total' => $this->base_total, + 'base_tax' => $this->base_tax, + 'currency_id' => $this->currency_id, + 'formatted_expiry_date' => $this->formattedExpiryDate, + 'formatted_estimate_date' => $this->formattedEstimateDate, + 'estimate_pdf_url' => $this->estimatePdfUrl, + 'items' => $this->when($this->items()->exists(), function () { + return EstimateItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/ExpenseCategoryCollection.php b/crater/app/Http/Resources/Customer/ExpenseCategoryCollection.php new file mode 100644 index 0000000..3795185 --- /dev/null +++ b/crater/app/Http/Resources/Customer/ExpenseCategoryCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'company_id' => $this->company_id, + 'amount' => $this->amount, + 'formatted_created_at' => $this->formattedCreatedAt, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/ExpenseCollection.php b/crater/app/Http/Resources/Customer/ExpenseCollection.php new file mode 100644 index 0000000..346fd18 --- /dev/null +++ b/crater/app/Http/Resources/Customer/ExpenseCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'expense_date' => $this->expense_date, + 'amount' => $this->amount, + 'notes' => $this->notes, + 'customer_id' => $this->customer_id, + 'attachment_receipt_url' => $this->receipt_url, + 'attachment_receipt' => $this->receipt, + 'attachment_receipt_meta' => $this->receipt_meta, + 'company_id' => $this->company_id, + 'expense_category_id' => $this->expense_category_id, + 'formatted_expense_date' => $this->formattedExpenseDate, + 'formatted_created_at' => $this->formattedCreatedAt, + 'exchange_rate' => $this->exchange_rate, + 'currency_id' => $this->currency_id, + 'base_amount' => $this->base_amount, + 'payment_method_id' => $this->payment_method_id, + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'expense_category' => $this->when($this->category()->exists(), function () { + return new ExpenseCategoryResource($this->category); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'payment_method' => $this->when($this->paymentMethod()->exists(), function () { + return new PaymentMethodResource($this->paymentMethod); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/InvoiceCollection.php b/crater/app/Http/Resources/Customer/InvoiceCollection.php new file mode 100644 index 0000000..475518f --- /dev/null +++ b/crater/app/Http/Resources/Customer/InvoiceCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'discount_type' => $this->discount_type, + 'price' => $this->price, + 'quantity' => $this->quantity, + 'unit_name' => $this->unit_name, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'tax' => $this->tax, + 'total' => $this->total, + 'invoice_id' => $this->invoice_id, + 'item_id' => $this->item_id, + 'company_id' => $this->company_id, + 'base_price' => $this->base_price, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_tax' => $this->base_tax, + 'base_total' => $this->base_total, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/InvoiceResource.php b/crater/app/Http/Resources/Customer/InvoiceResource.php new file mode 100644 index 0000000..d190c0b --- /dev/null +++ b/crater/app/Http/Resources/Customer/InvoiceResource.php @@ -0,0 +1,75 @@ + $this->id, + 'invoice_date' => $this->invoice_date, + 'due_date' => $this->due_date, + 'invoice_number' => $this->invoice_number, + 'reference_number' => $this->reference_number, + 'status' => $this->status, + 'paid_status' => $this->paid_status, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->getNotes(), + 'discount_type' => $this->discount_type, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'due_amount' => $this->due_amount, + 'sent' => $this->sent, + 'viewed' => $this->viewed, + 'unique_hash' => $this->unique_hash, + 'template_name' => $this->template_name, + 'customer_id' => $this->customer_id, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'sequence_number' => $this->sequence_number, + 'base_discount_val' => $this->base_discount_val, + 'base_sub_total' => $this->base_sub_total, + 'base_total' => $this->base_total, + 'base_tax' => $this->base_tax, + 'base_due_amount' => $this->base_due_amount, + 'currency_id' => $this->currency_id, + 'formatted_created_at' => $this->formattedCreatedAt, + 'formatted_notes' => $this->formattedNotes, + 'invoice_pdf_url' => $this->invoicePdfUrl, + 'formatted_invoice_date' => $this->formattedInvoiceDate, + 'formatted_due_date' => $this->formattedDueDate, + 'payment_module_enabled' => $this->payment_module_enabled, + 'overdue' => $this->overdue, + 'items' => $this->when($this->items()->exists(), function () { + return InvoiceItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/ItemCollection.php b/crater/app/Http/Resources/Customer/ItemCollection.php new file mode 100644 index 0000000..85a3717 --- /dev/null +++ b/crater/app/Http/Resources/Customer/ItemCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'company_id' => $this->company_id, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/PaymentResource.php b/crater/app/Http/Resources/Customer/PaymentResource.php new file mode 100644 index 0000000..ef4adef --- /dev/null +++ b/crater/app/Http/Resources/Customer/PaymentResource.php @@ -0,0 +1,58 @@ + $this->id, + 'payment_number' => $this->payment_number, + 'payment_date' => $this->payment_date, + 'notes' => $this->notes, + 'amount' => $this->amount, + 'unique_hash' => $this->unique_hash, + 'invoice_id' => $this->invoice_id, + 'company_id' => $this->company_id, + 'payment_method_id' => $this->payment_method_id, + 'customer_id' => $this->customer_id, + 'exchange_rate' => $this->exchange_rate, + 'base_amount' => $this->base_amount, + 'currency_id' => $this->currency_id, + 'transaction_id' => $this->transaction_id, + 'formatted_created_at' => $this->formattedCreatedAt, + 'formatted_payment_date' => $this->formattedPaymentDate, + 'payment_pdf_url' => $this->paymentPdfUrl, + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'invoice' => $this->when($this->invoice()->exists(), function () { + return new InvoiceResource($this->invoice); + }), + 'payment_method' => $this->when($this->paymentMethod()->exists(), function () { + return new PaymentMethodResource($this->paymentMethod); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'transaction' => $this->when($this->transaction()->exists(), function () { + return new TransactionResource($this->transaction); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/RecurringInvoiceCollection.php b/crater/app/Http/Resources/Customer/RecurringInvoiceCollection.php new file mode 100644 index 0000000..f8d6fe3 --- /dev/null +++ b/crater/app/Http/Resources/Customer/RecurringInvoiceCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'starts_at' => $this->starts_at, + 'formatted_starts_at' => $this->formattedStartsAt, + 'formatted_created_at' => $this->formattedCreatedAt, + 'formatted_next_invoice_at' => $this->formattedNextInvoiceAt, + 'formatted_limit_date' => $this->formattedLimitDate, + 'send_automatically' => $this->send_automatically, + 'customer_id' => $this->customer_id, + 'company_id' => $this->company_id, + 'status' => $this->status, + 'next_invoice_at' => $this->next_invoice_at, + 'frequency' => $this->frequency, + 'limit_by' => $this->limit_by, + 'limit_count' => $this->limit_count, + 'limit_date' => $this->limit_date, + 'exchange_rate' => $this->exchange_rate, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->notes, + 'discount_type' => $this->discount_type, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'due_amount' => $this->due_amount, + 'template_name' => $this->template_name, + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'items' => $this->when($this->items()->exists(), function () { + return InvoiceItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'invoices' => $this->when($this->invoices()->exists(), function () { + return InvoiceResource::collection($this->invoices); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/TaxCollection.php b/crater/app/Http/Resources/Customer/TaxCollection.php new file mode 100644 index 0000000..ca9c7e0 --- /dev/null +++ b/crater/app/Http/Resources/Customer/TaxCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'tax_type_id' => $this->tax_type_id, + 'invoice_id' => $this->invoice_id, + 'estimate_id' => $this->estimate_id, + 'invoice_item_id' => $this->invoice_item_id, + 'estimate_item_id' => $this->estimate_item_id, + 'item_id' => $this->item_id, + 'company_id' => $this->company_id, + 'name' => $this->name, + 'amount' => $this->amount, + 'percent' => $this->percent, + 'compound_tax' => $this->compound_tax, + 'base_amount' => $this->base_amount, + 'currency_id' => $this->currency_id, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'tax_type' => $this->when($this->taxType()->exists(), function () { + return new TaxTypeResource($this->taxType); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/TaxTypeCollection.php b/crater/app/Http/Resources/Customer/TaxTypeCollection.php new file mode 100644 index 0000000..8b27578 --- /dev/null +++ b/crater/app/Http/Resources/Customer/TaxTypeCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'percent' => $this->percent, + 'compound_tax' => $this->compound_tax, + 'collective_tax' => $this->collective_tax, + 'description' => $this->description, + 'company_id' => $this->company_id, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/TransactionCollection.php b/crater/app/Http/Resources/Customer/TransactionCollection.php new file mode 100644 index 0000000..cc19943 --- /dev/null +++ b/crater/app/Http/Resources/Customer/TransactionCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'transaction_id' => $this->transaction_id, + 'type' => $this->type, + 'status' => $this->status, + 'transaction_date' => $this->transaction_date, + 'invoice_id' => $this->invoice_id, + 'invoice' => $this->when($this->invoice()->exists(), function () { + return new InvoiceResource($this->invoice); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/Customer/UserCollection.php b/crater/app/Http/Resources/Customer/UserCollection.php new file mode 100644 index 0000000..2a3475e --- /dev/null +++ b/crater/app/Http/Resources/Customer/UserCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'phone' => $this->phone, + 'role' => $this->role, + 'contact_name' => $this->contact_name, + 'company_name' => $this->company_name, + 'website' => $this->website, + 'enable_portal' => $this->enable_portal, + 'currency_id' => $this->currency_id, + 'facebook_id' => $this->facebook_id, + 'google_id' => $this->google_id, + 'github_id' => $this->github_id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + 'avatar' => $this->avatar, + 'is_owner' => $this->isOwner(), + 'roles' => $this->roles, + 'formatted_created_at' => $this->formattedCreatedAt, + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'companies' => $this->when($this->companies()->exists(), function () { + return CompanyResource::collection($this->companies); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/CustomerCollection.php b/crater/app/Http/Resources/CustomerCollection.php new file mode 100644 index 0000000..44e9270 --- /dev/null +++ b/crater/app/Http/Resources/CustomerCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'phone' => $this->phone, + 'contact_name' => $this->contact_name, + 'company_name' => $this->company_name, + 'website' => $this->website, + 'enable_portal' => $this->enable_portal, + 'password_added' => $this->password ? true : false, + 'currency_id' => $this->currency_id, + 'company_id' => $this->company_id, + 'facebook_id' => $this->facebook_id, + 'google_id' => $this->google_id, + 'github_id' => $this->github_id, + 'created_at' => $this->created_at, + 'formatted_created_at' => $this->formattedCreatedAt, + 'updated_at' => $this->updated_at, + 'avatar' => $this->avatar, + 'due_amount' => $this->due_amount, + 'base_due_amount' => $this->base_due_amount, + 'prefix' => $this->prefix, + 'billing' => $this->when($this->billingAddress()->exists(), function () { + return new AddressResource($this->billingAddress); + }), + 'shipping' => $this->when($this->shippingAddress()->exists(), function () { + return new AddressResource($this->shippingAddress); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/EstimateCollection.php b/crater/app/Http/Resources/EstimateCollection.php new file mode 100644 index 0000000..1198ad5 --- /dev/null +++ b/crater/app/Http/Resources/EstimateCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'discount_type' => $this->discount_type, + 'quantity' => $this->quantity, + 'unit_name' => $this->unit_name, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'price' => $this->price, + 'tax' => $this->tax, + 'total' => $this->total, + 'item_id' => $this->item_id, + 'estimate_id' => $this->estimate_id, + 'company_id' => $this->company_id, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_price' => $this->base_price, + 'base_tax' => $this->base_tax, + 'base_total' => $this->base_total, + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/EstimateResource.php b/crater/app/Http/Resources/EstimateResource.php new file mode 100644 index 0000000..46b69ea --- /dev/null +++ b/crater/app/Http/Resources/EstimateResource.php @@ -0,0 +1,72 @@ + $this->id, + 'estimate_date' => $this->estimate_date, + 'expiry_date' => $this->expiry_date, + 'estimate_number' => $this->estimate_number, + 'status' => $this->status, + 'reference_number' => $this->reference_number, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->getNotes(), + 'discount' => $this->discount, + 'discount_type' => $this->discount_type, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'unique_hash' => $this->unique_hash, + 'creator_id' => $this->creator_id, + 'template_name' => $this->template_name, + 'customer_id' => $this->customer_id, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_sub_total' => $this->base_sub_total, + 'base_total' => $this->base_total, + 'base_tax' => $this->base_tax, + 'sequence_number' => $this->sequence_number, + 'currency_id' => $this->currency_id, + 'formatted_expiry_date' => $this->formattedExpiryDate, + 'formatted_estimate_date' => $this->formattedEstimateDate, + 'estimate_pdf_url' => $this->estimatePdfUrl, + 'sales_tax_type' => $this->sales_tax_type, + 'sales_tax_address_type' => $this->sales_tax_address_type, + 'items' => $this->when($this->items()->exists(), function () { + return EstimateItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'creator' => $this->when($this->creator()->exists(), function () { + return new UserResource($this->creator); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/ExchangeRateLogCollection.php b/crater/app/Http/Resources/ExchangeRateLogCollection.php new file mode 100644 index 0000000..b701d30 --- /dev/null +++ b/crater/app/Http/Resources/ExchangeRateLogCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'company_id' => $this->company_id, + 'base_currency_id' => $this->base_currency_id, + 'currency_id' => $this->currency_id, + 'exchange_rate' => $this->exchange_rate, + ]; + } +} diff --git a/crater/app/Http/Resources/ExchangeRateProviderCollection.php b/crater/app/Http/Resources/ExchangeRateProviderCollection.php new file mode 100644 index 0000000..30864c1 --- /dev/null +++ b/crater/app/Http/Resources/ExchangeRateProviderCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'key' => $this->key, + 'driver' => $this->driver, + 'currencies' => $this->currencies, + 'driver_config' => $this->driver_config, + 'company_id' => $this->company_id, + 'active' => $this->active, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/ExpenseCategoryCollection.php b/crater/app/Http/Resources/ExpenseCategoryCollection.php new file mode 100644 index 0000000..512be38 --- /dev/null +++ b/crater/app/Http/Resources/ExpenseCategoryCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'company_id' => $this->company_id, + 'amount' => $this->amount, + 'formatted_created_at' => $this->formattedCreatedAt, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/ExpenseCollection.php b/crater/app/Http/Resources/ExpenseCollection.php new file mode 100644 index 0000000..23a6f48 --- /dev/null +++ b/crater/app/Http/Resources/ExpenseCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'expense_date' => $this->expense_date, + 'amount' => $this->amount, + 'notes' => $this->notes, + 'customer_id' => $this->customer_id, + 'attachment_receipt_url' => $this->receipt_url, + 'attachment_receipt' => $this->receipt, + 'attachment_receipt_meta' => $this->receipt_meta, + 'company_id' => $this->company_id, + 'expense_category_id' => $this->expense_category_id, + 'creator_id' => $this->creator_id, + 'formatted_expense_date' => $this->formattedExpenseDate, + 'formatted_created_at' => $this->formattedCreatedAt, + 'exchange_rate' => $this->exchange_rate, + 'currency_id' => $this->currency_id, + 'base_amount' => $this->base_amount, + 'payment_method_id' => $this->payment_method_id, + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'expense_category' => $this->when($this->category()->exists(), function () { + return new ExpenseCategoryResource($this->category); + }), + 'creator' => $this->when($this->creator()->exists(), function () { + return new UserResource($this->creator); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'payment_method' => $this->when($this->paymentMethod()->exists(), function () { + return new PaymentMethodResource($this->paymentMethod); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/FileDiskCollection.php b/crater/app/Http/Resources/FileDiskCollection.php new file mode 100644 index 0000000..b82532b --- /dev/null +++ b/crater/app/Http/Resources/FileDiskCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'type' => $this->type, + 'driver' => $this->driver, + 'set_as_default' => $this->set_as_default, + 'credentials' => $this->credentials, + 'company_id' => $this->company_id, + ]; + } +} diff --git a/crater/app/Http/Resources/InvoiceCollection.php b/crater/app/Http/Resources/InvoiceCollection.php new file mode 100644 index 0000000..c7ee564 --- /dev/null +++ b/crater/app/Http/Resources/InvoiceCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'discount_type' => $this->discount_type, + 'price' => $this->price, + 'quantity' => $this->quantity, + 'unit_name' => $this->unit_name, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'tax' => $this->tax, + 'total' => $this->total, + 'invoice_id' => $this->invoice_id, + 'item_id' => $this->item_id, + 'company_id' => $this->company_id, + 'base_price' => $this->base_price, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_tax' => $this->base_tax, + 'base_total' => $this->base_total, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/InvoiceResource.php b/crater/app/Http/Resources/InvoiceResource.php new file mode 100644 index 0000000..c70d2a8 --- /dev/null +++ b/crater/app/Http/Resources/InvoiceResource.php @@ -0,0 +1,82 @@ + $this->id, + 'invoice_date' => $this->invoice_date, + 'due_date' => $this->due_date, + 'invoice_number' => $this->invoice_number, + 'reference_number' => $this->reference_number, + 'status' => $this->status, + 'paid_status' => $this->paid_status, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->notes, + 'discount_type' => $this->discount_type, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'due_amount' => $this->due_amount, + 'sent' => $this->sent, + 'viewed' => $this->viewed, + 'unique_hash' => $this->unique_hash, + 'template_name' => $this->template_name, + 'customer_id' => $this->customer_id, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'sequence_number' => $this->sequence_number, + 'exchange_rate' => $this->exchange_rate, + 'base_discount_val' => $this->base_discount_val, + 'base_sub_total' => $this->base_sub_total, + 'base_total' => $this->base_total, + 'creator_id' => $this->creator_id, + 'base_tax' => $this->base_tax, + 'base_due_amount' => $this->base_due_amount, + 'currency_id' => $this->currency_id, + 'formatted_created_at' => $this->formattedCreatedAt, + 'invoice_pdf_url' => $this->invoicePdfUrl, + 'formatted_invoice_date' => $this->formattedInvoiceDate, + 'formatted_due_date' => $this->formattedDueDate, + 'allow_edit' => $this->allow_edit, + 'payment_module_enabled' => $this->payment_module_enabled, + 'sales_tax_type' => $this->sales_tax_type, + 'sales_tax_address_type' => $this->sales_tax_address_type, + 'overdue' => $this->overdue, + 'items' => $this->when($this->items()->exists(), function () { + return InvoiceItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'creator' => $this->when($this->creator()->exists(), function () { + return new UserResource($this->creator); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/ItemCollection.php b/crater/app/Http/Resources/ItemCollection.php new file mode 100644 index 0000000..da74c02 --- /dev/null +++ b/crater/app/Http/Resources/ItemCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'price' => $this->price, + 'unit_id' => $this->unit_id, + 'company_id' => $this->company_id, + 'creator_id' => $this->creator_id, + 'currency_id' => $this->currency_id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + 'tax_per_item' => $this->tax_per_item, + 'formatted_created_at' => $this->formattedCreatedAt, + 'unit' => $this->when($this->unit()->exists(), function () { + return new UnitResource($this->unit); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/ModuleCollection.php b/crater/app/Http/Resources/ModuleCollection.php new file mode 100644 index 0000000..b18723d --- /dev/null +++ b/crater/app/Http/Resources/ModuleCollection.php @@ -0,0 +1,19 @@ +checkPurchased(); + $this->installed_module = ModelsModule::where('name', $this->module_name)->first(); + + return [ + 'id' => $this->id, + 'average_rating' => $this->average_rating, + 'cover' => $this->cover, + 'slug' => $this->slug, + 'module_name' => $this->module_name, + 'faq' => $this->faq, + 'highlights' => $this->highlights, + 'installed_module_version' => $this->getInstalledModuleVersion(), + 'installed_module_version_updated_at' => $this->getInstalledModuleUpdatedAt(), + 'latest_module_version' => $this->latest_module_version->module_version, + 'latest_module_version_updated_at' => $this->latest_module_version->created_at, + 'is_dev' => $this->is_dev, + 'license' => $this->license, + 'long_description' => $this->long_description, + 'monthly_price' => $this->monthly_price, + 'name' => $this->name, + 'purchased' => $this->purchased, + 'reviews' => $this->reviews ?? [], + 'screenshots' => $this->screenshots, + 'short_description' => $this->short_description, + 'type' => $this->type, + 'yearly_price' => $this->yearly_price, + 'author_name' => $this->author->name, + 'author_avatar' => $this->author->avatar, + 'installed' => $this->moduleInstalled(), + 'enabled' => $this->moduleEnabled(), + 'update_available' => $this->updateAvailable(), + 'video_link' => $this->video_link, + 'video_thumbnail' => $this->video_thumbnail, + 'links' => $this->links + ]; + } + + public function getInstalledModuleVersion() + { + if (isset($this->installed_module) && $this->installed_module->installed) { + return $this->installed_module->version; + } + + return null; + } + + public function getInstalledModuleUpdatedAt() + { + if (isset($this->installed_module) && $this->installed_module->installed) { + return $this->installed_module->updated_at; + } + + return null; + } + + public function moduleInstalled() + { + if (isset($this->installed_module) && $this->installed_module->installed) { + return true; + } + + return false; + } + + public function moduleEnabled() + { + if (isset($this->installed_module) && $this->installed_module->installed) { + return $this->installed_module->enabled; + } + + return false; + } + + public function updateAvailable() + { + if (! isset($this->installed_module)) { + return false; + } + + if (! $this->installed_module->installed) { + return false; + } + + if (! isset($this->latest_module_version)) { + return false; + } + + if (version_compare($this->installed_module->version, $this->latest_module_version->module_version, '>=')) { + return false; + } + + if (version_compare(Setting::getSetting('version'), $this->latest_module_version->crater_version, '<')) { + return false; + } + + return true; + } + + public function checkPurchased() + { + if ($this->purchased) { + return true; + } + + if (Module::has($this->module_name)) { + $module = Module::find($this->module_name); + $module->disable(); + ModelsModule::where('name', $this->module_name)->update(['enabled' => false]); + } + + return false; + } +} diff --git a/crater/app/Http/Resources/NoteCollection.php b/crater/app/Http/Resources/NoteCollection.php new file mode 100644 index 0000000..4dfa780 --- /dev/null +++ b/crater/app/Http/Resources/NoteCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'type' => $this->type, + 'name' => $this->name, + 'notes' => $this->notes, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/PaymentCollection.php b/crater/app/Http/Resources/PaymentCollection.php new file mode 100644 index 0000000..f3d0c6f --- /dev/null +++ b/crater/app/Http/Resources/PaymentCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'company_id' => $this->company_id, + 'type' => $this->type, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/PaymentResource.php b/crater/app/Http/Resources/PaymentResource.php new file mode 100644 index 0000000..0ac625f --- /dev/null +++ b/crater/app/Http/Resources/PaymentResource.php @@ -0,0 +1,60 @@ + $this->id, + 'payment_number' => $this->payment_number, + 'payment_date' => $this->payment_date, + 'notes' => $this->getNotes(), + 'amount' => $this->amount, + 'unique_hash' => $this->unique_hash, + 'invoice_id' => $this->invoice_id, + 'company_id' => $this->company_id, + 'payment_method_id' => $this->payment_method_id, + 'creator_id' => $this->creator_id, + 'customer_id' => $this->customer_id, + 'exchange_rate' => $this->exchange_rate, + 'base_amount' => $this->base_amount, + 'currency_id' => $this->currency_id, + 'transaction_id' => $this->transaction_id, + 'sequence_number' => $this->sequence_number, + 'formatted_created_at' => $this->formattedCreatedAt, + 'formatted_payment_date' => $this->formattedPaymentDate, + 'payment_pdf_url' => $this->paymentPdfUrl, + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'invoice' => $this->when($this->invoice()->exists(), function () { + return new InvoiceResource($this->invoice); + }), + 'payment_method' => $this->when($this->paymentMethod()->exists(), function () { + return new PaymentMethodResource($this->paymentMethod); + }), + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'transaction' => $this->when($this->transaction()->exists(), function () { + return new TransactionResource($this->transaction); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/RecurringInvoiceCollection.php b/crater/app/Http/Resources/RecurringInvoiceCollection.php new file mode 100644 index 0000000..f19905d --- /dev/null +++ b/crater/app/Http/Resources/RecurringInvoiceCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'starts_at' => $this->starts_at, + 'formatted_starts_at' => $this->formattedStartsAt, + 'formatted_created_at' => $this->formattedCreatedAt, + 'formatted_next_invoice_at' => $this->formattedNextInvoiceAt, + 'formatted_limit_date' => $this->formattedLimitDate, + 'send_automatically' => $this->send_automatically, + 'customer_id' => $this->customer_id, + 'company_id' => $this->company_id, + 'creator_id' => $this->creator_id, + 'status' => $this->status, + 'next_invoice_at' => $this->next_invoice_at, + 'frequency' => $this->frequency, + 'limit_by' => $this->limit_by, + 'limit_count' => $this->limit_count, + 'limit_date' => $this->limit_date, + 'exchange_rate' => $this->exchange_rate, + 'tax_per_item' => $this->tax_per_item, + 'discount_per_item' => $this->discount_per_item, + 'notes' => $this->notes, + 'discount_type' => $this->discount_type, + 'discount' => $this->discount, + 'discount_val' => $this->discount_val, + 'sub_total' => $this->sub_total, + 'total' => $this->total, + 'tax' => $this->tax, + 'due_amount' => $this->due_amount, + 'template_name' => $this->template_name, + 'sales_tax_type' => $this->sales_tax_type, + 'sales_tax_address_type' => $this->sales_tax_address_type, + 'fields' => $this->when($this->fields()->exists(), function () { + return CustomFieldValueResource::collection($this->fields); + }), + 'items' => $this->when($this->items()->exists(), function () { + return InvoiceItemResource::collection($this->items); + }), + 'customer' => $this->when($this->customer()->exists(), function () { + return new CustomerResource($this->customer); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + 'invoices' => $this->when($this->invoices()->exists(), function () { + return InvoiceResource::collection($this->invoices); + }), + 'taxes' => $this->when($this->taxes()->exists(), function () { + return TaxResource::collection($this->taxes); + }), + 'creator' => $this->when($this->creator()->exists(), function () { + return new UserResource($this->creator); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/RoleCollection.php b/crater/app/Http/Resources/RoleCollection.php new file mode 100644 index 0000000..e4e1b7c --- /dev/null +++ b/crater/app/Http/Resources/RoleCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'title' => $this->title, + 'level' => $this->level, + 'formatted_created_at' => $this->getFormattedAt(), + 'abilities' => $this->getAbilities() + ]; + } + + public function getFormattedAt() + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->scope); + + return Carbon::parse($this->created_at)->format($dateFormat); + } +} diff --git a/crater/app/Http/Resources/TaxCollection.php b/crater/app/Http/Resources/TaxCollection.php new file mode 100644 index 0000000..c7dcbb8 --- /dev/null +++ b/crater/app/Http/Resources/TaxCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'tax_type_id' => $this->tax_type_id, + 'invoice_id' => $this->invoice_id, + 'estimate_id' => $this->estimate_id, + 'invoice_item_id' => $this->invoice_item_id, + 'estimate_item_id' => $this->estimate_item_id, + 'item_id' => $this->item_id, + 'company_id' => $this->company_id, + 'name' => $this->name, + 'amount' => $this->amount, + 'percent' => $this->percent, + 'compound_tax' => $this->compound_tax, + 'base_amount' => $this->base_amount, + 'currency_id' => $this->currency_id, + 'type' => $this->taxType->type, + 'recurring_invoice_id' => $this->recurring_invoice_id, + 'tax_type' => $this->when($this->taxType()->exists(), function () { + return new TaxTypeResource($this->taxType); + }), + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/TaxTypeCollection.php b/crater/app/Http/Resources/TaxTypeCollection.php new file mode 100644 index 0000000..8866538 --- /dev/null +++ b/crater/app/Http/Resources/TaxTypeCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'percent' => $this->percent, + 'type' => $this->type, + 'compound_tax' => $this->compound_tax, + 'collective_tax' => $this->collective_tax, + 'description' => $this->description, + 'company_id' => $this->company_id, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/TransactionCollection.php b/crater/app/Http/Resources/TransactionCollection.php new file mode 100644 index 0000000..f7ab399 --- /dev/null +++ b/crater/app/Http/Resources/TransactionCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'transaction_id' => $this->transaction_id, + 'type' => $this->type, + 'status' => $this->status, + 'transaction_date' => $this->transaction_date, + 'invoice_id' => $this->invoice_id, + 'invoice' => $this->when($this->invoice()->exists(), function () { + return new InvoiceResource($this->invoice); + }), + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/UnitCollection.php b/crater/app/Http/Resources/UnitCollection.php new file mode 100644 index 0000000..163d4ca --- /dev/null +++ b/crater/app/Http/Resources/UnitCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'company_id' => $this->company_id, + 'company' => $this->when($this->company()->exists(), function () { + return new CompanyResource($this->company); + }), + ]; + } +} diff --git a/crater/app/Http/Resources/UserCollection.php b/crater/app/Http/Resources/UserCollection.php new file mode 100644 index 0000000..25f6546 --- /dev/null +++ b/crater/app/Http/Resources/UserCollection.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'phone' => $this->phone, + 'role' => $this->role, + 'contact_name' => $this->contact_name, + 'company_name' => $this->company_name, + 'website' => $this->website, + 'enable_portal' => $this->enable_portal, + 'currency_id' => $this->currency_id, + 'facebook_id' => $this->facebook_id, + 'google_id' => $this->google_id, + 'github_id' => $this->github_id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + 'avatar' => $this->avatar, + 'is_owner' => $this->isOwner(), + 'roles' => $this->roles, + 'formatted_created_at' => $this->formattedCreatedAt, + 'currency' => $this->when($this->currency()->exists(), function () { + return new CurrencyResource($this->currency); + }), + 'companies' => $this->when($this->companies()->exists(), function () { + return CompanyResource::collection($this->companies); + }) + ]; + } +} diff --git a/crater/app/Jobs/CreateBackupJob.php b/crater/app/Jobs/CreateBackupJob.php new file mode 100644 index 0000000..b45fe0f --- /dev/null +++ b/crater/app/Jobs/CreateBackupJob.php @@ -0,0 +1,64 @@ +data = $data; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $fileDisk = FileDisk::find($this->data['file_disk_id']); + $fileDisk->setConfig(); + + $prefix = env('DYNAMIC_DISK_PREFIX', 'temp_'); + + config(['backup.backup.destination.disks' => [$prefix.$fileDisk->driver]]); + + $backupJob = BackupJobFactory::createFromArray(config('backup')); + + if ($this->data['option'] === 'only-db') { + $backupJob->dontBackupFilesystem(); + } + + if ($this->data['option'] === 'only-files') { + $backupJob->dontBackupDatabases(); + } + + if (! empty($this->data['option'])) { + $prefix = str_replace('_', '-', $this->data['option']).'-'; + + $backupJob->setFilename($prefix.date('Y-m-d-H-i-s').'.zip'); + } + + $backupJob->run(); + } +} diff --git a/crater/app/Jobs/GenerateEstimatePdfJob.php b/crater/app/Jobs/GenerateEstimatePdfJob.php new file mode 100644 index 0000000..91030ea --- /dev/null +++ b/crater/app/Jobs/GenerateEstimatePdfJob.php @@ -0,0 +1,44 @@ +estimate = $estimate; + $this->deleteExistingFile = $deleteExistingFile; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $this->estimate->generatePDF('estimate', $this->estimate->estimate_number, $this->deleteExistingFile); + + return 0; + } +} diff --git a/crater/app/Jobs/GenerateInvoicePdfJob.php b/crater/app/Jobs/GenerateInvoicePdfJob.php new file mode 100644 index 0000000..42d29bd --- /dev/null +++ b/crater/app/Jobs/GenerateInvoicePdfJob.php @@ -0,0 +1,44 @@ +invoice = $invoice; + $this->deleteExistingFile = $deleteExistingFile; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $this->invoice->generatePDF('invoice', $this->invoice->invoice_number, $this->deleteExistingFile); + + return 0; + } +} diff --git a/crater/app/Jobs/GeneratePaymentPdfJob.php b/crater/app/Jobs/GeneratePaymentPdfJob.php new file mode 100644 index 0000000..ffe4f92 --- /dev/null +++ b/crater/app/Jobs/GeneratePaymentPdfJob.php @@ -0,0 +1,44 @@ +payment = $payment; + $this->deleteExistingFile = $deleteExistingFile; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $this->payment->generatePDF('payment', $this->payment->payment_number, $this->deleteExistingFile); + + return 0; + } +} diff --git a/crater/app/Listeners/Updates/Listener.php b/crater/app/Listeners/Updates/Listener.php new file mode 100644 index 0000000..1294f71 --- /dev/null +++ b/crater/app/Listeners/Updates/Listener.php @@ -0,0 +1,25 @@ +old, '<=')) { + return true; + } + + return false; + } +} diff --git a/crater/app/Listeners/Updates/v1/Version110.php b/crater/app/Listeners/Updates/v1/Version110.php new file mode 100644 index 0000000..d80ea5a --- /dev/null +++ b/crater/app/Listeners/Updates/v1/Version110.php @@ -0,0 +1,113 @@ +isListenerFired($event)) { + return; + } + + // Add currencies + $this->addCurrencies(); + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } + + private function addCurrencies() + { + $currencies = [ + '13' => [ + 'symbol' => 'S$', + ], + '16' => [ + 'symbol' => '₫', + ], + '17' => [ + 'symbol' => 'Fr.', + ], + '21' => [ + 'symbol' => '฿', + ], + '22' => [ + 'symbol' => '₦', + ], + '26' => [ + 'symbol' => 'HK$', + ], + '35' => [ + 'symbol' => 'NAƒ', + ], + '38' => [ + 'symbol' => 'GH₵', + ], + '39' => [ + 'symbol' => 'Лв.', + ], + '42' => [ + 'symbol' => 'RON', + ], + '44' => [ + 'symbol' => 'SِAR', + ], + '46' => [ + 'symbol' => 'Rf', + ], + '47' => [ + 'symbol' => '₡', + ], + '54' => [ + 'symbol' => '‎د.ت', + ], + '55' => [ + 'symbol' => '₽', + ], + '57' => [ + 'symbol' => 'ر.ع.', + ], + '58' => [ + 'symbol' => '₴', + ], + + ]; + + foreach ($currencies as $key => $currency) { + Currency::updateOrCreate(['id' => $key], $currency); + } + + Currency::create([ + 'name' => 'Kuwaiti Dinar', + 'code' => 'KWD', + 'symbol' => 'KWD ', + 'precision' => '3', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ]); + } +} diff --git a/crater/app/Listeners/Updates/v2/Version200.php b/crater/app/Listeners/Updates/v2/Version200.php new file mode 100644 index 0000000..d506c20 --- /dev/null +++ b/crater/app/Listeners/Updates/v2/Version200.php @@ -0,0 +1,111 @@ +isListenerFired($event)) { + return; + } + + // Replace state and city id to name + $this->replaceStateAndCityName(); + + // Drop states and cities foreign key + $this->dropForeignKey(); + + // Remove states and cities tables + $this->dropSchemas(); + + // Delete state & city models, migrations & seeders + $this->deleteFiles(); + + // Update Crater app version + $this->updateVersion(); + } + + private function replaceStateAndCityName() + { + \Schema::table('addresses', function (Blueprint $table) { + $table->string('state')->nullable(); + $table->string('city')->nullable(); + }); + + $addresses = \Crater\Models\Address::all(); + foreach ($addresses as $add) { + $city = \Crater\City::find($add->city_id); + if ($city) { + $add->city = $city->name; + } + + $state = \Crater\State::find($add->state_id); + if ($state) { + $add->state = $state->name; + } + + $add->save(); + } + } + + private function dropForeignKey() + { + \Schema::table('addresses', function (Blueprint $table) { + $table->dropForeign('addresses_state_id_foreign'); + $table->dropForeign('addresses_city_id_foreign'); + $table->dropColumn('state_id'); + $table->dropColumn('city_id'); + }); + } + + private function dropSchemas() + { + \Schema::disableForeignKeyConstraints(); + + \Schema::dropIfExists('states'); + \Schema::dropIfExists('cities'); + + \Schema::enableForeignKeyConstraints(); + } + + private function deleteFiles() + { + \File::delete( + database_path('migrations/2017_05_06_172817_create_cities_table.php'), + database_path('migrations/2017_05_06_173711_create_states_table.php'), + database_path('seeds/StatesTableSeeder.php'), + database_path('seeds/CitiesTableSeeder.php'), + app_path('City.php'), + app_path('State.php') + ); + } + + private function updateVersion() + { + Setting::setSetting('version', static::VERSION); + } +} diff --git a/crater/app/Listeners/Updates/v2/Version201.php b/crater/app/Listeners/Updates/v2/Version201.php new file mode 100644 index 0000000..8885033 --- /dev/null +++ b/crater/app/Listeners/Updates/v2/Version201.php @@ -0,0 +1,86 @@ +isListenerFired($event)) { + return; + } + + // Remove the language files + $this->removeLanguageFiles(); + + // Change estimate & invoice migrations + $this->changeMigrations(); + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } + + private function removeLanguageFiles() + { + $en = resource_path('assets/js/plugins/en.js'); + $es = resource_path('assets/js/plugins/es.js'); + $fr = resource_path('assets/js/plugins/fr.js'); + + if (file_exists($en)) { + unlink($en); + } + + if (file_exists($es)) { + unlink($es); + } + + if (file_exists($fr)) { + unlink($fr); + } + } + + private function changeMigrations() + { + \Schema::table('invoices', function (Blueprint $table) { + $table->decimal('discount', 15, 2)->nullable()->change(); + }); + + \Schema::table('estimates', function (Blueprint $table) { + $table->decimal('discount', 15, 2)->nullable()->change(); + }); + + \Schema::table('invoice_items', function (Blueprint $table) { + $table->decimal('quantity', 15, 2)->change(); + $table->decimal('discount', 15, 2)->nullable()->change(); + }); + + \Schema::table('estimate_items', function (Blueprint $table) { + $table->decimal('quantity', 15, 2)->change(); + $table->decimal('discount', 15, 2)->nullable()->change(); + $table->unsignedBigInteger('discount_val')->nullable()->change(); + }); + } +} diff --git a/crater/app/Listeners/Updates/v2/Version202.php b/crater/app/Listeners/Updates/v2/Version202.php new file mode 100644 index 0000000..5503c32 --- /dev/null +++ b/crater/app/Listeners/Updates/v2/Version202.php @@ -0,0 +1,38 @@ +isListenerFired($event)) { + return; + } + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } +} diff --git a/crater/app/Listeners/Updates/v2/Version210.php b/crater/app/Listeners/Updates/v2/Version210.php new file mode 100644 index 0000000..2a1f016 --- /dev/null +++ b/crater/app/Listeners/Updates/v2/Version210.php @@ -0,0 +1,62 @@ +isListenerFired($event)) { + return; + } + + // Add initial auto generate value + $this->addAutoGenerateSettings(); + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } + + private function addAutoGenerateSettings() + { + $settings = [ + 'invoice_auto_generate' => 'YES', + 'invoice_prefix' => 'INV', + 'estimate_prefix' => 'EST', + 'estimate_auto_generate' => 'YES', + 'payment_prefix' => 'PAY', + 'payment_auto_generate' => 'YES', + ]; + + foreach ($settings as $key => $value) { + CompanySetting::setSetting( + $key, + $value, + auth()->user()->company->id + ); + } + } +} diff --git a/crater/app/Listeners/Updates/v3/Version300.php b/crater/app/Listeners/Updates/v3/Version300.php new file mode 100644 index 0000000..55e5b15 --- /dev/null +++ b/crater/app/Listeners/Updates/v3/Version300.php @@ -0,0 +1,161 @@ +isListenerFired($event)) { + return; + } + + $this->changeMigrations(); + + $this->addSeederData(); + + $this->databaseChanges(); + + $this->changeMigrations(true); + + Setting::setSetting('version', static::VERSION); + } + + public function changeMigrations($removeColumn = false) + { + if ($removeColumn) { + \Schema::table('items', function (Blueprint $table) { + $table->dropColumn('unit'); + }); + + \Schema::table('payments', function (Blueprint $table) { + $table->dropColumn('payment_mode'); + }); + + return true; + } + + \Schema::create('units', function (Blueprint $table) { + $table->increments('id'); + $table->string('name'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + + \Schema::table('items', function (Blueprint $table) { + $table->integer('unit_id')->unsigned()->nullable(); + $table->foreign('unit_id')->references('id')->on('units')->onDelete('cascade'); + }); + + \Schema::create('payment_methods', function (Blueprint $table) { + $table->increments('id'); + $table->string('name'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + + \Schema::table('payments', function (Blueprint $table) { + $table->string('unique_hash')->nullable(); + $table->integer('payment_method_id')->unsigned()->nullable(); + $table->foreign('payment_method_id')->references('id')->on('payment_methods')->onDelete('cascade'); + }); + + return true; + } + + public function addSeederData() + { + $company_id = User::where('role', 'admin')->first()->company_id; + + Unit::create(['name' => 'box', 'company_id' => $company_id]); + Unit::create(['name' => 'cm', 'company_id' => $company_id]); + Unit::create(['name' => 'dz', 'company_id' => $company_id]); + Unit::create(['name' => 'ft', 'company_id' => $company_id]); + Unit::create(['name' => 'g', 'company_id' => $company_id]); + Unit::create(['name' => 'in', 'company_id' => $company_id]); + Unit::create(['name' => 'kg', 'company_id' => $company_id]); + Unit::create(['name' => 'km', 'company_id' => $company_id]); + Unit::create(['name' => 'lb', 'company_id' => $company_id]); + Unit::create(['name' => 'mg', 'company_id' => $company_id]); + Unit::create(['name' => 'pc', 'company_id' => $company_id]); + + PaymentMethod::create(['name' => 'Cash', 'company_id' => $company_id]); + PaymentMethod::create(['name' => 'Check', 'company_id' => $company_id]); + PaymentMethod::create(['name' => 'Credit Card', 'company_id' => $company_id]); + PaymentMethod::create(['name' => 'Bank Transfer', 'company_id' => $company_id]); + + Currency::create([ + 'name' => 'Serbian Dinar', + 'code' => 'RSD', + 'symbol' => 'RSD', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ]); + } + + public function databaseChanges() + { + $payments = Payment::all(); + + if ($payments) { + foreach ($payments as $payment) { + $payment->unique_hash = Hashids::connection(Payment::class)->encode($payment->id); + $payment->save(); + + $paymentMethod = PaymentMethod::where('name', $payment->payment_mode) + ->first(); + + if ($paymentMethod) { + $payment->payment_method_id = $paymentMethod->id; + $payment->save(); + } + } + } + + $items = Item::all(); + + if ($items) { + foreach ($items as $item) { + $unit = Unit::where('name', $item->unit) + ->first(); + + if ($unit) { + $item->unit_id = $unit->id; + $item->save(); + } + } + } + } +} diff --git a/crater/app/Listeners/Updates/v3/Version310.php b/crater/app/Listeners/Updates/v3/Version310.php new file mode 100644 index 0000000..9be0767 --- /dev/null +++ b/crater/app/Listeners/Updates/v3/Version310.php @@ -0,0 +1,47 @@ +isListenerFired($event)) { + return; + } + + Currency::firstOrCreate( + [ + 'name' => 'Kyrgyzstani som', + 'code' => 'KGS', + ], + [ + 'name' => 'Kyrgyzstani som', + 'code' => 'KGS', + 'symbol' => 'С̲ ', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ] + ); + + Artisan::call('migrate', ['--force' => true]); + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } +} diff --git a/crater/app/Listeners/Updates/v3/Version311.php b/crater/app/Listeners/Updates/v3/Version311.php new file mode 100644 index 0000000..e900521 --- /dev/null +++ b/crater/app/Listeners/Updates/v3/Version311.php @@ -0,0 +1,31 @@ +isListenerFired($event)) { + return; + } + + Artisan::call('migrate', ['--force' => true]); + + // Update Crater app version + Setting::setSetting('version', static::VERSION); + } +} diff --git a/crater/app/Mail/EstimateViewedMail.php b/crater/app/Mail/EstimateViewedMail.php new file mode 100644 index 0000000..a29d8f6 --- /dev/null +++ b/crater/app/Mail/EstimateViewedMail.php @@ -0,0 +1,36 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->from(config('mail.from.address'), config('mail.from.name')) + ->markdown('emails.viewed.estimate', ['data', $this->data]); + } +} diff --git a/crater/app/Mail/InvoiceViewedMail.php b/crater/app/Mail/InvoiceViewedMail.php new file mode 100644 index 0000000..94e858b --- /dev/null +++ b/crater/app/Mail/InvoiceViewedMail.php @@ -0,0 +1,36 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->from(config('mail.from.address'), config('mail.from.name')) + ->markdown('emails.viewed.invoice', ['data', $this->data]); + } +} diff --git a/crater/app/Mail/SendEstimateMail.php b/crater/app/Mail/SendEstimateMail.php new file mode 100644 index 0000000..70121e0 --- /dev/null +++ b/crater/app/Mail/SendEstimateMail.php @@ -0,0 +1,63 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $log = EmailLog::create([ + 'from' => $this->data['from'], + 'to' => $this->data['to'], + 'subject' => $this->data['subject'], + 'body' => $this->data['body'], + 'mailable_type' => Estimate::class, + 'mailable_id' => $this->data['estimate']['id'], + ]); + + $log->token = Hashids::connection(EmailLog::class)->encode($log->id); + $log->save(); + + $this->data['url'] = route('estimate', ['email_log' => $log->token]); + + $mailContent = $this->from($this->data['from'], config('mail.from.name')) + ->subject($this->data['subject']) + ->markdown('emails.send.estimate', ['data', $this->data]); + + if ($this->data['attach']['data']) { + $mailContent->attachData( + $this->data['attach']['data']->output(), + $this->data['estimate']['estimate_number'].'.pdf' + ); + } + + return $mailContent; + } +} diff --git a/crater/app/Mail/SendInvoiceMail.php b/crater/app/Mail/SendInvoiceMail.php new file mode 100644 index 0000000..81d76fa --- /dev/null +++ b/crater/app/Mail/SendInvoiceMail.php @@ -0,0 +1,63 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $log = EmailLog::create([ + 'from' => $this->data['from'], + 'to' => $this->data['to'], + 'subject' => $this->data['subject'], + 'body' => $this->data['body'], + 'mailable_type' => Invoice::class, + 'mailable_id' => $this->data['invoice']['id'], + ]); + + $log->token = Hashids::connection(EmailLog::class)->encode($log->id); + $log->save(); + + $this->data['url'] = route('invoice', ['email_log' => $log->token]); + + $mailContent = $this->from($this->data['from'], config('mail.from.name')) + ->subject($this->data['subject']) + ->markdown('emails.send.invoice', ['data', $this->data]); + + if ($this->data['attach']['data']) { + $mailContent->attachData( + $this->data['attach']['data']->output(), + $this->data['invoice']['invoice_number'].'.pdf' + ); + } + + return $mailContent; + } +} diff --git a/crater/app/Mail/SendPaymentMail.php b/crater/app/Mail/SendPaymentMail.php new file mode 100644 index 0000000..4d52547 --- /dev/null +++ b/crater/app/Mail/SendPaymentMail.php @@ -0,0 +1,63 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $log = EmailLog::create([ + 'from' => $this->data['from'], + 'to' => $this->data['to'], + 'subject' => $this->data['subject'], + 'body' => $this->data['body'], + 'mailable_type' => Payment::class, + 'mailable_id' => $this->data['payment']['id'], + ]); + + $log->token = Hashids::connection(EmailLog::class)->encode($log->id); + $log->save(); + + $this->data['url'] = route('payment', ['email_log' => $log->token]); + + $mailContent = $this->from($this->data['from'], config('mail.from.name')) + ->subject($this->data['subject']) + ->markdown('emails.send.payment', ['data', $this->data]); + + if ($this->data['attach']['data']) { + $mailContent->attachData( + $this->data['attach']['data']->output(), + $this->data['payment']['payment_number'].'.pdf' + ); + } + + return $mailContent; + } +} diff --git a/crater/app/Mail/TestMail.php b/crater/app/Mail/TestMail.php new file mode 100644 index 0000000..8e7548d --- /dev/null +++ b/crater/app/Mail/TestMail.php @@ -0,0 +1,41 @@ +subject = $subject; + $this->message = $message; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->subject($this->subject)->markdown('emails.test')->with([ + 'my_message' => $this->message, + ]); + } +} diff --git a/crater/app/Models/Address.php b/crater/app/Models/Address.php new file mode 100644 index 0000000..e51bae4 --- /dev/null +++ b/crater/app/Models/Address.php @@ -0,0 +1,42 @@ +country ? $this->country->name : null; + + return $name; + } + + public function user() + { + return $this->belongsTo(User::class); + } + + public function customer() + { + return $this->belongsTo(Customer::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function country() + { + return $this->belongsTo(Country::class); + } +} diff --git a/crater/app/Models/Company.php b/crater/app/Models/Company.php new file mode 100644 index 0000000..bf87820 --- /dev/null +++ b/crater/app/Models/Company.php @@ -0,0 +1,400 @@ +id) + ->get(); + } + + public function getLogoPathAttribute() + { + $logo = $this->getMedia('logo')->first(); + + $isSystem = FileDisk::whereSetAsDefault(true)->first()->isSystem(); + + if ($logo) { + if ($isSystem) { + return $logo->getPath(); + } else { + return $logo->getFullUrl(); + } + } + + return null; + } + + public function getLogoAttribute() + { + $logo = $this->getMedia('logo')->first(); + + if ($logo) { + return $logo->getFullUrl(); + } + + return null; + } + + public function customers() + { + return $this->hasMany(Customer::class); + } + + public function owner() + { + return $this->belongsTo(User::class, 'owner_id'); + } + + public function settings() + { + return $this->hasMany(CompanySetting::class); + } + + public function recurringInvoices() + { + return $this->hasMany(RecurringInvoice::class); + } + + public function customFields() + { + return $this->hasMany(CustomField::class); + } + + public function customFieldValues() + { + return $this->hasMany(CustomFieldValue::class); + } + + public function exchangeRateLogs() + { + return $this->hasMany(ExchangeRateLog::class); + } + + public function exchangeRateProviders() + { + return $this->hasMany(ExchangeRateProvider::class); + } + + public function invoices() + { + return $this->hasMany(Invoice::class); + } + + public function expenses() + { + return $this->hasMany(Expense::class); + } + + public function units() + { + return $this->hasMany(Unit::class); + } + + public function expenseCategories() + { + return $this->hasMany(ExpenseCategory::class); + } + + public function taxTypes() + { + return $this->hasMany(TaxType::class); + } + + public function items() + { + return $this->hasMany(Item::class); + } + + public function payments() + { + return $this->hasMany(Payment::class); + } + + public function paymentMethods() + { + return $this->hasMany(PaymentMethod::class); + } + + public function estimates() + { + return $this->hasMany(Estimate::class); + } + + public function address() + { + return $this->hasOne(Address::class); + } + + public function users() + { + return $this->belongsToMany(User::class, 'user_company', 'company_id', 'user_id'); + } + + public function setupRoles() + { + BouncerFacade::scope()->to($this->id); + + $super_admin = BouncerFacade::role()->firstOrCreate([ + 'name' => 'super admin', + 'title' => 'Super Admin', + 'scope' => $this->id + ]); + + foreach (config('abilities.abilities') as $ability) { + BouncerFacade::allow($super_admin)->to($ability['ability'], $ability['model']); + } + } + + public function setupDefaultPaymentMethods() + { + PaymentMethod::create(['name' => 'Cash', 'company_id' => $this->id]); + PaymentMethod::create(['name' => 'Check', 'company_id' => $this->id]); + PaymentMethod::create(['name' => 'Credit Card', 'company_id' => $this->id]); + PaymentMethod::create(['name' => 'Bank Transfer', 'company_id' => $this->id]); + } + + public function setupDefaultUnits() + { + Unit::create(['name' => 'box', 'company_id' => $this->id]); + Unit::create(['name' => 'cm', 'company_id' => $this->id]); + Unit::create(['name' => 'dz', 'company_id' => $this->id]); + Unit::create(['name' => 'ft', 'company_id' => $this->id]); + Unit::create(['name' => 'g', 'company_id' => $this->id]); + Unit::create(['name' => 'in', 'company_id' => $this->id]); + Unit::create(['name' => 'kg', 'company_id' => $this->id]); + Unit::create(['name' => 'km', 'company_id' => $this->id]); + Unit::create(['name' => 'lb', 'company_id' => $this->id]); + Unit::create(['name' => 'mg', 'company_id' => $this->id]); + Unit::create(['name' => 'pc', 'company_id' => $this->id]); + } + + public function setupDefaultSettings() + { + $defaultInvoiceEmailBody = 'You have received a new invoice from {COMPANY_NAME}.
Please download using the button below:'; + $defaultEstimateEmailBody = 'You have received a new estimate from {COMPANY_NAME}.
Please download using the button below:'; + $defaultPaymentEmailBody = 'Thank you for the payment.
Please download your payment receipt using the button below:'; + $billingAddressFormat = '

{BILLING_ADDRESS_NAME}

{BILLING_ADDRESS_STREET_1}

{BILLING_ADDRESS_STREET_2}

{BILLING_CITY} {BILLING_STATE}

{BILLING_COUNTRY} {BILLING_ZIP_CODE}

{BILLING_PHONE}

'; + $shippingAddressFormat = '

{SHIPPING_ADDRESS_NAME}

{SHIPPING_ADDRESS_STREET_1}

{SHIPPING_ADDRESS_STREET_2}

{SHIPPING_CITY} {SHIPPING_STATE}

{SHIPPING_COUNTRY} {SHIPPING_ZIP_CODE}

{SHIPPING_PHONE}

'; + $companyAddressFormat = '

{COMPANY_NAME}

{COMPANY_ADDRESS_STREET_1}

{COMPANY_ADDRESS_STREET_2}

{COMPANY_CITY} {COMPANY_STATE}

{COMPANY_COUNTRY} {COMPANY_ZIP_CODE}

{COMPANY_PHONE}

'; + $paymentFromCustomerAddress = '

{BILLING_ADDRESS_NAME}

{BILLING_ADDRESS_STREET_1}

{BILLING_ADDRESS_STREET_2}

{BILLING_CITY} {BILLING_STATE} {BILLING_ZIP_CODE}

{BILLING_COUNTRY}

{BILLING_PHONE}

'; + + $settings = [ + 'invoice_auto_generate' => 'YES', + 'payment_auto_generate' => 'YES', + 'estimate_auto_generate' => 'YES', + 'save_pdf_to_disk' => 'NO', + 'invoice_mail_body' => $defaultInvoiceEmailBody, + 'estimate_mail_body' => $defaultEstimateEmailBody, + 'payment_mail_body' => $defaultPaymentEmailBody, + 'invoice_company_address_format' => $companyAddressFormat, + 'invoice_shipping_address_format' => $shippingAddressFormat, + 'invoice_billing_address_format' => $billingAddressFormat, + 'estimate_company_address_format' => $companyAddressFormat, + 'estimate_shipping_address_format' => $shippingAddressFormat, + 'estimate_billing_address_format' => $billingAddressFormat, + 'payment_company_address_format' => $companyAddressFormat, + 'payment_from_customer_address_format' => $paymentFromCustomerAddress, + 'currency' => request()->currency ?? 13, + 'time_zone' => 'Asia/Kolkata', + 'language' => 'en', + 'fiscal_year' => '1-12', + 'carbon_date_format' => 'Y/m/d', + 'moment_date_format' => 'YYYY/MM/DD', + 'notification_email' => 'noreply@crater.in', + 'notify_invoice_viewed' => 'NO', + 'notify_estimate_viewed' => 'NO', + 'tax_per_item' => 'NO', + 'discount_per_item' => 'NO', + 'invoice_auto_generate' => 'YES', + 'invoice_email_attachment' => 'NO', + 'estimate_auto_generate' => 'YES', + 'estimate_email_attachment' => 'NO', + 'payment_auto_generate' => 'YES', + 'payment_email_attachment' => 'NO', + 'save_pdf_to_disk' => 'NO', + 'retrospective_edits' => 'allow', + 'invoice_number_format' => '{{SERIES:INV}}{{DELIMITER:-}}{{SEQUENCE:6}}', + 'estimate_number_format' => '{{SERIES:EST}}{{DELIMITER:-}}{{SEQUENCE:6}}', + 'payment_number_format' => '{{SERIES:PAY}}{{DELIMITER:-}}{{SEQUENCE:6}}', + 'estimate_set_expiry_date_automatically' => 'YES', + 'estimate_expiry_date_days' => 7, + 'invoice_set_due_date_automatically' => 'YES', + 'invoice_due_date_days' => 7, + 'bulk_exchange_rate_configured' => 'YES', + 'estimate_convert_action' => 'no_action', + 'automatically_expire_public_links' => 'YES', + 'link_expiry_days' => 7, + ]; + + CompanySetting::setSettings($settings, $this->id); + } + + public function setupDefaultData() + { + $this->setupRoles(); + $this->setupDefaultPaymentMethods(); + $this->setupDefaultUnits(); + $this->setupDefaultSettings(); + + return true; + } + + public function deleteCompany($user) + { + if ($this->exchangeRateLogs()->exists()) { + $this->exchangeRateLogs()->delete(); + } + + if ($this->exchangeRateProviders()->exists()) { + $this->exchangeRateProviders()->delete(); + } + + if ($this->expenses()->exists()) { + $this->expenses()->delete(); + } + + if ($this->expenseCategories()->exists()) { + $this->expenseCategories()->delete(); + } + + if ($this->payments()->exists()) { + $this->payments()->delete(); + } + + if ($this->paymentMethods()->exists()) { + $this->paymentMethods()->delete(); + } + + if ($this->customFieldValues()->exists()) { + $this->customFieldValues()->delete(); + } + + + if ($this->customFields()->exists()) { + $this->customFields()->delete(); + } + + if ($this->invoices()->exists()) { + $this->invoices->map(function ($invoice) { + $this->checkModelData($invoice); + + if ($invoice->transactions()->exists()) { + $invoice->transactions()->delete(); + } + }); + + $this->invoices()->delete(); + } + + if ($this->recurringInvoices()->exists()) { + $this->recurringInvoices->map(function ($recurringInvoice) { + $this->checkModelData($recurringInvoice); + }); + + $this->recurringInvoices()->delete(); + } + + if ($this->estimates()->exists()) { + $this->estimates->map(function ($estimate) { + $this->checkModelData($estimate); + }); + + $this->estimates()->delete(); + } + + if ($this->items()->exists()) { + $this->items()->delete(); + } + + if ($this->taxTypes()->exists()) { + $this->taxTypes()->delete(); + } + + if ($this->customers()->exists()) { + $this->customers->map(function ($customer) { + if ($customer->addresses()->exists()) { + $customer->addresses()->delete(); + } + + $customer->delete(); + }); + } + + $roles = Role::when($this->id, function ($query) { + return $query->where('scope', $this->id); + })->get(); + + if ($roles) { + $roles->map(function ($role) { + $role->delete(); + }); + } + + if ($this->users()->exists()) { + $user->companies()->detach($this->id); + } + + $this->settings()->delete(); + + $this->address()->delete(); + + $this->delete(); + + return true; + } + + public function checkModelData($model) + { + $model->items->map(function ($item) { + if ($item->taxes()->exists()) { + $item->taxes()->delete(); + } + + $item->delete(); + }); + + if ($model->taxes()->exists()) { + $model->taxes()->delete(); + } + } + + public function hasTransactions() + { + if ( + $this->customers()->exists() || + $this->items()->exists() || + $this->invoices()->exists() || + $this->estimates()->exists() || + $this->expenses()->exists() || + $this->payments()->exists() || + $this->recurringInvoices()->exists() + ) { + return true; + } + + return false; + } +} diff --git a/crater/app/Models/CompanySetting.php b/crater/app/Models/CompanySetting.php new file mode 100644 index 0000000..d1927c2 --- /dev/null +++ b/crater/app/Models/CompanySetting.php @@ -0,0 +1,66 @@ +belongsTo(Company::class); + } + + public function scopeWhereCompany($query, $company_id) + { + $query->where('company_id', $company_id); + } + + public static function setSettings($settings, $company_id) + { + foreach ($settings as $key => $value) { + self::updateOrCreate( + [ + 'option' => $key, + 'company_id' => $company_id, + ], + [ + 'option' => $key, + 'company_id' => $company_id, + 'value' => $value, + ] + ); + } + } + + public static function getAllSettings($company_id) + { + return static::whereCompany($company_id)->get()->mapWithKeys(function ($item) { + return [$item['option'] => $item['value']]; + }); + } + + public static function getSettings($settings, $company_id) + { + return static::whereIn('option', $settings)->whereCompany($company_id) + ->get()->mapWithKeys(function ($item) { + return [$item['option'] => $item['value']]; + }); + } + + public static function getSetting($key, $company_id) + { + $setting = static::whereOption($key)->whereCompany($company_id)->first(); + + if ($setting) { + return $setting->value; + } else { + return null; + } + } +} diff --git a/crater/app/Models/Country.php b/crater/app/Models/Country.php new file mode 100644 index 0000000..e169da3 --- /dev/null +++ b/crater/app/Models/Country.php @@ -0,0 +1,16 @@ +hasMany(Address::class); + } +} diff --git a/crater/app/Models/Currency.php b/crater/app/Models/Currency.php new file mode 100644 index 0000000..b852f11 --- /dev/null +++ b/crater/app/Models/Currency.php @@ -0,0 +1,15 @@ + 'array', + ]; + + public function setTimeAnswerAttribute($value) + { + if ($value && $value != null) { + $this->attributes['time_answer'] = date("H:i:s", strtotime($value)); + } + } + + public function setOptionsAttribute($value) + { + $this->attributes['options'] = json_encode($value); + } + + public function getDefaultAnswerAttribute() + { + $value_type = getCustomFieldValueKey($this->type); + + return $this->$value_type; + } + + public function getInUseAttribute() + { + return $this->customFieldValues()->exists(); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function customFieldValues() + { + return $this->hasMany(CustomFieldValue::class); + } + + public function scopeWhereCompany($query) + { + return $query->where('custom_fields.company_id', request()->header('company')); + } + + public function scopeWhereSearch($query, $search) + { + $query->where(function ($query) use ($search) { + $query->where('label', 'LIKE', '%'.$search.'%') + ->orWhere('name', 'LIKE', '%'.$search.'%'); + }); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('type')) { + $query->whereType($filters->get('type')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + } + + public function scopeWhereType($query, $type) + { + $query->where('custom_fields.model_type', $type); + } + + public static function createCustomField($request) + { + $data = $request->validated(); + $data[getCustomFieldValueKey($request->type)] = $request->default_answer; + $data['company_id'] = $request->header('company'); + $data['slug'] = clean_slug($request->model_type, $request->name); + + return CustomField::create($data); + } + + public function updateCustomField($request) + { + $data = $request->validated(); + $data[getCustomFieldValueKey($request->type)] = $request->default_answer; + $this->update($data); + + return $this; + } +} diff --git a/crater/app/Models/CustomFieldValue.php b/crater/app/Models/CustomFieldValue.php new file mode 100644 index 0000000..b80cefb --- /dev/null +++ b/crater/app/Models/CustomFieldValue.php @@ -0,0 +1,55 @@ +attributes['time_answer'] = date("H:i:s", strtotime($value)); + } else { + $this->attributes['time_answer'] = null; + } + } + + public function getDefaultAnswerAttribute() + { + $value_type = getCustomFieldValueKey($this->type); + + return $this->$value_type; + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function customField() + { + return $this->belongsTo(CustomField::class); + } + + public function customFieldValuable() + { + return $this->morphTo(); + } +} diff --git a/crater/app/Models/Customer.php b/crater/app/Models/Customer.php new file mode 100644 index 0000000..8b6a5af --- /dev/null +++ b/crater/app/Models/Customer.php @@ -0,0 +1,341 @@ + 'boolean', + ]; + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function setPasswordAttribute($value) + { + if ($value != null) { + $this->attributes['password'] = bcrypt($value); + } + } + + public function estimates() + { + return $this->hasMany(Estimate::class); + } + + public function expenses() + { + return $this->hasMany(Expense::class); + } + + public function invoices() + { + return $this->hasMany(Invoice::class); + } + + public function payments() + { + return $this->hasMany(Payment::class); + } + + public function addresses() + { + return $this->hasMany(Address::class); + } + + public function recurringInvoices() + { + return $this->hasMany(RecurringInvoice::class); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function creator() + { + return $this->belongsTo(Customer::class, 'creator_id'); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function billingAddress() + { + return $this->hasOne(Address::class)->where('type', Address::BILLING_TYPE); + } + + public function shippingAddress() + { + return $this->hasOne(Address::class)->where('type', Address::SHIPPING_TYPE); + } + + public function sendPasswordResetNotification($token) + { + $this->notify(new CustomerMailResetPasswordNotification($token)); + } + + public function getAvatarAttribute() + { + $avatar = $this->getMedia('customer_avatar')->first(); + + if ($avatar) { + return asset($avatar->getUrl()); + } + + return 0; + } + + public static function deleteCustomers($ids) + { + foreach ($ids as $id) { + $customer = self::find($id); + + if ($customer->estimates()->exists()) { + $customer->estimates()->delete(); + } + + if ($customer->invoices()->exists()) { + $customer->invoices->map(function ($invoice) { + if ($invoice->transactions()->exists()) { + $invoice->transactions()->delete(); + } + $invoice->delete(); + }); + } + + if ($customer->payments()->exists()) { + $customer->payments()->delete(); + } + + if ($customer->addresses()->exists()) { + $customer->addresses()->delete(); + } + + if ($customer->expenses()->exists()) { + $customer->expenses()->delete(); + } + + if ($customer->recurringInvoices()->exists()) { + foreach ($customer->recurringInvoices as $recurringInvoice) { + if ($recurringInvoice->items()->exists()) { + $recurringInvoice->items()->delete(); + } + + $recurringInvoice->delete(); + } + } + + $customer->delete(); + } + + return true; + } + + public static function createCustomer($request) + { + $customer = Customer::create($request->getCustomerPayload()); + + if ($request->shipping) { + if ($request->hasAddress($request->shipping)) { + $customer->addresses()->create($request->getShippingAddress()); + } + } + + if ($request->billing) { + if ($request->hasAddress($request->billing)) { + $customer->addresses()->create($request->getBillingAddress()); + } + } + + $customFields = $request->customFields; + + if ($customFields) { + $customer->addCustomFields($customFields); + } + + $customer = Customer::with('billingAddress', 'shippingAddress', 'fields')->find($customer->id); + + return $customer; + } + + public static function updateCustomer($request, $customer) + { + $condition = $customer->estimates()->exists() || $customer->invoices()->exists() || $customer->payments()->exists() || $customer->recurringInvoices()->exists(); + + if (($customer->currency_id !== $request->currency_id) && $condition) { + return 'you_cannot_edit_currency'; + } + + $customer->update($request->getCustomerPayload()); + + $customer->addresses()->delete(); + + if ($request->shipping) { + if ($request->hasAddress($request->shipping)) { + $customer->addresses()->create($request->getShippingAddress()); + } + } + + if ($request->billing) { + if ($request->hasAddress($request->billing)) { + $customer->addresses()->create($request->getBillingAddress()); + } + } + + $customFields = $request->customFields; + + if ($customFields) { + $customer->updateCustomFields($customFields); + } + + $customer = Customer::with('billingAddress', 'shippingAddress', 'fields')->find($customer->id); + + return $customer; + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeWhereCompany($query) + { + return $query->where('customers.company_id', request()->header('company')); + } + + public function scopeWhereContactName($query, $contactName) + { + return $query->where('contact_name', 'LIKE', '%'.$contactName.'%'); + } + + public function scopeWhereDisplayName($query, $displayName) + { + return $query->where('name', 'LIKE', '%'.$displayName.'%'); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->where(function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('email', 'LIKE', '%'.$term.'%') + ->orWhere('phone', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeWherePhone($query, $phone) + { + return $query->where('phone', 'LIKE', '%'.$phone.'%'); + } + + public function scopeWhereCustomer($query, $customer_id) + { + $query->orWhere('customers.id', $customer_id); + } + + public function scopeApplyInvoiceFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->invoicesBetween($start, $end); + } + } + + public function scopeInvoicesBetween($query, $start, $end) + { + $query->whereHas('invoices', function ($query) use ($start, $end) { + $query->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('contact_name')) { + $query->whereContactName($filters->get('contact_name')); + } + + if ($filters->get('display_name')) { + $query->whereDisplayName($filters->get('display_name')); + } + + if ($filters->get('customer_id')) { + $query->whereCustomer($filters->get('customer_id')); + } + + if ($filters->get('phone')) { + $query->wherePhone($filters->get('phone')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } +} diff --git a/crater/app/Models/EmailLog.php b/crater/app/Models/EmailLog.php new file mode 100644 index 0000000..d4f253b --- /dev/null +++ b/crater/app/Models/EmailLog.php @@ -0,0 +1,33 @@ +morphTo(); + } + + public function isExpired() + { + $linkexpiryDays = CompanySetting::getSetting('link_expiry_days', $this->mailable()->get()->toArray()[0]['company_id']); + $checkExpiryLinks = CompanySetting::getSetting('automatically_expire_public_links', $this->mailable()->get()->toArray()[0]['company_id']); + + $expiryDate = $this->created_at->addDays($linkexpiryDays); + + if ($checkExpiryLinks == 'YES' && Carbon::now()->format('Y-m-d') > $expiryDate->format('Y-m-d')) { + return true; + } + + return false; + } +} diff --git a/crater/app/Models/Estimate.php b/crater/app/Models/Estimate.php new file mode 100644 index 0000000..5b7c3b8 --- /dev/null +++ b/crater/app/Models/Estimate.php @@ -0,0 +1,539 @@ + 'integer', + 'tax' => 'integer', + 'sub_total' => 'integer', + 'discount' => 'float', + 'discount_val' => 'integer', + 'exchange_rate' => 'float' + ]; + + public function getEstimatePdfUrlAttribute() + { + return url('/estimates/pdf/'.$this->unique_hash); + } + + public function emailLogs() + { + return $this->morphMany('App\Models\EmailLog', 'mailable'); + } + + public function items() + { + return $this->hasMany('Crater\Models\EstimateItem'); + } + + public function customer() + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + public function creator() + { + return $this->belongsTo('Crater\Models\User', 'creator_id'); + } + + public function company() + { + return $this->belongsTo('Crater\Models\Company'); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function getFormattedExpiryDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->expiry_date)->format($dateFormat); + } + + public function getFormattedEstimateDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->estimate_date)->format($dateFormat); + } + + public function scopeEstimatesBetween($query, $start, $end) + { + return $query->whereBetween( + 'estimates.estimate_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereStatus($query, $status) + { + return $query->where('estimates.status', $status); + } + + public function scopeWhereEstimateNumber($query, $estimateNumber) + { + return $query->where('estimates.estimate_number', 'LIKE', '%'.$estimateNumber.'%'); + } + + public function scopeWhereEstimate($query, $estimate_id) + { + $query->orWhere('id', $estimate_id); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('customer', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('contact_name', 'LIKE', '%'.$term.'%') + ->orWhere('company_name', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('estimate_number')) { + $query->whereEstimateNumber($filters->get('estimate_number')); + } + + if ($filters->get('status')) { + $query->whereStatus($filters->get('status')); + } + + if ($filters->get('estimate_id')) { + $query->whereEstimate($filters->get('estimate_id')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->estimatesBetween($start, $end); + } + + if ($filters->get('customer_id')) { + $query->whereCustomer($filters->get('customer_id')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereCompany($query) + { + $query->where('estimates.company_id', request()->header('company')); + } + + public function scopeWhereCustomer($query, $customer_id) + { + $query->where('estimates.customer_id', $customer_id); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public static function createEstimate($request) + { + $data = $request->getEstimatePayload(); + + if ($request->has('estimateSend')) { + $data['status'] = self::STATUS_SENT; + } + + $estimate = self::create($data); + $estimate->unique_hash = Hashids::connection(Estimate::class)->encode($estimate->id); + $serial = (new SerialNumberFormatter()) + ->setModel($estimate) + ->setCompany($estimate->company_id) + ->setCustomer($estimate->customer_id) + ->setNextNumbers(); + + $estimate->sequence_number = $serial->nextSequenceNumber; + $estimate->customer_sequence_number = $serial->nextCustomerSequenceNumber; + $estimate->save(); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($estimate); + } + + self::createItems($estimate, $request, $estimate->exchange_rate); + + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($estimate, $request, $estimate->exchange_rate); + } + + $customFields = $request->customFields; + + if ($customFields) { + $estimate->addCustomFields($customFields); + } + + return $estimate; + } + + public function updateEstimate($request) + { + $data = $request->getEstimatePayload(); + + $serial = (new SerialNumberFormatter()) + ->setModel($this) + ->setCompany($this->company_id) + ->setCustomer($request->customer_id) + ->setModelObject($this->id) + ->setNextNumbers(); + + $data['customer_sequence_number'] = $serial->nextCustomerSequenceNumber; + + $this->update($data); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($this); + } + + $this->items->map(function ($item) { + $fields = $item->fields()->get(); + + $fields->map(function ($field) { + $field->delete(); + }); + }); + + $this->items()->delete(); + $this->taxes()->delete(); + + self::createItems($this, $request, $this->exchange_rate); + + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($this, $request, $this->exchange_rate); + } + + if ($request->customFields) { + $this->updateCustomFields($request->customFields); + } + + return Estimate::with([ + 'items.taxes', + 'items.fields', + 'items.fields.customField', + 'customer', + 'taxes' + ]) + ->find($this->id); + } + + public static function createItems($estimate, $request, $exchange_rate) + { + $estimateItems = $request->items; + + foreach ($estimateItems as $estimateItem) { + $estimateItem['company_id'] = $request->header('company'); + $estimateItem['exchange_rate'] = $exchange_rate; + $estimateItem['base_price'] = $estimateItem['price'] * $exchange_rate; + $estimateItem['base_discount_val'] = $estimateItem['discount_val'] * $exchange_rate; + $estimateItem['base_tax'] = $estimate['tax'] * $exchange_rate; + $estimateItem['base_total'] = $estimateItem['total'] * $exchange_rate; + + $item = $estimate->items()->create($estimateItem); + + if (array_key_exists('taxes', $estimateItem) && $estimateItem['taxes']) { + foreach ($estimateItem['taxes'] as $tax) { + if (gettype($tax['amount']) !== "NULL") { + $tax['company_id'] = $request->header('company'); + $item->taxes()->create($tax); + } + } + } + + if (array_key_exists('custom_fields', $estimateItem) && $estimateItem['custom_fields']) { + $item->addCustomFields($estimateItem['custom_fields']); + } + } + } + + public static function createTaxes($estimate, $request, $exchange_rate) + { + $estimateTaxes = $request->taxes; + + foreach ($estimateTaxes as $tax) { + if (gettype($tax['amount']) !== "NULL") { + $tax['company_id'] = $request->header('company'); + $tax['exchange_rate'] = $exchange_rate; + $tax['base_amount'] = $tax['amount'] * $exchange_rate; + $tax['currency_id'] = $estimate->currency_id; + + $estimate->taxes()->create($tax); + } + } + } + + public function sendEstimateData($data) + { + $data['estimate'] = $this->toArray(); + $data['user'] = $this->customer->toArray(); + $data['company'] = $this->company->toArray(); + $data['body'] = $this->getEmailBody($data['body']); + $data['attach']['data'] = ($this->getEmailAttachmentSetting()) ? $this->getPDFData() : null; + + return $data; + } + + public function send($data) + { + $data = $this->sendEstimateData($data); + + if ($this->status == Estimate::STATUS_DRAFT) { + $this->status = Estimate::STATUS_SENT; + $this->save(); + } + + \Mail::to($data['to'])->send(new SendEstimateMail($data)); + + return [ + 'success' => true, + 'type' => 'send', + ]; + } + + public function getPDFData() + { + $taxes = collect(); + + if ($this->tax_per_item === 'YES') { + foreach ($this->items as $item) { + foreach ($item->taxes as $tax) { + $found = $taxes->filter(function ($item) use ($tax) { + return $item->tax_type_id == $tax->tax_type_id; + })->first(); + + if ($found) { + $found->amount += $tax->amount; + } else { + $taxes->push($tax); + } + } + } + } + + $estimateTemplate = self::find($this->id)->template_name; + + $company = Company::find($this->company_id); + $locale = CompanySetting::getSetting('language', $company->id); + $customFields = CustomField::where('model_type', 'Item')->get(); + + App::setLocale($locale); + + $logo = $company->logo_path; + + view()->share([ + 'estimate' => $this, + 'customFields' => $customFields, + 'logo' => $logo ?? null, + 'company_address' => $this->getCompanyAddress(), + 'shipping_address' => $this->getCustomerShippingAddress(), + 'billing_address' => $this->getCustomerBillingAddress(), + 'notes' => $this->getNotes(), + 'taxes' => $taxes, + ]); + + if (request()->has('preview')) { + return view('app.pdf.estimate.'.$estimateTemplate); + } + + return PDF::loadView('app.pdf.estimate.'.$estimateTemplate); + } + + public function getCompanyAddress() + { + if ($this->company && (! $this->company->address()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('estimate_company_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getCustomerShippingAddress() + { + if ($this->customer && (! $this->customer->shippingAddress()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('estimate_shipping_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getCustomerBillingAddress() + { + if ($this->customer && (! $this->customer->billingAddress()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('estimate_billing_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getNotes() + { + return $this->getFormattedString($this->notes); + } + + public function getEmailAttachmentSetting() + { + $estimateAsAttachment = CompanySetting::getSetting('estimate_email_attachment', $this->company_id); + + if ($estimateAsAttachment == 'NO') { + return false; + } + + return true; + } + + public function getEmailBody($body) + { + $values = array_merge($this->getFieldsArray(), $this->getExtraFields()); + + $body = strtr($body, $values); + + return preg_replace('/{(.*?)}/', '', $body); + } + + public function getExtraFields() + { + return [ + '{ESTIMATE_DATE}' => $this->formattedEstimateDate, + '{ESTIMATE_EXPIRY_DATE}' => $this->formattedExpiryDate, + '{ESTIMATE_NUMBER}' => $this->estimate_number, + '{ESTIMATE_REF_NUMBER}' => $this->reference_number, + ]; + } + + public static function estimateTemplates() + { + $templates = Storage::disk('views')->files('/app/pdf/estimate'); + $estimateTemplates = []; + + foreach ($templates as $key => $template) { + $templateName = Str::before(basename($template), '.blade.php'); + $estimateTemplates[$key]['name'] = $templateName; + $estimateTemplates[$key]['path'] = vite_asset('/img/PDF/'.$templateName.'.png'); + } + + return $estimateTemplates; + } + + public function getInvoiceTemplateName() + { + $templateName = Str::replace('estimate', 'invoice', $this->template_name); + + $name = []; + + foreach (Invoice::invoiceTemplates() as $template) { + $name[] = $template['name']; + } + + if (in_array($templateName, $name) == false) { + $templateName = 'invoice1'; + } + + return $templateName; + } + + public function checkForEstimateConvertAction() + { + $convertEstimateAction = CompanySetting::getSetting( + 'estimate_convert_action', + $this->company_id + ); + + if ($convertEstimateAction === 'delete_estimate') { + $this->delete(); + } + + if ($convertEstimateAction === 'mark_estimate_as_accepted') { + $this->status = self::STATUS_ACCEPTED; + $this->save(); + } + + return true; + } +} diff --git a/crater/app/Models/EstimateItem.php b/crater/app/Models/EstimateItem.php new file mode 100644 index 0000000..d38a2b6 --- /dev/null +++ b/crater/app/Models/EstimateItem.php @@ -0,0 +1,46 @@ + 'integer', + 'total' => 'integer', + 'discount' => 'float', + 'quantity' => 'float', + 'discount_val' => 'integer', + 'tax' => 'integer', + ]; + + public function estimate() + { + return $this->belongsTo(Estimate::class); + } + + public function item() + { + return $this->belongsTo(Item::class); + } + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function scopeWhereCompany($query, $company_id) + { + $query->where('company_id', $company_id); + } +} diff --git a/crater/app/Models/ExchangeRateLog.php b/crater/app/Models/ExchangeRateLog.php new file mode 100644 index 0000000..72a9639 --- /dev/null +++ b/crater/app/Models/ExchangeRateLog.php @@ -0,0 +1,41 @@ + 'float' + ]; + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public static function addExchangeRateLog($model) + { + $data = [ + 'exchange_rate' => $model->exchange_rate, + 'company_id' => $model->company_id, + 'base_currency_id' => $model->currency_id, + 'currency_id' => CompanySetting::getSetting('currency', $model->company_id), + ]; + + return self::create($data); + } +} diff --git a/crater/app/Models/ExchangeRateProvider.php b/crater/app/Models/ExchangeRateProvider.php new file mode 100644 index 0000000..d02ae51 --- /dev/null +++ b/crater/app/Models/ExchangeRateProvider.php @@ -0,0 +1,166 @@ + 'array', + 'driver_config' => 'array', + 'active' => 'boolean' + ]; + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function setCurrenciesAttribute($value) + { + $this->attributes['currencies'] = json_encode($value); + } + + public function setDriverConfigAttribute($value) + { + $this->attributes['driver_config'] = json_encode($value); + } + + public function scopeWhereCompany($query) + { + $query->where('exchange_rate_providers.company_id', request()->header('company')); + } + + public static function createFromRequest(ExchangeRateProviderRequest $request) + { + $exchangeRateProvider = self::create($request->getExchangeRateProviderPayload()); + + return $exchangeRateProvider; + } + + public function updateFromRequest(ExchangeRateProviderRequest $request) + { + $this->update($request->getExchangeRateProviderPayload()); + + return $this; + } + + public static function checkActiveCurrencies($request) + { + $query = ExchangeRateProvider::whereJsonContains('currencies', $request->currencies) + ->where('active', true) + ->get(); + + return $query; + } + + public function checkUpdateActiveCurrencies($request) + { + $query = ExchangeRateProvider::where('active', $request->active) + ->where('id', '<>', $this->id) + ->whereJsonContains('currencies', $request->currencies) + ->get(); + + return $query; + } + + public static function checkExchangeRateProviderStatus($request) + { + switch ($request['driver']) { + case 'currency_freak': + $url = "https://api.currencyfreaks.com/latest?apikey=".$request['key']."&symbols=INR&base=USD"; + $response = Http::get($url)->json(); + + if (array_key_exists('success', $response)) { + if ($response["success"] == false) { + return respondJson($response["error"]["message"], $response["error"]["message"]); + } + } + + return response()->json([ + 'exchangeRate' => array_values($response["rates"]), + ], 200); + + break; + + case 'currency_layer': + $url = "http://api.currencylayer.com/live?access_key=".$request['key']."&source=INR¤cies=USD"; + $response = Http::get($url)->json(); + + if (array_key_exists('success', $response)) { + if ($response["success"] == false) { + return respondJson($response["error"]["info"], $response["error"]["info"]); + } + } + + return response()->json([ + 'exchangeRate' => array_values($response['quotes']), + ], 200); + + break; + + case 'open_exchange_rate': + $url = "https://openexchangerates.org/api/latest.json?app_id=".$request['key']."&base=INR&symbols=USD"; + $response = Http::get($url)->json(); + + if (array_key_exists("error", $response)) { + return respondJson($response['message'], $response["description"]); + } + + return response()->json([ + 'exchangeRate' => array_values($response["rates"]), + ], 200); + + break; + + case 'currency_converter': + $url = self::getCurrencyConverterUrl($request['driver_config']); + $url = $url."/api/v7/convert?apiKey=".$request['key']; + + $query = "INR_USD"; + $url = $url."&q={$query}"."&compact=y"; + $response = Http::get($url)->json(); + + return response()->json([ + 'exchangeRate' => array_values($response[$query]), + ], 200); + + break; + } + } + + public static function getCurrencyConverterUrl($data) + { + switch ($data['type']) { + case 'PREMIUM': + return "https://api.currconv.com"; + + break; + + case 'PREPAID': + return "https://prepaid.currconv.com"; + + break; + + case 'FREE': + return "https://free.currconv.com"; + + break; + + case 'DEDICATED': + return $data['url']; + + break; + } + } +} diff --git a/crater/app/Models/Expense.php b/crater/app/Models/Expense.php new file mode 100644 index 0000000..bd0aa1e --- /dev/null +++ b/crater/app/Models/Expense.php @@ -0,0 +1,279 @@ + 'string', + 'exchange_rate' => 'float' + ]; + + public function category() + { + return $this->belongsTo(ExpenseCategory::class, 'expense_category_id'); + } + + public function customer() + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + public function company() + { + return $this->belongsTo(Company::class, 'company_id'); + } + + public function paymentMethod() + { + return $this->belongsTo(PaymentMethod::class); + } + + public function currency() + { + return $this->belongsTo(Currency::class, 'currency_id'); + } + + public function creator() + { + return $this->belongsTo('Crater\Models\User', 'creator_id'); + } + + public function getFormattedExpenseDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->expense_date)->format($dateFormat); + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function getReceiptUrlAttribute($value) + { + $media = $this->getFirstMedia('receipts'); + + if ($media) { + return [ + 'url' => $media->getFullUrl(), + 'type' => $media->type + ]; + } + + return null; + } + + public function getReceiptAttribute($value) + { + $media = $this->getFirstMedia('receipts'); + + if ($media) { + return $media->getPath(); + } + + return null; + } + + public function getReceiptMetaAttribute($value) + { + $media = $this->getFirstMedia('receipts'); + + if ($media) { + return $media; + } + + return null; + } + + public function scopeExpensesBetween($query, $start, $end) + { + return $query->whereBetween( + 'expenses.expense_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereCategoryName($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('category', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeWhereNotes($query, $search) + { + $query->where('notes', 'LIKE', '%'.$search.'%'); + } + + public function scopeWhereCategory($query, $categoryId) + { + return $query->where('expenses.expense_category_id', $categoryId); + } + + public function scopeWhereUser($query, $customer_id) + { + return $query->where('expenses.customer_id', $customer_id); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('expense_category_id')) { + $query->whereCategory($filters->get('expense_category_id')); + } + + if ($filters->get('customer_id')) { + $query->whereUser($filters->get('customer_id')); + } + + if ($filters->get('expense_id')) { + $query->whereExpense($filters->get('expense_id')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->expensesBetween($start, $end); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'expense_date'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + } + + public function scopeWhereExpense($query, $expense_id) + { + $query->orWhere('id', $expense_id); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('category', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%'); + }) + ->orWhere('notes', 'LIKE', '%'.$term.'%'); + } + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereCompany($query) + { + $query->where('expenses.company_id', request()->header('company')); + } + + public function scopeWhereCompanyId($query, $company) + { + $query->where('expenses.company_id', $company); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeExpensesAttributes($query) + { + $query->select( + DB::raw(' + count(*) as expenses_count, + sum(base_amount) as total_amount, + expense_category_id') + ) + ->groupBy('expense_category_id'); + } + + public static function createExpense($request) + { + $expense = self::create($request->getExpensePayload()); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$expense['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($expense); + } + + if ($request->hasFile('attachment_receipt')) { + $expense->addMediaFromRequest('attachment_receipt')->toMediaCollection('receipts'); + } + + if ($request->customFields) { + $expense->addCustomFields(json_decode($request->customFields)); + } + + return $expense; + } + + public function updateExpense($request) + { + $data = $request->getExpensePayload(); + + $this->update($data); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($this); + } + + if (isset($request->is_attachment_receipt_removed) && (bool) $request->is_attachment_receipt_removed) { + $this->clearMediaCollection('receipts'); + } + if ($request->hasFile('attachment_receipt')) { + $this->clearMediaCollection('receipts'); + $this->addMediaFromRequest('attachment_receipt')->toMediaCollection('receipts'); + } + + if ($request->customFields) { + $this->updateCustomFields(json_decode($request->customFields)); + } + + return true; + } +} diff --git a/crater/app/Models/ExpenseCategory.php b/crater/app/Models/ExpenseCategory.php new file mode 100644 index 0000000..71dca9b --- /dev/null +++ b/crater/app/Models/ExpenseCategory.php @@ -0,0 +1,84 @@ +hasMany(Expense::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function getAmountAttribute() + { + return $this->expenses()->sum('amount'); + } + + public function scopeWhereCompany($query) + { + $query->where('company_id', request()->header('company')); + } + + public function scopeWhereCategory($query, $category_id) + { + $query->orWhere('id', $category_id); + } + + public function scopeWhereSearch($query, $search) + { + $query->where('name', 'LIKE', '%'.$search.'%'); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('category_id')) { + $query->whereCategory($filters->get('category_id')); + } + + if ($filters->get('company_id')) { + $query->whereCompany($filters->get('company_id')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } +} diff --git a/crater/app/Models/FileDisk.php b/crater/app/Models/FileDisk.php new file mode 100644 index 0000000..a435bf7 --- /dev/null +++ b/crater/app/Models/FileDisk.php @@ -0,0 +1,204 @@ + 'boolean', + ]; + + public function setCredentialsAttribute($value) + { + $this->attributes['credentials'] = json_encode($value); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeFileDisksBetween($query, $start, $end) + { + return $query->whereBetween( + 'file_disks.created_at', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('driver', 'LIKE', '%'.$term.'%'); + } + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->fileDisksBetween($start, $end); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } + + public function setConfig() + { + $driver = $this->driver; + + $credentials = collect(json_decode($this['credentials'])); + + self::setFilesystem($credentials, $driver); + } + + public function setAsDefault() + { + return $this->set_as_default; + } + + public static function setFilesystem($credentials, $driver) + { + $prefix = env('DYNAMIC_DISK_PREFIX', 'temp_'); + + config(['filesystems.default' => $prefix.$driver]); + + $disks = config('filesystems.disks.'.$driver); + + foreach ($disks as $key => $value) { + if ($credentials->has($key)) { + $disks[$key] = $credentials[$key]; + } + } + + config(['filesystems.disks.'.$prefix.$driver => $disks]); + } + + public static function validateCredentials($credentials, $disk) + { + $exists = false; + + self::setFilesystem(collect($credentials), $disk); + + $prefix = env('DYNAMIC_DISK_PREFIX', 'temp_'); + + try { + $root = ''; + if ($disk == 'dropbox') { + $root = $credentials['root'].'/'; + } + \Storage::disk($prefix.$disk)->put($root.'crater_temp.text', 'Check Credentials'); + + if (\Storage::disk($prefix.$disk)->exists($root.'crater_temp.text')) { + $exists = true; + \Storage::disk($prefix.$disk)->delete($root.'crater_temp.text'); + } + } catch (\Exception $e) { + $exists = false; + } + + return $exists; + } + + public static function createDisk($request) + { + if ($request->set_as_default) { + self::updateDefaultDisks(); + } + + $disk = self::create([ + 'credentials' => $request->credentials, + 'name' => $request->name, + 'driver' => $request->driver, + 'set_as_default' => $request->set_as_default, + 'company_id' => $request->header('company'), + ]); + + return $disk; + } + + public static function updateDefaultDisks() + { + $disks = self::get(); + + foreach ($disks as $disk) { + $disk->set_as_default = false; + $disk->save(); + } + + return true; + } + + public function updateDisk($request) + { + $data = [ + 'credentials' => $request->credentials, + 'name' => $request->name, + 'driver' => $request->driver, + ]; + + if (! $this->setAsDefault()) { + if ($request->set_as_default) { + self::updateDefaultDisks(); + } + + $data['set_as_default'] = $request->set_as_default; + } + + $this->update($data); + + return $this; + } + + public function setAsDefaultDisk() + { + self::updateDefaultDisks(); + + $this->set_as_default = true; + $this->save(); + + return $this; + } + + public function isSystem() + { + return $this->type === self::DISK_TYPE_SYSTEM; + } + + public function isRemote() + { + return $this->type === self::DISK_TYPE_REMOTE; + } +} diff --git a/crater/app/Models/Invoice.php b/crater/app/Models/Invoice.php new file mode 100644 index 0000000..39cd316 --- /dev/null +++ b/crater/app/Models/Invoice.php @@ -0,0 +1,725 @@ + 'integer', + 'tax' => 'integer', + 'sub_total' => 'integer', + 'discount' => 'float', + 'discount_val' => 'integer', + 'exchange_rate' => 'float' + ]; + + protected $guarded = [ + 'id', + ]; + + protected $appends = [ + 'formattedCreatedAt', + 'formattedInvoiceDate', + 'formattedDueDate', + 'invoicePdfUrl', + ]; + + public function transactions() + { + return $this->hasMany(Transaction::class); + } + + public function emailLogs() + { + return $this->morphMany('App\Models\EmailLog', 'mailable'); + } + + public function items() + { + return $this->hasMany('Crater\Models\InvoiceItem'); + } + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function payments() + { + return $this->hasMany(Payment::class); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function customer() + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + public function recurringInvoice() + { + return $this->belongsTo(RecurringInvoice::class); + } + + public function creator() + { + return $this->belongsTo(User::class, 'creator_id'); + } + + public function getInvoicePdfUrlAttribute() + { + return url('/invoices/pdf/'.$this->unique_hash); + } + + public function getPaymentModuleEnabledAttribute() + { + if (Module::has('Payments')) { + return Module::isEnabled('Payments'); + } + + return false; + } + + public function getAllowEditAttribute() + { + $retrospective_edit = CompanySetting::getSetting('retrospective_edits', $this->company_id); + + $allowed = true; + + $status = [ + self::STATUS_DRAFT, + self::STATUS_SENT, + self::STATUS_VIEWED, + self::STATUS_COMPLETED, + ]; + + if ($retrospective_edit == 'disable_on_invoice_sent' && (in_array($this->status, $status)) && ($this->paid_status === Invoice::STATUS_PARTIALLY_PAID || $this->paid_status === Invoice::STATUS_PAID)) { + $allowed = false; + } elseif ($retrospective_edit == 'disable_on_invoice_partial_paid' && ($this->paid_status === Invoice::STATUS_PARTIALLY_PAID || $this->paid_status === Invoice::STATUS_PAID)) { + $allowed = false; + } elseif ($retrospective_edit == 'disable_on_invoice_paid' && $this->paid_status === Invoice::STATUS_PAID) { + $allowed = false; + } + + return $allowed; + } + + public function getPreviousStatus() + { + if ($this->viewed) { + return self::STATUS_VIEWED; + } elseif ($this->sent) { + return self::STATUS_SENT; + } else { + return self::STATUS_DRAFT; + } + } + + public function getFormattedNotesAttribute($value) + { + return $this->getNotes(); + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function getFormattedDueDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->due_date)->format($dateFormat); + } + + public function getFormattedInvoiceDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->invoice_date)->format($dateFormat); + } + + public function scopeWhereStatus($query, $status) + { + return $query->where('invoices.status', $status); + } + + public function scopeWherePaidStatus($query, $status) + { + return $query->where('invoices.paid_status', $status); + } + + public function scopeWhereDueStatus($query, $status) + { + return $query->whereIn('invoices.paid_status', [ + self::STATUS_UNPAID, + self::STATUS_PARTIALLY_PAID, + ]); + } + + public function scopeWhereInvoiceNumber($query, $invoiceNumber) + { + return $query->where('invoices.invoice_number', 'LIKE', '%'.$invoiceNumber.'%'); + } + + public function scopeInvoicesBetween($query, $start, $end) + { + return $query->whereBetween( + 'invoices.invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('customer', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('contact_name', 'LIKE', '%'.$term.'%') + ->orWhere('company_name', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('status')) { + if ( + $filters->get('status') == self::STATUS_UNPAID || + $filters->get('status') == self::STATUS_PARTIALLY_PAID || + $filters->get('status') == self::STATUS_PAID + ) { + $query->wherePaidStatus($filters->get('status')); + } elseif ($filters->get('status') == 'DUE') { + $query->whereDueStatus($filters->get('status')); + } else { + $query->whereStatus($filters->get('status')); + } + } + + if ($filters->get('paid_status')) { + $query->wherePaidStatus($filters->get('status')); + } + + if ($filters->get('invoice_id')) { + $query->whereInvoice($filters->get('invoice_id')); + } + + if ($filters->get('invoice_number')) { + $query->whereInvoiceNumber($filters->get('invoice_number')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->invoicesBetween($start, $end); + } + + if ($filters->get('customer_id')) { + $query->whereCustomer($filters->get('customer_id')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopeWhereInvoice($query, $invoice_id) + { + $query->orWhere('id', $invoice_id); + } + + public function scopeWhereCompany($query) + { + $query->where('invoices.company_id', request()->header('company')); + } + + public function scopeWhereCompanyId($query, $company) + { + $query->where('invoices.company_id', $company); + } + + public function scopeWhereCustomer($query, $customer_id) + { + $query->where('invoices.customer_id', $customer_id); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public static function createInvoice($request) + { + $data = $request->getInvoicePayload(); + + if ($request->has('invoiceSend')) { + $data['status'] = Invoice::STATUS_SENT; + } + + $invoice = Invoice::create($data); + + $serial = (new SerialNumberFormatter()) + ->setModel($invoice) + ->setCompany($invoice->company_id) + ->setCustomer($invoice->customer_id) + ->setNextNumbers(); + + $invoice->sequence_number = $serial->nextSequenceNumber; + $invoice->customer_sequence_number = $serial->nextCustomerSequenceNumber; + $invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id); + $invoice->save(); + + self::createItems($invoice, $request->items); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($invoice); + } + + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($invoice, $request->taxes); + } + + if ($request->customFields) { + $invoice->addCustomFields($request->customFields); + } + + $invoice = Invoice::with([ + 'items', + 'items.fields', + 'items.fields.customField', + 'customer', + 'taxes' + ]) + ->find($invoice->id); + + return $invoice; + } + + public function updateInvoice($request) + { + $serial = (new SerialNumberFormatter()) + ->setModel($this) + ->setCompany($this->company_id) + ->setCustomer($request->customer_id) + ->setModelObject($this->id) + ->setNextNumbers(); + + $data = $request->getInvoicePayload(); + $oldTotal = $this->total; + + $total_paid_amount = $this->total - $this->due_amount; + + if ($total_paid_amount > 0 && $this->customer_id !== $request->customer_id) { + return 'customer_cannot_be_changed_after_payment_is_added'; + } + + if ($request->total < $total_paid_amount) { + return 'total_invoice_amount_must_be_more_than_paid_amount'; + } + + if ($oldTotal != $request->total) { + $oldTotal = (int) round($request->total) - (int) $oldTotal; + } else { + $oldTotal = 0; + } + + $data['due_amount'] = ($this->due_amount + $oldTotal); + $data['base_due_amount'] = $data['due_amount'] * $data['exchange_rate']; + $data['customer_sequence_number'] = $serial->nextCustomerSequenceNumber; + + $this->changeInvoiceStatus($data['due_amount']); + + $this->update($data); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($this); + } + + $this->items->map(function ($item) { + $fields = $item->fields()->get(); + + $fields->map(function ($field) { + $field->delete(); + }); + }); + + $this->items()->delete(); + $this->taxes()->delete(); + + self::createItems($this, $request->items); + + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($this, $request->taxes); + } + + if ($request->customFields) { + $this->updateCustomFields($request->customFields); + } + + $invoice = Invoice::with([ + 'items', + 'items.fields', + 'items.fields.customField', + 'customer', + 'taxes' + ]) + ->find($this->id); + + return $invoice; + } + + public function sendInvoiceData($data) + { + $data['invoice'] = $this->toArray(); + $data['customer'] = $this->customer->toArray(); + $data['company'] = Company::find($this->company_id); + $data['subject'] = $this->getEmailString($data['subject']); + $data['body'] = $this->getEmailString($data['body']); + $data['attach']['data'] = ($this->getEmailAttachmentSetting()) ? $this->getPDFData() : null; + + return $data; + } + + public function preview($data) + { + $data = $this->sendInvoiceData($data); + + return [ + 'type' => 'preview', + 'view' => new SendInvoiceMail($data) + ]; + } + + public function send($data) + { + $data = $this->sendInvoiceData($data); + + \Mail::to($data['to'])->send(new SendInvoiceMail($data)); + + if ($this->status == Invoice::STATUS_DRAFT) { + $this->status = Invoice::STATUS_SENT; + $this->sent = true; + $this->save(); + } + + return [ + 'success' => true, + 'type' => 'send', + ]; + } + + public static function createItems($invoice, $invoiceItems) + { + $exchange_rate = $invoice->exchange_rate; + + foreach ($invoiceItems as $invoiceItem) { + $invoiceItem['company_id'] = $invoice->company_id; + $invoiceItem['exchange_rate'] = $exchange_rate; + $invoiceItem['base_price'] = $invoiceItem['price'] * $exchange_rate; + $invoiceItem['base_discount_val'] = $invoiceItem['discount_val'] * $exchange_rate; + $invoiceItem['base_tax'] = $invoiceItem['tax'] * $exchange_rate; + $invoiceItem['base_total'] = $invoiceItem['total'] * $exchange_rate; + + if (array_key_exists('recurring_invoice_id', $invoiceItem)) { + unset($invoiceItem['recurring_invoice_id']); + } + + $item = $invoice->items()->create($invoiceItem); + + if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { + foreach ($invoiceItem['taxes'] as $tax) { + $tax['company_id'] = $invoice->company_id; + $tax['exchange_rate'] = $invoice->exchange_rate; + $tax['base_amount'] = $tax['amount'] * $exchange_rate; + $tax['currency_id'] = $invoice->currency_id; + + if (gettype($tax['amount']) !== "NULL") { + if (array_key_exists('recurring_invoice_id', $invoiceItem)) { + unset($invoiceItem['recurring_invoice_id']); + } + + $item->taxes()->create($tax); + } + } + } + + if (array_key_exists('custom_fields', $invoiceItem) && $invoiceItem['custom_fields']) { + $item->addCustomFields($invoiceItem['custom_fields']); + } + } + } + + public static function createTaxes($invoice, $taxes) + { + $exchange_rate = $invoice->exchange_rate; + + foreach ($taxes as $tax) { + $tax['company_id'] = $invoice->company_id; + $tax['exchange_rate'] = $invoice->exchange_rate; + $tax['base_amount'] = $tax['amount'] * $exchange_rate; + $tax['currency_id'] = $invoice->currency_id; + + if (gettype($tax['amount']) !== "NULL") { + if (array_key_exists('recurring_invoice_id', $tax)) { + unset($tax['recurring_invoice_id']); + } + + $invoice->taxes()->create($tax); + } + } + } + + public function getPDFData() + { + $taxes = collect(); + + if ($this->tax_per_item === 'YES') { + foreach ($this->items as $item) { + foreach ($item->taxes as $tax) { + $found = $taxes->filter(function ($item) use ($tax) { + return $item->tax_type_id == $tax->tax_type_id; + })->first(); + + if ($found) { + $found->amount += $tax->amount; + } else { + $taxes->push($tax); + } + } + } + } + + $invoiceTemplate = self::find($this->id)->template_name; + + $company = Company::find($this->company_id); + $locale = CompanySetting::getSetting('language', $company->id); + $customFields = CustomField::where('model_type', 'Item')->get(); + + App::setLocale($locale); + + $logo = $company->logo_path; + + view()->share([ + 'invoice' => $this, + 'customFields' => $customFields, + 'company_address' => $this->getCompanyAddress(), + 'shipping_address' => $this->getCustomerShippingAddress(), + 'billing_address' => $this->getCustomerBillingAddress(), + 'notes' => $this->getNotes(), + 'logo' => $logo ?? null, + 'taxes' => $taxes, + ]); + + if (request()->has('preview')) { + return view('app.pdf.invoice.'.$invoiceTemplate); + } + + return PDF::loadView('app.pdf.invoice.'.$invoiceTemplate); + } + + public function getEmailAttachmentSetting() + { + $invoiceAsAttachment = CompanySetting::getSetting('invoice_email_attachment', $this->company_id); + + if ($invoiceAsAttachment == 'NO') { + return false; + } + + return true; + } + + public function getCompanyAddress() + { + if ($this->company && (! $this->company->address()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('invoice_company_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getCustomerShippingAddress() + { + if ($this->customer && (! $this->customer->shippingAddress()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('invoice_shipping_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getCustomerBillingAddress() + { + if ($this->customer && (! $this->customer->billingAddress()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('invoice_billing_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getNotes() + { + return $this->getFormattedString($this->notes); + } + + public function getEmailString($body) + { + $values = array_merge($this->getFieldsArray(), $this->getExtraFields()); + + $body = strtr($body, $values); + + return preg_replace('/{(.*?)}/', '', $body); + } + + public function getExtraFields() + { + return [ + '{INVOICE_DATE}' => $this->formattedInvoiceDate, + '{INVOICE_DUE_DATE}' => $this->formattedDueDate, + '{INVOICE_NUMBER}' => $this->invoice_number, + '{INVOICE_REF_NUMBER}' => $this->reference_number, + ]; + } + + public static function invoiceTemplates() + { + $templates = Storage::disk('views')->files('/app/pdf/invoice'); + $invoiceTemplates = []; + + foreach ($templates as $key => $template) { + $templateName = Str::before(basename($template), '.blade.php'); + $invoiceTemplates[$key]['name'] = $templateName; + $invoiceTemplates[$key]['path'] = vite_asset('img/PDF/'.$templateName.'.png'); + } + + return $invoiceTemplates; + } + + public function addInvoicePayment($amount) + { + $this->due_amount += $amount; + $this->base_due_amount = $this->due_amount * $this->exchange_rate; + + $this->changeInvoiceStatus($this->due_amount); + } + + public function subtractInvoicePayment($amount) + { + $this->due_amount -= $amount; + $this->base_due_amount = $this->due_amount * $this->exchange_rate; + + $this->changeInvoiceStatus($this->due_amount); + } + + public function changeInvoiceStatus($amount) + { + if ($amount < 0) { + return [ + 'error' => 'invalid_amount', + ]; + } + + if ($amount == 0) { + $this->status = Invoice::STATUS_COMPLETED; + $this->paid_status = Invoice::STATUS_PAID; + $this->overdue = false; + } elseif ($amount == $this->total) { + $this->status = $this->getPreviousStatus(); + $this->paid_status = Invoice::STATUS_UNPAID; + } else { + $this->status = $this->getPreviousStatus(); + $this->paid_status = Invoice::STATUS_PARTIALLY_PAID; + } + + $this->save(); + } + + public static function deleteInvoices($ids) + { + foreach ($ids as $id) { + $invoice = self::find($id); + + if ($invoice->transactions()->exists()) { + $invoice->transactions()->delete(); + } + + $invoice->delete(); + } + + return true; + } +} diff --git a/crater/app/Models/InvoiceItem.php b/crater/app/Models/InvoiceItem.php new file mode 100644 index 0000000..2489b6f --- /dev/null +++ b/crater/app/Models/InvoiceItem.php @@ -0,0 +1,81 @@ + 'integer', + 'total' => 'integer', + 'discount' => 'float', + 'quantity' => 'float', + 'discount_val' => 'integer', + 'tax' => 'integer', + ]; + + public function invoice() + { + return $this->belongsTo(Invoice::class); + } + + public function item() + { + return $this->belongsTo(Item::class); + } + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function recurringInvoice() + { + return $this->belongsTo(RecurringInvoice::class); + } + + public function scopeWhereCompany($query, $company_id) + { + $query->where('company_id', $company_id); + } + + public function scopeInvoicesBetween($query, $start, $end) + { + $query->whereHas('invoice', function ($query) use ($start, $end) { + $query->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }); + } + + public function scopeApplyInvoiceFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->invoicesBetween($start, $end); + } + } + + public function scopeItemAttributes($query) + { + $query->select( + DB::raw('sum(quantity) as total_quantity, sum(base_total) as total_amount, invoice_items.name') + )->groupBy('invoice_items.name'); + } +} diff --git a/crater/app/Models/Item.php b/crater/app/Models/Item.php new file mode 100644 index 0000000..db1163f --- /dev/null +++ b/crater/app/Models/Item.php @@ -0,0 +1,174 @@ + 'integer', + ]; + + protected $appends = [ + 'formattedCreatedAt', + ]; + + public function unit() + { + return $this->belongsTo(Unit::class, 'unit_id'); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function creator() + { + return $this->belongsTo('Crater\Models\User', 'creator_id'); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function scopeWhereSearch($query, $search) + { + return $query->where('items.name', 'LIKE', '%'.$search.'%'); + } + + public function scopeWherePrice($query, $price) + { + return $query->where('items.price', $price); + } + + public function scopeWhereUnit($query, $unit_id) + { + return $query->where('items.unit_id', $unit_id); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereItem($query, $item_id) + { + $query->orWhere('id', $item_id); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('price')) { + $query->wherePrice($filters->get('price')); + } + + if ($filters->get('unit_id')) { + $query->whereUnit($filters->get('unit_id')); + } + + if ($filters->get('item_id')) { + $query->whereItem($filters->get('item_id')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', request()->header('company')); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function taxes() + { + return $this->hasMany(Tax::class) + ->where('invoice_item_id', null) + ->where('estimate_item_id', null); + } + + public function scopeWhereCompany($query) + { + $query->where('items.company_id', request()->header('company')); + } + + public function invoiceItems() + { + return $this->hasMany(InvoiceItem::class); + } + + public function estimateItems() + { + return $this->hasMany(EstimateItem::class); + } + + public static function createItem($request) + { + $data = $request->validated(); + $data['company_id'] = $request->header('company'); + $data['creator_id'] = Auth::id(); + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + $data['currency_id'] = $company_currency; + $item = self::create($data); + + if ($request->has('taxes')) { + foreach ($request->taxes as $tax) { + $item->tax_per_item = true; + $item->save(); + $tax['company_id'] = $request->header('company'); + $item->taxes()->create($tax); + } + } + + $item = self::with('taxes')->find($item->id); + + return $item; + } + + public function updateItem($request) + { + $this->update($request->validated()); + + $this->taxes()->delete(); + + if ($request->has('taxes')) { + foreach ($request->taxes as $tax) { + $this->tax_per_item = true; + $this->save(); + $tax['company_id'] = $request->header('company'); + $this->taxes()->create($tax); + } + } + + return Item::with('taxes')->find($this->id); + } +} diff --git a/crater/app/Models/Module.php b/crater/app/Models/Module.php new file mode 100644 index 0000000..97977fb --- /dev/null +++ b/crater/app/Models/Module.php @@ -0,0 +1,13 @@ +belongsTo(Company::class); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('type')) { + $query->whereType($filters->get('type')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + } + + public function scopeWhereSearch($query, $search) + { + $query->where('name', 'LIKE', '%'.$search.'%'); + } + + public function scopeWhereType($query, $type) + { + return $query->where('type', $type); + } + + public function scopeWhereCompany($query) + { + $query->where('notes.company_id', request()->header('company')); + } +} diff --git a/crater/app/Models/Payment.php b/crater/app/Models/Payment.php new file mode 100644 index 0000000..83a1744 --- /dev/null +++ b/crater/app/Models/Payment.php @@ -0,0 +1,474 @@ + 'string', + 'exchange_rate' => 'float' + ]; + + protected static function booted() + { + static::created(function ($payment) { + GeneratePaymentPdfJob::dispatch($payment); + }); + + static::updated(function ($payment) { + GeneratePaymentPdfJob::dispatch($payment, true); + }); + } + + public function setSettingsAttribute($value) + { + if ($value) { + $this->attributes['settings'] = json_encode($value); + } + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function getFormattedPaymentDateAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->payment_date)->format($dateFormat); + } + + public function getPaymentPdfUrlAttribute() + { + return url('/payments/pdf/'.$this->unique_hash); + } + + public function transaction() + { + return $this->belongsTo(Transaction::class); + } + + public function emailLogs() + { + return $this->morphMany('App\Models\EmailLog', 'mailable'); + } + + public function customer() + { + return $this->belongsTo(Customer::class, 'customer_id'); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function invoice() + { + return $this->belongsTo(Invoice::class); + } + + public function creator() + { + return $this->belongsTo('Crater\Models\User', 'creator_id'); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function paymentMethod() + { + return $this->belongsTo(PaymentMethod::class); + } + + public function sendPaymentData($data) + { + $data['payment'] = $this->toArray(); + $data['user'] = $this->customer->toArray(); + $data['company'] = Company::find($this->company_id); + $data['body'] = $this->getEmailBody($data['body']); + $data['attach']['data'] = ($this->getEmailAttachmentSetting()) ? $this->getPDFData() : null; + + return $data; + } + + public function send($data) + { + $data = $this->sendPaymentData($data); + + \Mail::to($data['to'])->send(new SendPaymentMail($data)); + + return [ + 'success' => true, + ]; + } + + public static function createPayment($request) + { + $data = $request->getPaymentPayload(); + + if ($request->invoice_id) { + $invoice = Invoice::find($request->invoice_id); + $invoice->subtractInvoicePayment($request->amount); + } + + $payment = Payment::create($data); + $payment->unique_hash = Hashids::connection(Payment::class)->encode($payment->id); + + $serial = (new SerialNumberFormatter()) + ->setModel($payment) + ->setCompany($payment->company_id) + ->setCustomer($payment->customer_id) + ->setNextNumbers(); + + $payment->sequence_number = $serial->nextSequenceNumber; + $payment->customer_sequence_number = $serial->nextCustomerSequenceNumber; + $payment->save(); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$payment['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($payment); + } + + $customFields = $request->customFields; + + if ($customFields) { + $payment->addCustomFields($customFields); + } + + $payment = Payment::with([ + 'customer', + 'invoice', + 'paymentMethod', + 'fields' + ])->find($payment->id); + + return $payment; + } + + public function updatePayment($request) + { + $data = $request->getPaymentPayload(); + + if ($request->invoice_id && (! $this->invoice_id || $this->invoice_id !== $request->invoice_id)) { + $invoice = Invoice::find($request->invoice_id); + $invoice->subtractInvoicePayment($request->amount); + } + + if ($this->invoice_id && (! $request->invoice_id || $this->invoice_id !== $request->invoice_id)) { + $invoice = Invoice::find($this->invoice_id); + $invoice->addInvoicePayment($this->amount); + } + + if ($this->invoice_id && $this->invoice_id === $request->invoice_id && $request->amount !== $this->amount) { + $invoice = Invoice::find($this->invoice_id); + $invoice->addInvoicePayment($this->amount); + $invoice->subtractInvoicePayment($request->amount); + } + + $serial = (new SerialNumberFormatter()) + ->setModel($this) + ->setCompany($this->company_id) + ->setCustomer($request->customer_id) + ->setModelObject($this->id) + ->setNextNumbers(); + + $data['customer_sequence_number'] = $serial->nextCustomerSequenceNumber; + $this->update($data); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($this); + } + + $customFields = $request->customFields; + + if ($customFields) { + $this->updateCustomFields($customFields); + } + + $payment = Payment::with([ + 'customer', + 'invoice', + 'paymentMethod', + ]) + ->find($this->id); + + return $payment; + } + + public static function deletePayments($ids) + { + foreach ($ids as $id) { + $payment = Payment::find($id); + + if ($payment->invoice_id != null) { + $invoice = Invoice::find($payment->invoice_id); + $invoice->due_amount = ((int)$invoice->due_amount + (int)$payment->amount); + + if ($invoice->due_amount == $invoice->total) { + $invoice->paid_status = Invoice::STATUS_UNPAID; + } else { + $invoice->paid_status = Invoice::STATUS_PARTIALLY_PAID; + } + + $invoice->status = $invoice->getPreviousStatus(); + $invoice->save(); + } + + $payment->delete(); + } + + return true; + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('customer', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('contact_name', 'LIKE', '%'.$term.'%') + ->orWhere('company_name', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopePaymentNumber($query, $paymentNumber) + { + return $query->where('payments.payment_number', 'LIKE', '%'.$paymentNumber.'%'); + } + + public function scopePaymentMethod($query, $paymentMethodId) + { + return $query->where('payments.payment_method_id', $paymentMethodId); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('payment_number')) { + $query->paymentNumber($filters->get('payment_number')); + } + + if ($filters->get('payment_id')) { + $query->wherePayment($filters->get('payment_id')); + } + + if ($filters->get('payment_method_id')) { + $query->paymentMethod($filters->get('payment_method_id')); + } + + if ($filters->get('customer_id')) { + $query->whereCustomer($filters->get('customer_id')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->paymentsBetween($start, $end); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopePaymentsBetween($query, $start, $end) + { + return $query->whereBetween( + 'payments.payment_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWherePayment($query, $payment_id) + { + $query->orWhere('id', $payment_id); + } + + public function scopeWhereCompany($query) + { + $query->where('payments.company_id', request()->header('company')); + } + + public function scopeWhereCustomer($query, $customer_id) + { + $query->where('payments.customer_id', $customer_id); + } + + public function getPDFData() + { + $company = Company::find($this->company_id); + $locale = CompanySetting::getSetting('language', $company->id); + + \App::setLocale($locale); + + $logo = $company->logo_path; + + view()->share([ + 'payment' => $this, + 'company_address' => $this->getCompanyAddress(), + 'billing_address' => $this->getCustomerBillingAddress(), + 'notes' => $this->getNotes(), + 'logo' => $logo ?? null, + ]); + + if (request()->has('preview')) { + return view('app.pdf.payment.payment'); + } + + return PDF::loadView('app.pdf.payment.payment'); + } + + public function getCompanyAddress() + { + if ($this->company && (! $this->company->address()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('payment_company_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getCustomerBillingAddress() + { + if ($this->customer && (! $this->customer->billingAddress()->exists())) { + return false; + } + + $format = CompanySetting::getSetting('payment_from_customer_address_format', $this->company_id); + + return $this->getFormattedString($format); + } + + public function getEmailAttachmentSetting() + { + $paymentAsAttachment = CompanySetting::getSetting('payment_email_attachment', $this->company_id); + + if ($paymentAsAttachment == 'NO') { + return false; + } + + return true; + } + + public function getNotes() + { + return $this->getFormattedString($this->notes); + } + + public function getEmailBody($body) + { + $values = array_merge($this->getFieldsArray(), $this->getExtraFields()); + + $body = strtr($body, $values); + + return preg_replace('/{(.*?)}/', '', $body); + } + + public function getExtraFields() + { + return [ + '{PAYMENT_DATE}' => $this->formattedPaymentDate, + '{PAYMENT_MODE}' => $this->paymentMethod ? $this->paymentMethod->name : null, + '{PAYMENT_NUMBER}' => $this->payment_number, + '{PAYMENT_AMOUNT}' => $this->reference_number, + ]; + } + + public static function generatePayment($transaction) + { + $invoice = Invoice::find($transaction->invoice_id); + + $serial = (new SerialNumberFormatter()) + ->setModel(new Payment()) + ->setCompany($invoice->company_id) + ->setCustomer($invoice->customer_id) + ->setNextNumbers(); + + $data['payment_number'] = $serial->getNextNumber(); + $data['payment_date'] = Carbon::now()->format('y-m-d'); + $data['amount'] = $invoice->total; + $data['invoice_id'] = $invoice->id; + $data['payment_method_id'] = request()->payment_method_id; + $data['customer_id'] = $invoice->customer_id; + $data['exchange_rate'] = $invoice->exchange_rate; + $data['base_amount'] = $data['amount'] * $data['exchange_rate']; + $data['currency_id'] = $invoice->currency_id; + $data['company_id'] = $invoice->company_id; + $data['transaction_id'] = $transaction->id; + + $payment = Payment::create($data); + $payment->unique_hash = Hashids::connection(Payment::class)->encode($payment->id); + $payment->sequence_number = $serial->nextSequenceNumber; + $payment->customer_sequence_number = $serial->nextCustomerSequenceNumber; + $payment->save(); + + $invoice->subtractInvoicePayment($invoice->total); + + return $payment; + } +} diff --git a/crater/app/Models/PaymentMethod.php b/crater/app/Models/PaymentMethod.php new file mode 100644 index 0000000..23723c8 --- /dev/null +++ b/crater/app/Models/PaymentMethod.php @@ -0,0 +1,106 @@ + 'array', + 'use_test_env' => 'boolean' + ]; + + public function setSettingsAttribute($value) + { + $this->attributes['settings'] = json_encode($value); + } + + public function payments() + { + return $this->hasMany(Payment::class); + } + + public function expenses() + { + return $this->hasMany(Expense::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function scopeWhereCompanyId($query, $id) + { + $query->where('company_id', $id); + } + + public function scopeWhereCompany($query) + { + $query->where('company_id', request()->header('company')); + } + + public function scopeWherePaymentMethod($query, $payment_id) + { + $query->orWhere('id', $payment_id); + } + + public function scopeWhereSearch($query, $search) + { + $query->where('name', 'LIKE', '%'.$search.'%'); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('method_id')) { + $query->wherePaymentMethod($filters->get('method_id')); + } + + if ($filters->get('company_id')) { + $query->whereCompany($filters->get('company_id')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public static function createPaymentMethod($request) + { + $data = $request->getPaymentMethodPayload(); + + $paymentMethod = self::create($data); + + return $paymentMethod; + } + + public static function getSettings($id) + { + $settings = PaymentMethod::find($id) + ->settings; + + return $settings; + } +} diff --git a/crater/app/Models/RecurringInvoice.php b/crater/app/Models/RecurringInvoice.php new file mode 100644 index 0000000..aa3ecab --- /dev/null +++ b/crater/app/Models/RecurringInvoice.php @@ -0,0 +1,430 @@ + 'float', + 'send_automatically' => 'boolean' + ]; + + public function getFormattedStartsAtAttribute() + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->starts_at)->format($dateFormat); + } + + public function getFormattedNextInvoiceAtAttribute() + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->next_invoice_at)->format($dateFormat); + } + + public function getFormattedLimitDateAttribute() + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->limit_date)->format($dateFormat); + } + + public function getFormattedCreatedAtAttribute() + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function invoices() + { + return $this->hasMany(Invoice::class); + } + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function items() + { + return $this->hasMany(InvoiceItem::class); + } + + public function customer() + { + return $this->belongsTo(Customer::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function creator() + { + return $this->belongsTo(User::class, 'creator_id'); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function scopeWhereCompany($query) + { + $query->where('recurring_invoices.company_id', request()->header('company')); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereStatus($query, $status) + { + return $query->where('recurring_invoices.status', $status); + } + + public function scopeWhereCustomer($query, $customer_id) + { + $query->where('customer_id', $customer_id); + } + + public function scopeRecurringInvoicesStartBetween($query, $start, $end) + { + return $query->whereBetween( + 'starts_at', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->whereHas('customer', function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('contact_name', 'LIKE', '%'.$term.'%') + ->orWhere('company_name', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('status') && $filters->get('status') !== 'ALL') { + $query->whereStatus($filters->get('status')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->recurringInvoicesStartBetween($start, $end); + } + + if ($filters->get('customer_id')) { + $query->whereCustomer($filters->get('customer_id')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'created_at'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } + + public static function createFromRequest(RecurringInvoiceRequest $request) + { + $recurringInvoice = self::create($request->getRecurringInvoicePayload()); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$recurringInvoice['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($recurringInvoice); + } + + self::createItems($recurringInvoice, $request->items); + + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($recurringInvoice, $request->taxes); + } + + if ($request->customFields) { + $recurringInvoice->addCustomFields($request->customFields); + } + + return $recurringInvoice; + } + + public function updateFromRequest(RecurringInvoiceRequest $request) + { + $data = $request->getRecurringInvoicePayload(); + + $this->update($data); + + $company_currency = CompanySetting::getSetting('currency', $request->header('company')); + + if ((string)$data['currency_id'] !== $company_currency) { + ExchangeRateLog::addExchangeRateLog($this); + } + + $this->items()->delete(); + self::createItems($this, $request->items); + + $this->taxes()->delete(); + if ($request->has('taxes') && (! empty($request->taxes))) { + self::createTaxes($this, $request->taxes); + } + + if ($request->customFields) { + $this->updateCustomFields($request->customFields); + } + + return $this; + } + + public static function createItems($recurringInvoice, $invoiceItems) + { + foreach ($invoiceItems as $invoiceItem) { + $invoiceItem['company_id'] = $recurringInvoice->company_id; + $item = $recurringInvoice->items()->create($invoiceItem); + if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { + foreach ($invoiceItem['taxes'] as $tax) { + $tax['company_id'] = $recurringInvoice->company_id; + if (gettype($tax['amount']) !== "NULL") { + $item->taxes()->create($tax); + } + } + } + } + } + + public static function createTaxes($recurringInvoice, $taxes) + { + foreach ($taxes as $tax) { + $tax['company_id'] = $recurringInvoice->company_id; + + if (gettype($tax['amount']) !== "NULL") { + $recurringInvoice->taxes()->create($tax); + } + } + } + + public function generateInvoice() + { + if (Carbon::now()->lessThan($this->starts_at)) { + return; + } + + if ($this->limit_by == 'DATE') { + $startDate = Carbon::today()->format('Y-m-d'); + + $endDate = $this->limit_date; + + if ($endDate >= $startDate) { + $this->createInvoice(); + + $this->updateNextInvoiceDate(); + } else { + $this->markStatusAsCompleted(); + } + } elseif ($this->limit_by == 'COUNT') { + $invoiceCount = Invoice::where('recurring_invoice_id', $this->id)->count(); + + if ($invoiceCount < $this->limit_count) { + $this->createInvoice(); + + $this->updateNextInvoiceDate(); + } else { + $this->markStatusAsCompleted(); + } + } else { + $this->createInvoice(); + + $this->updateNextInvoiceDate(); + } + } + + public function createInvoice() + { + //get invoice_number + $serial = (new SerialNumberFormatter()) + ->setModel(new Invoice()) + ->setCompany($this->company_id) + ->setCustomer($this->customer_id) + ->setNextNumbers(); + + $days = CompanySetting::getSetting('invoice_due_date_days', $this->company_id); + + if (! $days || $days == "null") { + $days = 7; + } + + $newInvoice['creator_id'] = $this->creator_id; + $newInvoice['invoice_date'] = Carbon::today()->format('Y-m-d'); + $newInvoice['due_date'] = Carbon::today()->addDays($days)->format('Y-m-d'); + $newInvoice['status'] = Invoice::STATUS_DRAFT; + $newInvoice['company_id'] = $this->company_id; + $newInvoice['paid_status'] = Invoice::STATUS_UNPAID; + $newInvoice['sub_total'] = $this->sub_total; + $newInvoice['tax_per_item'] = $this->tax_per_item; + $newInvoice['discount_per_item'] = $this->discount_per_item; + $newInvoice['tax'] = $this->tax; + $newInvoice['total'] = $this->total; + $newInvoice['customer_id'] = $this->customer_id; + $newInvoice['currency_id'] = Customer::find($this->customer_id)->currency_id; + $newInvoice['template_name'] = $this->template_name; + $newInvoice['due_amount'] = $this->total; + $newInvoice['recurring_invoice_id'] = $this->id; + $newInvoice['discount_val'] = $this->discount_val; + $newInvoice['discount'] = $this->discount; + $newInvoice['discount_type'] = $this->discount_type; + $newInvoice['notes'] = $this->notes; + $newInvoice['exchange_rate'] = $this->exchange_rate; + $newInvoice['sales_tax_type'] = $this->sales_tax_type; + $newInvoice['sales_tax_address_type'] = $this->sales_tax_address_type; + $newInvoice['invoice_number'] = $serial->getNextNumber(); + $newInvoice['sequence_number'] = $serial->nextSequenceNumber; + $newInvoice['customer_sequence_number'] = $serial->nextCustomerSequenceNumber; + $newInvoice['base_due_amount'] = $this->exchange_rate * $this->due_amount; + $newInvoice['base_discount_val'] = $this->exchange_rate * $this->discount_val; + $newInvoice['base_sub_total'] = $this->exchange_rate * $this->sub_total; + $newInvoice['base_tax'] = $this->exchange_rate * $this->tax; + $newInvoice['base_total'] = $this->exchange_rate * $this->total; + $invoice = Invoice::create($newInvoice); + $invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id); + $invoice->save(); + + $this->load('items.taxes'); + Invoice::createItems($invoice, $this->items->toArray()); + + if ($this->taxes()->exists()) { + Invoice::createTaxes($invoice, $this->taxes->toArray()); + } + + if ($this->fields()->exists()) { + $customField = []; + + foreach ($this->fields as $data) { + $customField[] = [ + 'id' => $data->custom_field_id, + 'value' => $data->defaultAnswer + ]; + } + + $invoice->addCustomFields($customField); + } + + //send automatically + if ($this->send_automatically == true) { + $data = [ + 'body' => CompanySetting::getSetting('invoice_mail_body', $this->company_id), + 'from' => config('mail.from.address'), + 'to' => $this->customer->email, + 'subject' => 'New Invoice', + 'invoice' => $invoice->toArray(), + 'customer' => $invoice->customer->toArray(), + 'company' => Company::find($invoice->company_id) + ]; + + $invoice->send($data); + } + } + + public function markStatusAsCompleted() + { + if ($this->status == $this->status) { + $this->status = self::COMPLETED; + $this->save(); + } + } + + public static function getNextInvoiceDate($frequency, $starts_at) + { + $cron = new Cron\CronExpression($frequency); + + return $cron->getNextRunDate($starts_at)->format('Y-m-d H:i:s'); + } + + public function updateNextInvoiceDate() + { + $nextInvoiceAt = self::getNextInvoiceDate($this->frequency, $this->starts_at); + + $this->next_invoice_at = $nextInvoiceAt; + $this->save(); + } + + public static function deleteRecurringInvoice($ids) + { + foreach ($ids as $id) { + $recurringInvoice = self::find($id); + + if ($recurringInvoice->invoices()->exists()) { + $recurringInvoice->invoices()->update(['recurring_invoice_id' => null]); + } + + if ($recurringInvoice->items()->exists()) { + $recurringInvoice->items()->delete(); + } + + if ($recurringInvoice->taxes()->exists()) { + $recurringInvoice->taxes()->delete(); + } + + $recurringInvoice->delete(); + } + + return true; + } +} diff --git a/crater/app/Models/Setting.php b/crater/app/Models/Setting.php new file mode 100644 index 0000000..d386923 --- /dev/null +++ b/crater/app/Models/Setting.php @@ -0,0 +1,64 @@ +first(); + + if ($old) { + $old->value = $setting; + $old->save(); + + return; + } + + $set = new Setting(); + $set->option = $key; + $set->value = $setting; + $set->save(); + } + + public static function setSettings($settings) + { + foreach ($settings as $key => $value) { + self::updateOrCreate( + [ + 'option' => $key, + ], + [ + 'option' => $key, + 'value' => $value, + ] + ); + } + } + + public static function getSetting($key) + { + $setting = static::whereOption($key)->first(); + + if ($setting) { + return $setting->value; + } else { + return null; + } + } + + public static function getSettings($settings) + { + return static::whereIn('option', $settings) + ->get()->mapWithKeys(function ($item) { + return [$item['option'] => $item['value']]; + }); + } +} diff --git a/crater/app/Models/Tax.php b/crater/app/Models/Tax.php new file mode 100644 index 0000000..b8491e2 --- /dev/null +++ b/crater/app/Models/Tax.php @@ -0,0 +1,104 @@ + 'integer', + 'percent' => 'float', + ]; + + public function taxType() + { + return $this->belongsTo(TaxType::class); + } + + public function invoice() + { + return $this->belongsTo(Invoice::class); + } + + public function recurringInvoice() + { + return $this->belongsTo(RecurringInvoice::class); + } + + public function estimate() + { + return $this->belongsTo(Estimate::class); + } + + public function currency() + { + return $this->belongsTo(Currency::class); + } + + public function invoiceItem() + { + return $this->belongsTo(InvoiceItem::class); + } + + public function estimateItem() + { + return $this->belongsTo(EstimateItem::class); + } + + public function item() + { + return $this->belongsTo(Item::class); + } + + public function scopeWhereCompany($query, $company_id) + { + $query->where('company_id', $company_id); + } + + public function scopeTaxAttributes($query) + { + $query->select( + DB::raw('sum(base_amount) as total_tax_amount, tax_type_id') + )->groupBy('tax_type_id'); + } + + public function scopeInvoicesBetween($query, $start, $end) + { + $query->whereHas('invoice', function ($query) use ($start, $end) { + $query->where('paid_status', Invoice::STATUS_PAID) + ->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }) + ->orWhereHas('invoiceItem.invoice', function ($query) use ($start, $end) { + $query->where('paid_status', Invoice::STATUS_PAID) + ->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }); + } + + public function scopeWhereInvoicesFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + + $query->invoicesBetween($start, $end); + } + } +} diff --git a/crater/app/Models/TaxType.php b/crater/app/Models/TaxType.php new file mode 100644 index 0000000..4417701 --- /dev/null +++ b/crater/app/Models/TaxType.php @@ -0,0 +1,85 @@ + 'float', + 'compound_tax' => 'boolean' + ]; + + public const TYPE_GENERAL = 'GENERAL'; + public const TYPE_MODULE = 'MODULE'; + + public function taxes() + { + return $this->hasMany(Tax::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function scopeWhereCompany($query) + { + $query->where('company_id', request()->header('company')); + } + + public function scopeWhereTaxType($query, $tax_type_id) + { + $query->orWhere('id', $tax_type_id); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('tax_type_id')) { + $query->whereTaxType($filters->get('tax_type_id')); + } + + if ($filters->get('company_id')) { + $query->whereCompany($filters->get('company_id')); + } + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'payment_number'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereSearch($query, $search) + { + $query->where('name', 'LIKE', '%'.$search.'%'); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } +} diff --git a/crater/app/Models/Transaction.php b/crater/app/Models/Transaction.php new file mode 100644 index 0000000..97a3faf --- /dev/null +++ b/crater/app/Models/Transaction.php @@ -0,0 +1,75 @@ +hasMany(Payment::class); + } + + public function invoice() + { + return $this->belongsTo(Invoice::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function completeTransaction() + { + $this->status = self::SUCCESS; + $this->save(); + } + + public function failedTransaction() + { + $this->status = self::FAILED; + $this->save(); + } + + public static function createTransaction($data) + { + $transaction = self::create($data); + $transaction->unique_hash = Hashids::connection(Transaction::class)->encode($transaction->id); + $transaction->save(); + + return $transaction; + } + + public function isExpired() + { + $linkexpiryDays = CompanySetting::getSetting('link_expiry_days', $this->company_id); + $checkExpiryLinks = CompanySetting::getSetting('automatically_expire_public_links', $this->company_id); + + $expiryDate = $this->updated_at->addDays($linkexpiryDays); + + if ($checkExpiryLinks == 'YES' && $this->status == self::SUCCESS && Carbon::now()->format('Y-m-d') > $expiryDate->format('Y-m-d')) { + return true; + } + + return false; + } +} diff --git a/crater/app/Models/Unit.php b/crater/app/Models/Unit.php new file mode 100644 index 0000000..7d682ed --- /dev/null +++ b/crater/app/Models/Unit.php @@ -0,0 +1,66 @@ +hasMany(Item::class); + } + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function scopeWhereCompany($query) + { + $query->where('company_id', request()->header('company')); + } + + public function scopeWhereUnit($query, $unit_id) + { + $query->orWhere('id', $unit_id); + } + + public function scopeWhereSearch($query, $search) + { + return $query->where('name', 'LIKE', '%'.$search.'%'); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('unit_id')) { + $query->whereUnit($filters->get('unit_id')); + } + + if ($filters->get('company_id')) { + $query->whereCompany($filters->get('company_id')); + } + + return $query; + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } +} diff --git a/crater/app/Models/User.php b/crater/app/Models/User.php new file mode 100644 index 0000000..69093ba --- /dev/null +++ b/crater/app/Models/User.php @@ -0,0 +1,432 @@ +where('email', $username)->first(); + } + + public function setPasswordAttribute($value) + { + if ($value != null) { + $this->attributes['password'] = bcrypt($value); + } + } + + public function isSuperAdminOrAdmin() + { + return ($this->role == 'super admin') || ($this->role == 'admin'); + } + + public static function login($request) + { + $remember = $request->remember; + $email = $request->email; + $password = $request->password; + + return (\Auth::attempt(['email' => $email, 'password' => $password], $remember)); + } + + public function getFormattedCreatedAtAttribute($value) + { + $dateFormat = CompanySetting::getSetting('carbon_date_format', request()->header('company')); + + return Carbon::parse($this->created_at)->format($dateFormat); + } + + public function estimates() + { + return $this->hasMany(Estimate::class, 'creator_id'); + } + + public function customers() + { + return $this->hasMany(Customer::class, 'creator_id'); + } + + public function recurringInvoices() + { + return $this->hasMany(RecurringInvoice::class, 'creator_id'); + } + + public function currency() + { + return $this->belongsTo(Currency::class, 'currency_id'); + } + + public function creator() + { + return $this->belongsTo('Crater\Models\User', 'creator_id'); + } + + public function companies() + { + return $this->belongsToMany(Company::class, 'user_company', 'user_id', 'company_id'); + } + + public function expenses() + { + return $this->hasMany(Expense::class, 'creator_id'); + } + + public function payments() + { + return $this->hasMany(Payment::class, 'creator_id'); + } + + public function invoices() + { + return $this->hasMany(Invoice::class, 'creator_id'); + } + + public function items() + { + return $this->hasMany(Item::class, 'creator_id'); + } + + public function settings() + { + return $this->hasMany(UserSetting::class, 'user_id'); + } + + public function addresses() + { + return $this->hasMany(Address::class); + } + + public function billingAddress() + { + return $this->hasOne(Address::class)->where('type', Address::BILLING_TYPE); + } + + public function shippingAddress() + { + return $this->hasOne(Address::class)->where('type', Address::SHIPPING_TYPE); + } + + /** + * Override the mail body for reset password notification mail. + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new MailResetPasswordNotification($token)); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeWhereSearch($query, $search) + { + foreach (explode(' ', $search) as $term) { + $query->where(function ($query) use ($term) { + $query->where('name', 'LIKE', '%'.$term.'%') + ->orWhere('email', 'LIKE', '%'.$term.'%') + ->orWhere('phone', 'LIKE', '%'.$term.'%'); + }); + } + } + + public function scopeWhereContactName($query, $contactName) + { + return $query->where('contact_name', 'LIKE', '%'.$contactName.'%'); + } + + public function scopeWhereDisplayName($query, $displayName) + { + return $query->where('name', 'LIKE', '%'.$displayName.'%'); + } + + public function scopeWherePhone($query, $phone) + { + return $query->where('phone', 'LIKE', '%'.$phone.'%'); + } + + public function scopeWhereEmail($query, $email) + { + return $query->where('email', 'LIKE', '%'.$email.'%'); + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('search')) { + $query->whereSearch($filters->get('search')); + } + + if ($filters->get('display_name')) { + $query->whereDisplayName($filters->get('display_name')); + } + + if ($filters->get('email')) { + $query->whereEmail($filters->get('email')); + } + + if ($filters->get('phone')) { + $query->wherePhone($filters->get('phone')); + } + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopeWhereSuperAdmin($query) + { + $query->orWhere('role', 'super admin'); + } + + public function scopeApplyInvoiceFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('from_date') && $filters->get('to_date')) { + $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); + $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->invoicesBetween($start, $end); + } + } + + public function scopeInvoicesBetween($query, $start, $end) + { + $query->whereHas('invoices', function ($query) use ($start, $end) { + $query->whereBetween( + 'invoice_date', + [$start->format('Y-m-d'), $end->format('Y-m-d')] + ); + }); + } + + public function getAvatarAttribute() + { + $avatar = $this->getMedia('admin_avatar')->first(); + + if ($avatar) { + return asset($avatar->getUrl()); + } + + return 0; + } + + public function setSettings($settings) + { + foreach ($settings as $key => $value) { + $this->settings()->updateOrCreate( + [ + 'key' => $key, + ], + [ + 'key' => $key, + 'value' => $value, + ] + ); + } + } + + public function hasCompany($company_id) + { + $companies = $this->companies()->pluck('company_id')->toArray(); + + return in_array($company_id, $companies); + } + + public function getAllSettings() + { + return $this->settings()->get()->mapWithKeys(function ($item) { + return [$item['key'] => $item['value']]; + }); + } + + public function getSettings($settings) + { + return $this->settings()->whereIn('key', $settings)->get()->mapWithKeys(function ($item) { + return [$item['key'] => $item['value']]; + }); + } + + public function isOwner() + { + if (Schema::hasColumn('companies', 'owner_id')) { + $company = Company::find(request()->header('company')); + + if ($company && $this->id == $company->owner_id) { + return true; + } + } else { + return $this->role == 'super admin' || $this->role == 'admin'; + } + + return false; + } + + public static function createFromRequest(UserRequest $request) + { + $user = self::create($request->getUserPayload()); + + $user->setSettings([ + 'language' => CompanySetting::getSetting('language', $request->header('company')), + ]); + + $companies = collect($request->companies); + $user->companies()->sync($companies->pluck('id')); + + foreach ($companies as $company) { + BouncerFacade::scope()->to($company['id']); + + BouncerFacade::sync($user)->roles([$company['role']]); + } + + return $user; + } + + public function updateFromRequest(UserRequest $request) + { + $this->update($request->getUserPayload()); + + $companies = collect($request->companies); + $this->companies()->sync($companies->pluck('id')); + + foreach ($companies as $company) { + BouncerFacade::scope()->to($company['id']); + + BouncerFacade::sync($this)->roles([$company['role']]); + } + + return $this; + } + + public function checkAccess($data) + { + if ($this->isOwner()) { + return true; + } + + if ((! $data->data['owner_only']) && empty($data->data['ability'])) { + return true; + } + + if ((! $data->data['owner_only']) && (! empty($data->data['ability'])) && (! empty($data->data['model'])) && $this->can($data->data['ability'], $data->data['model'])) { + return true; + } + + if ((! $data->data['owner_only']) && $this->can($data->data['ability'])) { + return true; + } + + return false; + } + + public static function deleteUsers($ids) + { + foreach ($ids as $id) { + $user = self::find($id); + + if ($user->invoices()->exists()) { + $user->invoices()->update(['creator_id' => null]); + } + + if ($user->estimates()->exists()) { + $user->estimates()->update(['creator_id' => null]); + } + + if ($user->customers()->exists()) { + $user->customers()->update(['creator_id' => null]); + } + + if ($user->recurringInvoices()->exists()) { + $user->recurringInvoices()->update(['creator_id' => null]); + } + + if ($user->expenses()->exists()) { + $user->expenses()->update(['creator_id' => null]); + } + + if ($user->payments()->exists()) { + $user->payments()->update(['creator_id' => null]); + } + + if ($user->items()->exists()) { + $user->items()->update(['creator_id' => null]); + } + + if ($user->settings()->exists()) { + $user->settings()->delete(); + } + + $user->delete(); + } + + return true; + } +} diff --git a/crater/app/Models/UserSetting.php b/crater/app/Models/UserSetting.php new file mode 100644 index 0000000..81b9661 --- /dev/null +++ b/crater/app/Models/UserSetting.php @@ -0,0 +1,18 @@ +belongsTo(User::class); + } +} diff --git a/crater/app/Notifications/CustomerMailResetPasswordNotification.php b/crater/app/Notifications/CustomerMailResetPasswordNotification.php new file mode 100644 index 0000000..054f6d9 --- /dev/null +++ b/crater/app/Notifications/CustomerMailResetPasswordNotification.php @@ -0,0 +1,66 @@ +company->slug}/customer/reset/password/".$this->token); + + return ( new MailMessage() ) + ->subject('Reset Password Notification') + ->line("Hello! You are receiving this email because we received a password reset request for your account.") + ->action('Reset Password', $link) + ->line("This password reset link will expire in ".config('auth.passwords.users.expire')." minutes") + ->line("If you did not request a password reset, no further action is required."); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/crater/app/Notifications/MailResetPasswordNotification.php b/crater/app/Notifications/MailResetPasswordNotification.php new file mode 100644 index 0000000..b9f1458 --- /dev/null +++ b/crater/app/Notifications/MailResetPasswordNotification.php @@ -0,0 +1,66 @@ +token); + + return ( new MailMessage() ) + ->subject('Reset Password Notification') + ->line("Hello! You are receiving this email because we received a password reset request for your account.") + ->action('Reset Password', $link) + ->line("This password reset link will expire in ".config('auth.passwords.users.expire')." minutes") + ->line("If you did not request a password reset, no further action is required."); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/crater/app/Policies/CompanyPolicy.php b/crater/app/Policies/CompanyPolicy.php new file mode 100644 index 0000000..19f7d34 --- /dev/null +++ b/crater/app/Policies/CompanyPolicy.php @@ -0,0 +1,39 @@ +isOwner()) { + return true; + } + + return false; + } + + public function delete(User $user, Company $company) + { + if ($user->id == $company->owner_id) { + return true; + } + + return false; + } + + public function transferOwnership(User $user, Company $company) + { + if ($user->id == $company->owner_id) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/CustomFieldPolicy.php b/crater/app/Policies/CustomFieldPolicy.php new file mode 100644 index 0000000..ac14448 --- /dev/null +++ b/crater/app/Policies/CustomFieldPolicy.php @@ -0,0 +1,123 @@ +hasCompany($customField->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-custom-field', CustomField::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\CustomField $customField + * @return mixed + */ + public function update(User $user, CustomField $customField) + { + if (BouncerFacade::can('edit-custom-field', $customField) && $user->hasCompany($customField->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\CustomField $customField + * @return mixed + */ + public function delete(User $user, CustomField $customField) + { + if (BouncerFacade::can('delete-custom-field', $customField) && $user->hasCompany($customField->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\CustomField $customField + * @return mixed + */ + public function restore(User $user, CustomField $customField) + { + if (BouncerFacade::can('delete-custom-field', $customField) && $user->hasCompany($customField->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\CustomField $customField + * @return mixed + */ + public function forceDelete(User $user, CustomField $customField) + { + if (BouncerFacade::can('delete-custom-field', $customField) && $user->hasCompany($customField->company_id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/CustomerPolicy.php b/crater/app/Policies/CustomerPolicy.php new file mode 100644 index 0000000..69fb19c --- /dev/null +++ b/crater/app/Policies/CustomerPolicy.php @@ -0,0 +1,138 @@ +hasCompany($company->id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/EstimatePolicy.php b/crater/app/Policies/EstimatePolicy.php new file mode 100644 index 0000000..1180049 --- /dev/null +++ b/crater/app/Policies/EstimatePolicy.php @@ -0,0 +1,154 @@ +hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-estimate', Estimate::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Estimate $estimate + * @return mixed + */ + public function update(User $user, Estimate $estimate) + { + if (BouncerFacade::can('edit-estimate', $estimate) && $user->hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Estimate $estimate + * @return mixed + */ + public function delete(User $user, Estimate $estimate) + { + if (BouncerFacade::can('delete-estimate', $estimate) && $user->hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Estimate $estimate + * @return mixed + */ + public function restore(User $user, Estimate $estimate) + { + if (BouncerFacade::can('delete-estimate', $estimate) && $user->hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Estimate $estimate + * @return mixed + */ + public function forceDelete(User $user, Estimate $estimate) + { + if (BouncerFacade::can('delete-estimate', $estimate) && $user->hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can send email of the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Estimate $payment + * @return mixed + */ + public function send(User $user, Estimate $estimate) + { + if (BouncerFacade::can('send-estimate', $estimate) && $user->hasCompany($estimate->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-estimate', Estimate::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/ExchangeRateProviderPolicy.php b/crater/app/Policies/ExchangeRateProviderPolicy.php new file mode 100644 index 0000000..cfdef4e --- /dev/null +++ b/crater/app/Policies/ExchangeRateProviderPolicy.php @@ -0,0 +1,115 @@ +hasCompany($exchangeRateProvider->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return \Illuminate\Auth\Access\Response|bool + */ + public function create(User $user) + { + if (BouncerFacade::can('create-exchange-rate-provider', ExchangeRateProvider::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Auth\Access\Response|bool + */ + public function update(User $user, ExchangeRateProvider $exchangeRateProvider) + { + if (BouncerFacade::can('edit-exchange-rate-provider', $exchangeRateProvider) && $user->hasCompany($exchangeRateProvider->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Auth\Access\Response|bool + */ + public function delete(User $user, ExchangeRateProvider $exchangeRateProvider) + { + if (BouncerFacade::can('delete-exchange-rate-provider', $exchangeRateProvider) && $user->hasCompany($exchangeRateProvider->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Auth\Access\Response|bool + */ + public function restore(User $user, ExchangeRateProvider $exchangeRateProvider) + { + // + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExchangeRateProvider $exchangeRateProvider + * @return \Illuminate\Auth\Access\Response|bool + */ + public function forceDelete(User $user, ExchangeRateProvider $exchangeRateProvider) + { + // + } +} diff --git a/crater/app/Policies/ExpenseCategoryPolicy.php b/crater/app/Policies/ExpenseCategoryPolicy.php new file mode 100644 index 0000000..abe2e73 --- /dev/null +++ b/crater/app/Policies/ExpenseCategoryPolicy.php @@ -0,0 +1,124 @@ +hasCompany($expenseCategory->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('view-expense', Expense::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExpenseCategory $expenseCategory + * @return mixed + */ + public function update(User $user, ExpenseCategory $expenseCategory) + { + if (BouncerFacade::can('view-expense', Expense::class) && $user->hasCompany($expenseCategory->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExpenseCategory $expenseCategory + * @return mixed + */ + public function delete(User $user, ExpenseCategory $expenseCategory) + { + if (BouncerFacade::can('view-expense', Expense::class) && $user->hasCompany($expenseCategory->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExpenseCategory $expenseCategory + * @return mixed + */ + public function restore(User $user, ExpenseCategory $expenseCategory) + { + if (BouncerFacade::can('view-expense', Expense::class) && $user->hasCompany($expenseCategory->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\ExpenseCategory $expenseCategory + * @return mixed + */ + public function forceDelete(User $user, ExpenseCategory $expenseCategory) + { + if (BouncerFacade::can('view-expense', Expense::class) && $user->hasCompany($expenseCategory->company_id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/ExpensePolicy.php b/crater/app/Policies/ExpensePolicy.php new file mode 100644 index 0000000..451ff0f --- /dev/null +++ b/crater/app/Policies/ExpensePolicy.php @@ -0,0 +1,138 @@ +hasCompany($expense->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-expense', Expense::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Expense $expense + * @return mixed + */ + public function update(User $user, Expense $expense) + { + if (BouncerFacade::can('edit-expense', $expense) && $user->hasCompany($expense->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Expense $expense + * @return mixed + */ + public function delete(User $user, Expense $expense) + { + if (BouncerFacade::can('delete-expense', $expense) && $user->hasCompany($expense->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Expense $expense + * @return mixed + */ + public function restore(User $user, Expense $expense) + { + if (BouncerFacade::can('delete-expense', $expense) && $user->hasCompany($expense->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Expense $expense + * @return mixed + */ + public function forceDelete(User $user, Expense $expense) + { + if (BouncerFacade::can('delete-expense', $expense) && $user->hasCompany($expense->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-expense', Expense::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/InvoicePolicy.php b/crater/app/Policies/InvoicePolicy.php new file mode 100644 index 0000000..9ed9454 --- /dev/null +++ b/crater/app/Policies/InvoicePolicy.php @@ -0,0 +1,154 @@ +hasCompany($invoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-invoice', Invoice::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Invoice $invoice + * @return mixed + */ + public function update(User $user, Invoice $invoice) + { + if (BouncerFacade::can('edit-invoice', $invoice) && $user->hasCompany($invoice->company_id)) { + return $invoice->allow_edit; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Invoice $invoice + * @return mixed + */ + public function delete(User $user, Invoice $invoice) + { + if (BouncerFacade::can('delete-invoice', $invoice) && $user->hasCompany($invoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Invoice $invoice + * @return mixed + */ + public function restore(User $user, Invoice $invoice) + { + if (BouncerFacade::can('delete-invoice', $invoice) && $user->hasCompany($invoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Invoice $invoice + * @return mixed + */ + public function forceDelete(User $user, Invoice $invoice) + { + if (BouncerFacade::can('delete-invoice', $invoice) && $user->hasCompany($invoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can send email of the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function send(User $user, Invoice $invoice) + { + if (BouncerFacade::can('send-invoice', $invoice) && $user->hasCompany($invoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-invoice', Invoice::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/ItemPolicy.php b/crater/app/Policies/ItemPolicy.php new file mode 100644 index 0000000..9283018 --- /dev/null +++ b/crater/app/Policies/ItemPolicy.php @@ -0,0 +1,138 @@ +hasCompany($item->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-item', Item::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Item $item + * @return mixed + */ + public function update(User $user, Item $item) + { + if (BouncerFacade::can('edit-item', $item) && $user->hasCompany($item->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Item $item + * @return mixed + */ + public function delete(User $user, Item $item) + { + if (BouncerFacade::can('delete-item', $item) && $user->hasCompany($item->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Item $item + * @return mixed + */ + public function restore(User $user, Item $item) + { + if (BouncerFacade::can('delete-item', $item) && $user->hasCompany($item->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Item $item + * @return mixed + */ + public function forceDelete(User $user, Item $item) + { + if (BouncerFacade::can('delete-item', $item) && $user->hasCompany($item->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-item', Item::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/ModulesPolicy.php b/crater/app/Policies/ModulesPolicy.php new file mode 100644 index 0000000..65f8341 --- /dev/null +++ b/crater/app/Policies/ModulesPolicy.php @@ -0,0 +1,20 @@ +isOwner()) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/NotePolicy.php b/crater/app/Policies/NotePolicy.php new file mode 100644 index 0000000..748f18d --- /dev/null +++ b/crater/app/Policies/NotePolicy.php @@ -0,0 +1,31 @@ +isOwner()) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/PaymentMethodPolicy.php b/crater/app/Policies/PaymentMethodPolicy.php new file mode 100644 index 0000000..5cfaebc --- /dev/null +++ b/crater/app/Policies/PaymentMethodPolicy.php @@ -0,0 +1,124 @@ +hasCompany($paymentMethod->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('view-payment', Payment::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return mixed + */ + public function update(User $user, PaymentMethod $paymentMethod) + { + if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return mixed + */ + public function delete(User $user, PaymentMethod $paymentMethod) + { + if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return mixed + */ + public function restore(User $user, PaymentMethod $paymentMethod) + { + if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\PaymentMethod $paymentMethod + * @return mixed + */ + public function forceDelete(User $user, PaymentMethod $paymentMethod) + { + if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/PaymentPolicy.php b/crater/app/Policies/PaymentPolicy.php new file mode 100644 index 0000000..43e71f4 --- /dev/null +++ b/crater/app/Policies/PaymentPolicy.php @@ -0,0 +1,154 @@ +hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-payment', Payment::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function update(User $user, Payment $payment) + { + if (BouncerFacade::can('edit-payment', $payment) && $user->hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function delete(User $user, Payment $payment) + { + if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function restore(User $user, Payment $payment) + { + if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function forceDelete(User $user, Payment $payment) + { + if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can send email of the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Payment $payment + * @return mixed + */ + public function send(User $user, Payment $payment) + { + if (BouncerFacade::can('send-payment', $payment) && $user->hasCompany($payment->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-payment', Payment::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/RecurringInvoicePolicy.php b/crater/app/Policies/RecurringInvoicePolicy.php new file mode 100644 index 0000000..18ad553 --- /dev/null +++ b/crater/app/Policies/RecurringInvoicePolicy.php @@ -0,0 +1,138 @@ +hasCompany($recurringInvoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return \Illuminate\Auth\Access\Response|bool + */ + public function create(User $user) + { + if (BouncerFacade::can('create-recurring-invoice', RecurringInvoice::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Auth\Access\Response|bool + */ + public function update(User $user, RecurringInvoice $recurringInvoice) + { + if (BouncerFacade::can('edit-recurring-invoice', $recurringInvoice) && $user->hasCompany($recurringInvoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Auth\Access\Response|bool + */ + public function delete(User $user, RecurringInvoice $recurringInvoice) + { + if (BouncerFacade::can('delete-recurring-invoice', $recurringInvoice) && $user->hasCompany($recurringInvoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Auth\Access\Response|bool + */ + public function restore(User $user, RecurringInvoice $recurringInvoice) + { + if (BouncerFacade::can('delete-recurring-invoice', $recurringInvoice) && $user->hasCompany($recurringInvoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\RecurringInvoice $recurringInvoice + * @return \Illuminate\Auth\Access\Response|bool + */ + public function forceDelete(User $user, RecurringInvoice $recurringInvoice) + { + if (BouncerFacade::can('delete-recurring-invoice', $recurringInvoice) && $user->hasCompany($recurringInvoice->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if (BouncerFacade::can('delete-recurring-invoice', RecurringInvoice::class)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/ReportPolicy.php b/crater/app/Policies/ReportPolicy.php new file mode 100644 index 0000000..0e6faf4 --- /dev/null +++ b/crater/app/Policies/ReportPolicy.php @@ -0,0 +1,22 @@ +hasCompany($company->id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/RolePolicy.php b/crater/app/Policies/RolePolicy.php new file mode 100644 index 0000000..cd8acc0 --- /dev/null +++ b/crater/app/Policies/RolePolicy.php @@ -0,0 +1,122 @@ +isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can view the model. + * + * @param \Crater\Models\User $user + * @param \Silber\Bouncer\Database\Role $role + * @return mixed + */ + public function view(User $user, Role $role) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Silber\Bouncer\Database\Role $role + * @return mixed + */ + public function update(User $user, Role $role) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Silber\Bouncer\Database\Role $role + * @return mixed + */ + public function delete(User $user, Role $role) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Silber\Bouncer\Database\Role $role + * @return mixed + */ + public function restore(User $user, Role $role) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Silber\Bouncer\Database\Role $role + * @return mixed + */ + public function forceDelete(User $user, Role $role) + { + if ($user->isOwner()) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/SettingsPolicy.php b/crater/app/Policies/SettingsPolicy.php new file mode 100644 index 0000000..e093032 --- /dev/null +++ b/crater/app/Policies/SettingsPolicy.php @@ -0,0 +1,57 @@ +id == $company->owner_id) { + return true; + } + + return false; + } + + public function manageBackups(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + public function manageFileDisk(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + public function manageEmailConfig(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + public function manageSettings(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/TaxTypePolicy.php b/crater/app/Policies/TaxTypePolicy.php new file mode 100644 index 0000000..ab770da --- /dev/null +++ b/crater/app/Policies/TaxTypePolicy.php @@ -0,0 +1,123 @@ +hasCompany($taxType->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('create-tax-type', TaxType::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\TaxType $taxType + * @return mixed + */ + public function update(User $user, TaxType $taxType) + { + if (BouncerFacade::can('edit-tax-type', $taxType) && $user->hasCompany($taxType->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\TaxType $taxType + * @return mixed + */ + public function delete(User $user, TaxType $taxType) + { + if (BouncerFacade::can('delete-tax-type', $taxType) && $user->hasCompany($taxType->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\TaxType $taxType + * @return mixed + */ + public function restore(User $user, TaxType $taxType) + { + if (BouncerFacade::can('delete-tax-type', $taxType) && $user->hasCompany($taxType->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\TaxType $taxType + * @return mixed + */ + public function forceDelete(User $user, TaxType $taxType) + { + if (BouncerFacade::can('delete-tax-type', $taxType) && $user->hasCompany($taxType->company_id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/UnitPolicy.php b/crater/app/Policies/UnitPolicy.php new file mode 100644 index 0000000..3672d06 --- /dev/null +++ b/crater/app/Policies/UnitPolicy.php @@ -0,0 +1,124 @@ +hasCompany($unit->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if (BouncerFacade::can('view-item', Item::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Unit $unit + * @return mixed + */ + public function update(User $user, Unit $unit) + { + if (BouncerFacade::can('view-item', Item::class) && $user->hasCompany($unit->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Unit $unit + * @return mixed + */ + public function delete(User $user, Unit $unit) + { + if (BouncerFacade::can('view-item', Item::class) && $user->hasCompany($unit->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Unit $unit + * @return mixed + */ + public function restore(User $user, Unit $unit) + { + if (BouncerFacade::can('view-item', Item::class) && $user->hasCompany($unit->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\Unit $unit + * @return mixed + */ + public function forceDelete(User $user, Unit $unit) + { + if (BouncerFacade::can('view-item', Item::class) && $user->hasCompany($unit->company_id)) { + return true; + } + + return false; + } +} diff --git a/crater/app/Policies/UserPolicy.php b/crater/app/Policies/UserPolicy.php new file mode 100644 index 0000000..2f48c3f --- /dev/null +++ b/crater/app/Policies/UserPolicy.php @@ -0,0 +1,152 @@ +isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can view the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function view(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function create(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function update(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function delete(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function restore(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function forceDelete(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can invite the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\User $model + * @return mixed + */ + public function invite(User $user, User $model) + { + if ($user->isOwner()) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete models. + * + * @param \Crater\Models\User $user + * @return mixed + */ + public function deleteMultiple(User $user) + { + if ($user->isOwner()) { + return true; + } + + return false; + } +} diff --git a/crater/app/Providers/AppServiceProvider.php b/crater/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..b40d9c6 --- /dev/null +++ b/crater/app/Providers/AppServiceProvider.php @@ -0,0 +1,69 @@ +loadJsonTranslationsFrom(resource_path('scripts/locales')); + + if (\Storage::disk('local')->has('database_created') && Schema::hasTable('abilities')) { + $this->addMenus(); + } + } + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + // + } + + public function addMenus() + { + //main menu + \Menu::make('main_menu', function ($menu) { + foreach (config('crater.main_menu') as $data) { + $this->generateMenu($menu, $data); + } + }); + + //setting menu + \Menu::make('setting_menu', function ($menu) { + foreach (config('crater.setting_menu') as $data) { + $this->generateMenu($menu, $data); + } + }); + + \Menu::make('customer_portal_menu', function ($menu) { + foreach (config('crater.customer_menu') as $data) { + $this->generateMenu($menu, $data); + } + }); + } + + public function generateMenu($menu, $data) + { + $menu->add($data['title'], $data['link']) + ->data('icon', $data['icon']) + ->data('name', $data['name']) + ->data('owner_only', $data['owner_only']) + ->data('ability', $data['ability']) + ->data('model', $data['model']) + ->data('group', $data['group']); + } +} diff --git a/crater/app/Providers/AuthServiceProvider.php b/crater/app/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..61cd430 --- /dev/null +++ b/crater/app/Providers/AuthServiceProvider.php @@ -0,0 +1,90 @@ + \Crater\Policies\CustomerPolicy::class, + \Crater\Models\Invoice::class => \Crater\Policies\InvoicePolicy::class, + \Crater\Models\Estimate::class => \Crater\Policies\EstimatePolicy::class, + \Crater\Models\Payment::class => \Crater\Policies\PaymentPolicy::class, + \Crater\Models\Expense::class => \Crater\Policies\ExpensePolicy::class, + \Crater\Models\ExpenseCategory::class => \Crater\Policies\ExpenseCategoryPolicy::class, + \Crater\Models\PaymentMethod::class => \Crater\Policies\PaymentMethodPolicy::class, + \Crater\Models\TaxType::class => \Crater\Policies\TaxTypePolicy::class, + \Crater\Models\CustomField::class => \Crater\Policies\CustomFieldPolicy::class, + \Crater\Models\User::class => \Crater\Policies\UserPolicy::class, + \Crater\Models\Item::class => \Crater\Policies\ItemPolicy::class, + \Silber\Bouncer\Database\Role::class => \Crater\Policies\RolePolicy::class, + \Crater\Models\Unit::class => \Crater\Policies\UnitPolicy::class, + \Crater\Models\RecurringInvoice::class => \Crater\Policies\RecurringInvoicePolicy::class, + \Crater\Models\ExchangeRateProvider::class => \Crater\Policies\ExchangeRateProviderPolicy::class, + ]; + + /** + * Register any authentication / authorization services. + * + * @return void + */ + public function boot() + { + $this->registerPolicies(); + + Gate::define('create company', [CompanyPolicy::class, 'create']); + Gate::define('transfer company ownership', [CompanyPolicy::class, 'transferOwnership']); + Gate::define('delete company', [CompanyPolicy::class, 'delete']); + + Gate::define('manage modules', [ModulesPolicy::class, 'manageModules']); + + Gate::define('manage settings', [SettingsPolicy::class, 'manageSettings']); + Gate::define('manage company', [SettingsPolicy::class, 'manageCompany']); + Gate::define('manage backups', [SettingsPolicy::class, 'manageBackups']); + Gate::define('manage file disk', [SettingsPolicy::class, 'manageFileDisk']); + Gate::define('manage email config', [SettingsPolicy::class, 'manageEmailConfig']); + Gate::define('manage notes', [NotePolicy::class, 'manageNotes']); + Gate::define('view notes', [NotePolicy::class, 'viewNotes']); + + Gate::define('send invoice', [InvoicePolicy::class, 'send']); + Gate::define('send estimate', [EstimatePolicy::class, 'send']); + Gate::define('send payment', [PaymentPolicy::class, 'send']); + + Gate::define('delete multiple items', [ItemPolicy::class, 'deleteMultiple']); + Gate::define('delete multiple customers', [CustomerPolicy::class, 'deleteMultiple']); + Gate::define('delete multiple users', [UserPolicy::class, 'deleteMultiple']); + Gate::define('delete multiple invoices', [InvoicePolicy::class, 'deleteMultiple']); + Gate::define('delete multiple estimates', [EstimatePolicy::class, 'deleteMultiple']); + Gate::define('delete multiple expenses', [ExpensePolicy::class, 'deleteMultiple']); + Gate::define('delete multiple payments', [PaymentPolicy::class, 'deleteMultiple']); + Gate::define('delete multiple recurring invoices', [RecurringInvoicePolicy::class, 'deleteMultiple']); + + Gate::define('view dashboard', [DashboardPolicy::class, 'view']); + + Gate::define('view report', [ReportPolicy::class, 'viewReport']); + + Gate::define('owner only', [OwnerPolicy::class, 'managedByOwner']); + } +} diff --git a/crater/app/Providers/BroadcastServiceProvider.php b/crater/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 0000000..b7fa5ce --- /dev/null +++ b/crater/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,21 @@ + 'api.auth']); + require base_path('routes/channels.php'); + } +} diff --git a/crater/app/Providers/DropboxServiceProvider.php b/crater/app/Providers/DropboxServiceProvider.php new file mode 100644 index 0000000..c2a477a --- /dev/null +++ b/crater/app/Providers/DropboxServiceProvider.php @@ -0,0 +1,38 @@ + [ + Version110::class, + Version200::class, + Version201::class, + Version202::class, + Version210::class, + Version300::class, + Version310::class, + Version311::class, + ], + Registered::class => [ + SendEmailVerificationNotification::class, + ], + ]; + + /** + * Register any events for your application. + * + * @return void + */ + public function boot() + { + parent::boot(); + + // + } +} diff --git a/crater/app/Providers/RouteServiceProvider.php b/crater/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..76b5312 --- /dev/null +++ b/crater/app/Providers/RouteServiceProvider.php @@ -0,0 +1,72 @@ +configureRateLimiting(); + + $this->routes(function () { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/api.php')); + + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web.php')); + }); + } + + /** + * Configure the rate limiters for the application. + * + * @return void + */ + protected function configureRateLimiting() + { + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60); + }); + } +} diff --git a/crater/app/Providers/ViewServiceProvider.php b/crater/app/Providers/ViewServiceProvider.php new file mode 100644 index 0000000..216adbd --- /dev/null +++ b/crater/app/Providers/ViewServiceProvider.php @@ -0,0 +1,36 @@ +has('database_created') && Schema::hasTable('settings')) { + View::share('login_page_logo', get_app_setting('login_page_logo')); + View::share('login_page_heading', get_app_setting('login_page_heading')); + View::share('login_page_description', get_app_setting('login_page_description')); + View::share('admin_page_title', get_app_setting('admin_page_title')); + View::share('copyright_text', get_app_setting('copyright_text')); + } + } +} diff --git a/crater/app/Rules/Backup/BackupDisk.php b/crater/app/Rules/Backup/BackupDisk.php new file mode 100644 index 0000000..1e285ff --- /dev/null +++ b/crater/app/Rules/Backup/BackupDisk.php @@ -0,0 +1,42 @@ +extensions = $extensions; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $this->attribute = $attribute; + + try { + $data = json_decode($value)->data; + } catch (\Exception $e) { + return false; + } + + $pattern = '/^data:\w+\/[\w\+]+;base64,[\w\+\=\/]+$/'; + + if (! preg_match($pattern, $data)) { + return false; + } + + $data = explode(',', $data); + + if (! isset($data[1]) || empty($data[1])) { + return false; + } + + try { + $data = base64_decode($data[1]); + $f = finfo_open(); + $result = finfo_buffer($f, $data, FILEINFO_EXTENSION); + + if ($result === '???') { + return false; + } + + if (strpos($result, '/')) { + foreach (explode('/', $result) as $ext) { + if (in_array($ext, $this->extensions)) { + return true; + } + } + } else { + if (in_array($result, $this->extensions)) { + return true; + } + } + } catch (\Exception $e) { + return false; + } + + return false; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The '.$this->attribute.' must be a json with file of type: '.implode(', ', $this->extensions).' encoded in base64.'; + } +} diff --git a/crater/app/Rules/RelationNotExist.php b/crater/app/Rules/RelationNotExist.php new file mode 100644 index 0000000..57086d6 --- /dev/null +++ b/crater/app/Rules/RelationNotExist.php @@ -0,0 +1,52 @@ +class = $class; + $this->relation = $relation; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $relation = $this->relation; + + if ($this->class::find($value)->$relation()->exists()) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return "Relation {$this->relation} exists."; + } +} diff --git a/crater/app/Services/Module/Module.php b/crater/app/Services/Module/Module.php new file mode 100644 index 0000000..fa6747a --- /dev/null +++ b/crater/app/Services/Module/Module.php @@ -0,0 +1,75 @@ +model = $model; + + return $this; + } + + public function setModelObject($id = null) + { + $this->ob = $this->model::find($id); + + if ($this->ob && $this->ob->sequence_number) { + $this->nextSequenceNumber = $this->ob->sequence_number; + } + + if (isset($this->ob) && isset($this->ob->customer_sequence_number) && isset($this->customer) && $this->ob->customer_id == $this->customer->id) { + $this->nextCustomerSequenceNumber = $this->ob->customer_sequence_number; + } + + return $this; + } + + /** + * @param $company + * @return $this + */ + public function setCompany($company) + { + $this->company = $company; + + return $this; + } + + /** + * @param $customer + * @return $this + */ + public function setCustomer($customer = null) + { + $this->customer = Customer::find($customer); + + return $this; + } + + /** + * @return string + */ + public function getNextNumber($data = null) + { + $modelName = strtolower(class_basename($this->model)); + $settingKey = $modelName.'_number_format'; + $companyId = $this->company; + + if (request()->has('format')) { + $format = request()->format; + } else { + $format = CompanySetting::getSetting( + $settingKey, + $companyId + ); + } + $this->setNextNumbers(); + + $serialNumber = $this->generateSerialNumber( + $format + ); + + return $serialNumber; + } + + public function setNextNumbers() + { + $this->nextSequenceNumber ? + $this->nextSequenceNumber : $this->setNextSequenceNumber(); + + $this->nextCustomerSequenceNumber ? + $this->nextCustomerSequenceNumber : $this->setNextCustomerSequenceNumber(); + + return $this; + } + + /** + * @return $this + */ + public function setNextSequenceNumber() + { + $companyId = $this->company; + + $last = $this->model::orderBy('sequence_number', 'desc') + ->where('company_id', $companyId) + ->where('sequence_number', '<>', null) + ->take(1) + ->first(); + + $this->nextSequenceNumber = ($last) ? $last->sequence_number + 1 : 1; + + return $this; + } + + /** + * @return self + */ + public function setNextCustomerSequenceNumber() + { + $customer_id = ($this->customer) ? $this->customer->id : 1; + + $last = $this->model::orderBy('customer_sequence_number', 'desc') + ->where('company_id', $this->company) + ->where('customer_id', $customer_id) + ->where('customer_sequence_number', '<>', null) + ->take(1) + ->first(); + + $this->nextCustomerSequenceNumber = ($last) ? $last->customer_sequence_number + 1 : 1; + + return $this; + } + + public static function getPlaceholders(string $format) + { + $regex = "/{{([A-Z_]{1,})(?::)?([a-zA-Z0-9_]{1,6}|.{1})?}}/"; + + preg_match_all($regex, $format, $placeholders); + array_shift($placeholders); + $validPlaceholders = collect(); + + /** @var array */ + $mappedPlaceholders = array_map( + null, + current($placeholders), + end($placeholders) + ); + + foreach ($mappedPlaceholders as $placeholder) { + $name = current($placeholder); + $value = end($placeholder); + + if (in_array($name, self::VALID_PLACEHOLDERS)) { + $validPlaceholders->push([ + "name" => $name, + "value" => $value + ]); + } + } + + return $validPlaceholders; + } + + /** + * @return string + */ + private function generateSerialNumber(string $format) + { + $serialNumber = ''; + + $placeholders = self::getPlaceholders($format); + + foreach ($placeholders as $placeholder) { + $name = $placeholder['name']; + $value = $placeholder['value']; + + switch ($name) { + case "SEQUENCE": + $value = $value ? $value : 6; + $serialNumber .= str_pad($this->nextSequenceNumber, $value, 0, STR_PAD_LEFT); + + break; + case "DATE_FORMAT": + $value = $value ? $value : 'Y'; + $serialNumber .= date($value); + + break; + case "RANDOM_SEQUENCE": + $value = $value ? $value : 6; + $serialNumber .= substr(bin2hex(random_bytes($value)), 0, $value); + + break; + case "CUSTOMER_SERIES": + if (isset($this->customer)) { + $serialNumber .= $this->customer->prefix ?? 'CST'; + } else { + $serialNumber .= 'CST'; + } + + break; + case "CUSTOMER_SEQUENCE": + $serialNumber .= str_pad($this->nextCustomerSequenceNumber, $value, 0, STR_PAD_LEFT); + + break; + default: + $serialNumber .= $value; + } + } + + return $serialNumber; + } +} diff --git a/crater/app/Space/DateFormatter.php b/crater/app/Space/DateFormatter.php new file mode 100644 index 0000000..a3c95bb --- /dev/null +++ b/crater/app/Space/DateFormatter.php @@ -0,0 +1,58 @@ + "Y M d", + "moment_format" => "YYYY MMM DD", + ], + [ + "carbon_format" => "d M Y", + "moment_format" => "DD MMM YYYY", + ], + [ + "carbon_format" => "d/m/Y", + "moment_format" => "DD/MM/YYYY", + ], + [ + "carbon_format" => "d.m.Y", + "moment_format" => "DD.MM.YYYY", + ], + [ + "carbon_format" => "d-m-Y", + "moment_format" => "DD-MM-YYYY", + ], + [ + "carbon_format" => "m/d/Y", + "moment_format" => "MM/DD/YYYY", + ], + [ + "carbon_format" => "Y/m/d", + "moment_format" => " YYYY/MM/DD", + ], + [ + "carbon_format" => "Y-m-d", + "moment_format" => "YYYY-MM-DD", + ], + ]; + + public static function get_list() + { + $new = []; + + foreach (static::$formats as $format) { + $new[] = [ + "display_date" => Carbon::now()->format($format['carbon_format']) , + "carbon_format_value" => $format['carbon_format'], + "moment_format_value" => $format['moment_format'], + ]; + } + + return $new; + } +} diff --git a/crater/app/Space/EnvironmentManager.php b/crater/app/Space/EnvironmentManager.php new file mode 100755 index 0000000..7ba57dc --- /dev/null +++ b/crater/app/Space/EnvironmentManager.php @@ -0,0 +1,583 @@ +envPath = base_path('.env'); + } + + /** + * Save the database content to the .env file. + * + * @param DatabaseEnvironmentRequest $request + * @return array + */ + public function saveDatabaseVariables(DatabaseEnvironmentRequest $request) + { + $oldDatabaseData = + 'DB_CONNECTION='.config('database.default')."\n"; + + $newDatabaseData = + 'DB_CONNECTION='.$request->database_connection."\n"; + + if ($request->has('database_username') && $request->has('database_password')) { + if (env('DB_USERNAME') && env('DB_HOST')) { + $oldDatabaseData = $oldDatabaseData. + 'DB_HOST='.config('database.connections.'.config('database.default').'.host')."\n". + 'DB_PORT='.config('database.connections.'.config('database.default').'.port')."\n". + 'DB_DATABASE='.config('database.connections.'.config('database.default').'.database')."\n". + 'DB_USERNAME='.config('database.connections.'.config('database.default').'.username')."\n". + 'DB_PASSWORD="'.config('database.connections.'.config('database.default').'.password')."\"\n\n"; + } else { + $oldDatabaseData = $oldDatabaseData. + 'DB_DATABASE='.config('database.connections.'.config('database.default').'.database')."\n\n"; + } + + $newDatabaseData = $newDatabaseData. + 'DB_HOST='.$request->database_hostname."\n". + 'DB_PORT='.$request->database_port."\n". + 'DB_DATABASE='.$request->database_name."\n". + 'DB_USERNAME='.$request->database_username."\n". + 'DB_PASSWORD="'.$request->database_password."\"\n\n"; + } else { + if (env('DB_USERNAME') && env('DB_HOST')) { + $oldDatabaseData = $oldDatabaseData. + 'DB_HOST='.config('database.connections.'.config('database.default').'.host')."\n". + 'DB_PORT='.config('database.connections.'.config('database.default').'.port')."\n". + 'DB_DATABASE='.config('database.connections.'.config('database.default').'.database')."\n". + 'DB_USERNAME='.config('database.connections.'.config('database.default').'.username')."\n". + 'DB_PASSWORD="'.config('database.connections.'.config('database.default').'.password')."\"\n\n"; + } else { + $oldDatabaseData = $oldDatabaseData. + 'DB_DATABASE='.config('database.connections.'.config('database.default').'.database')."\n\n"; + } + + $newDatabaseData = $newDatabaseData. + 'DB_DATABASE='.$request->database_name."\n\n"; + } + + try { + $conn = $this->checkDatabaseConnection($request); + + // $requirement = $this->checkVersionRequirements($request, $conn); + + // if ($requirement) { + // return [ + // 'error' => 'minimum_version_requirement', + // 'requirement' => $requirement, + // ]; + // } + + if (\Schema::hasTable('users')) { + return [ + 'error' => 'database_should_be_empty', + ]; + } + } catch (Exception $e) { + return [ + 'error_message' => $e->getMessage(), + ]; + } + + try { + file_put_contents($this->envPath, str_replace( + $oldDatabaseData, + $newDatabaseData, + file_get_contents($this->envPath) + )); + + file_put_contents($this->envPath, str_replace( + 'APP_URL='.config('app.url'), + 'APP_URL='.$request->app_url, + file_get_contents($this->envPath) + )); + + file_put_contents($this->envPath, str_replace( + 'SANCTUM_STATEFUL_DOMAINS='.env('SANCTUM_STATEFUL_DOMAINS'), + 'SANCTUM_STATEFUL_DOMAINS='.$request->app_domain, + file_get_contents($this->envPath) + )); + + + file_put_contents($this->envPath, str_replace( + 'SESSION_DOMAIN='.config('session.domain'), + 'SESSION_DOMAIN='.explode(':', $request->app_domain)[0], + file_get_contents($this->envPath) + )); + } catch (Exception $e) { + return [ + 'error' => 'database_variables_save_error', + ]; + } + + return [ + 'success' => 'database_variables_save_successfully', + ]; + } + + /** + * + * @param DatabaseEnvironmentRequest $request + * @return bool + */ + private function checkDatabaseConnection(DatabaseEnvironmentRequest $request) + { + $connection = $request->database_connection; + + $settings = config("database.connections.$connection"); + $settings = config("database.connections.$connection"); + + $connectionArray = array_merge($settings, [ + 'driver' => $connection, + 'database' => $request->database_name, + ]); + + if ($request->has('database_username') && $request->has('database_password')) { + $connectionArray = array_merge($connectionArray, [ + 'username' => $request->database_username, + 'password' => $request->database_password, + 'host' => $request->database_hostname, + 'port' => $request->database_port, + ]); + } + + config([ + 'database' => [ + 'migrations' => 'migrations', + 'default' => $connection, + 'connections' => [$connection => $connectionArray], + ], + ]); + + return DB::connection()->getPdo(); + } + + /** + * + * @param DatabaseEnvironmentRequest $request + * @return bool + */ + private function checkVersionRequirements(DatabaseEnvironmentRequest $request, $conn) + { + $connection = $request->database_connection; + + $checker = new RequirementsChecker(); + + $phpSupportInfo = $checker->checkPHPVersion( + config('crater.min_php_version') + ); + + if (! $phpSupportInfo['supported']) { + return $phpSupportInfo; + } + + $dbSupportInfo = []; + + switch ($connection) { + case 'mysql': + $dbSupportInfo = $checker->checkMysqlVersion($conn); + + break; + + case 'pgsql': + $conn = pg_connect("host={$request->database_hostname} port={$request->database_port} dbname={$request->database_name} user={$request->database_username} password={$request->database_password}"); + $dbSupportInfo = $checker->checkPgsqlVersion( + $conn, + config('crater.min_pgsql_version') + ); + + break; + + case 'sqlite': + $dbSupportInfo = $checker->checkSqliteVersion( + config('crater.min_sqlite_version') + ); + + break; + + } + + if (! $dbSupportInfo['supported']) { + return $dbSupportInfo; + } + + return false; + } + + /** + * Save the mail content to the .env file. + * + * @param Request $request + * @return array + */ + public function saveMailVariables(MailEnvironmentRequest $request) + { + $mailData = $this->getMailData($request); + + try { + file_put_contents($this->envPath, str_replace( + $mailData['old_mail_data'], + $mailData['new_mail_data'], + file_get_contents($this->envPath) + )); + + if ($mailData['extra_old_mail_data']) { + file_put_contents($this->envPath, str_replace( + $mailData['extra_old_mail_data'], + $mailData['extra_mail_data'], + file_get_contents($this->envPath) + )); + } else { + file_put_contents( + $this->envPath, + "\n".$mailData['extra_mail_data'], + FILE_APPEND + ); + } + } catch (Exception $e) { + return [ + 'error' => 'mail_variables_save_error', + ]; + } + + return [ + 'success' => 'mail_variables_save_successfully', + ]; + } + + private function getMailData($request) + { + $mailFromCredential = ""; + $extraMailData = ""; + $extraOldMailData = ""; + $oldMailData = ""; + $newMailData = ""; + + if (env('MAIL_FROM_ADDRESS') !== null && env('MAIL_FROM_NAME') !== null) { + $mailFromCredential = + 'MAIL_FROM_ADDRESS='.config('mail.from.address')."\n". + 'MAIL_FROM_NAME="'.config('mail.from.name')."\"\n\n"; + } + + switch ($request->mail_driver) { + case 'smtp': + + $oldMailData = + 'MAIL_DRIVER='.config('mail.driver')."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + $mailFromCredential; + + $newMailData = + 'MAIL_DRIVER='.$request->mail_driver."\n". + 'MAIL_HOST='.$request->mail_host."\n". + 'MAIL_PORT='.$request->mail_port."\n". + 'MAIL_USERNAME='.$request->mail_username."\n". + 'MAIL_PASSWORD='.$request->mail_password."\n". + 'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n". + 'MAIL_FROM_ADDRESS='.$request->from_mail."\n". + 'MAIL_FROM_NAME="'.$request->from_name."\"\n\n"; + + break; + + case 'mailgun': + $oldMailData = + 'MAIL_DRIVER='.config('mail.driver')."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + $mailFromCredential; + + $newMailData = + 'MAIL_DRIVER='.$request->mail_driver."\n". + 'MAIL_HOST='.$request->mail_host."\n". + 'MAIL_PORT='.$request->mail_port."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n". + 'MAIL_FROM_ADDRESS='.$request->from_mail."\n". + 'MAIL_FROM_NAME="'.$request->from_name."\"\n\n"; + + $extraMailData = + 'MAILGUN_DOMAIN='.$request->mail_mailgun_domain."\n". + 'MAILGUN_SECRET='.$request->mail_mailgun_secret."\n". + 'MAILGUN_ENDPOINT='.$request->mail_mailgun_endpoint."\n"; + + if (env('MAILGUN_DOMAIN') !== null && env('MAILGUN_SECRET') !== null && env('MAILGUN_ENDPOINT') !== null) { + $extraOldMailData = + 'MAILGUN_DOMAIN='.config('services.mailgun.domain')."\n". + 'MAILGUN_SECRET='.config('services.mailgun.secret')."\n". + 'MAILGUN_ENDPOINT='.config('services.mailgun.endpoint')."\n"; + } + + break; + + case 'ses': + $oldMailData = + 'MAIL_DRIVER='.config('mail.driver')."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + $mailFromCredential; + + $newMailData = + 'MAIL_DRIVER='.$request->mail_driver."\n". + 'MAIL_HOST='.$request->mail_host."\n". + 'MAIL_PORT='.$request->mail_port."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n". + 'MAIL_FROM_ADDRESS='.$request->from_mail."\n". + 'MAIL_FROM_NAME="'.$request->from_name."\"\n\n"; + + $extraMailData = + 'SES_KEY='.$request->mail_ses_key."\n". + 'SES_SECRET='.$request->mail_ses_secret."\n"; + + if (env('SES_KEY') !== null && env('SES_SECRET') !== null) { + $extraOldMailData = + 'SES_KEY='.config('services.ses.key')."\n". + 'SES_SECRET='.config('services.ses.secret')."\n"; + } + + break; + + case 'mail': + $oldMailData = + 'MAIL_DRIVER='.config('mail.driver')."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + $mailFromCredential; + + $newMailData = + 'MAIL_DRIVER='.$request->mail_driver."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + 'MAIL_FROM_ADDRESS='.$request->from_mail."\n". + 'MAIL_FROM_NAME="'.$request->from_name."\"\n\n"; + + break; + + case 'sendmail': + $oldMailData = + 'MAIL_DRIVER='.config('mail.driver')."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + $mailFromCredential; + + $newMailData = + 'MAIL_DRIVER='.$request->mail_driver."\n". + 'MAIL_HOST='.config('mail.host')."\n". + 'MAIL_PORT='.config('mail.port')."\n". + 'MAIL_USERNAME='.config('mail.username')."\n". + 'MAIL_PASSWORD='.config('mail.password')."\n". + 'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n". + 'MAIL_FROM_ADDRESS='.$request->from_mail."\n". + 'MAIL_FROM_NAME="'.$request->from_name."\"\n\n"; + + break; + } + + return [ + 'old_mail_data' => $oldMailData, + 'new_mail_data' => $newMailData, + 'extra_mail_data' => $extraMailData, + 'extra_old_mail_data' => $extraOldMailData, + ]; + } + + /** + * Save the disk content to the .env file. + * + * @param Request $request + * @return array + */ + public function saveDiskVariables(DiskEnvironmentRequest $request) + { + $diskData = $this->getDiskData($request); + + try { + if (! $diskData['old_default_driver']) { + file_put_contents($this->envPath, $diskData['default_driver'], FILE_APPEND); + } else { + file_put_contents($this->envPath, str_replace( + $diskData['old_default_driver'], + $diskData['default_driver'], + file_get_contents($this->envPath) + )); + } + + if (! $diskData['old_disk_data']) { + file_put_contents($this->envPath, $diskData['new_disk_data'], FILE_APPEND); + } else { + file_put_contents($this->envPath, str_replace( + $diskData['old_disk_data'], + $diskData['new_disk_data'], + file_get_contents($this->envPath) + )); + } + } catch (Exception $e) { + return [ + 'error' => 'disk_variables_save_error', + ]; + } + + return [ + 'success' => 'disk_variables_save_successfully', + ]; + } + + private function getDiskData($request) + { + $oldDefaultDriver = ""; + $defaultDriver = ""; + $oldDiskData = ""; + $newDiskData = ""; + + if ($request->default_driver) { + if (env('FILESYSTEM_DRIVER') !== null) { + $defaultDriver = "\n".'FILESYSTEM_DRIVER='.$request->default_driver."\n"; + + $oldDefaultDriver = + "\n".'FILESYSTEM_DRIVER='.config('filesystems.default')."\n"; + } else { + $defaultDriver = + "\n".'FILESYSTEM_DRIVER='.$request->default_driver."\n"; + } + } + + switch ($request->selected_driver) { + case 's3': + if (env('AWS_KEY') !== null) { + $oldDiskData = "\n". + 'AWS_KEY='.config('filesystems.disks.s3.key')."\n". + 'AWS_SECRET="'.config('filesystems.disks.s3.secret')."\"\n". + 'AWS_REGION='.config('filesystems.disks.s3.region')."\n". + 'AWS_BUCKET='.config('filesystems.disks.s3.bucket')."\n". + 'AWS_ROOT='.config('filesystems.disks.s3.root')."\n"; + } + + $newDiskData = "\n". + 'AWS_KEY='.$request->aws_key."\n". + 'AWS_SECRET="'.$request->aws_secret."\"\n". + 'AWS_REGION='.$request->aws_region."\n". + 'AWS_BUCKET='.$request->aws_bucket."\n". + 'AWS_ROOT='.$request->aws_root."\n"; + + break; + + case 'doSpaces': + if (env('DO_SPACES_KEY') !== null) { + $oldDiskData = "\n". + 'DO_SPACES_KEY='.config('filesystems.disks.doSpaces.key')."\n". + 'DO_SPACES_SECRET="'.config('filesystems.disks.doSpaces.secret')."\"\n". + 'DO_SPACES_REGION='.config('filesystems.disks.doSpaces.region')."\n". + 'DO_SPACES_BUCKET='.config('filesystems.disks.doSpaces.bucket')."\n". + 'DO_SPACES_ENDPOINT='.config('filesystems.disks.doSpaces.endpoint')."\n"; + 'DO_SPACES_ROOT='.config('filesystems.disks.doSpaces.root')."\n"; + } + + $newDiskData = "\n". + 'DO_SPACES_KEY='.$request->do_spaces_key."\n". + 'DO_SPACES_SECRET="'.$request->do_spaces_secret."\"\n". + 'DO_SPACES_REGION='.$request->do_spaces_region."\n". + 'DO_SPACES_BUCKET='.$request->do_spaces_bucket."\n". + 'DO_SPACES_ENDPOINT='.$request->do_spaces_endpoint."\n"; + 'DO_SPACES_ROOT='.$request->do_spaces_root."\n\n"; + + break; + + case 'dropbox': + if (env('DROPBOX_TOKEN') !== null) { + $oldDiskData = "\n". + 'DROPBOX_TOKEN='.config('filesystems.disks.dropbox.token')."\n". + 'DROPBOX_KEY='.config('filesystems.disks.dropbox.key')."\n". + 'DROPBOX_SECRET="'.config('filesystems.disks.dropbox.secret')."\"\n". + 'DROPBOX_APP='.config('filesystems.disks.dropbox.app')."\n". + 'DROPBOX_ROOT='.config('filesystems.disks.dropbox.root')."\n"; + } + + $newDiskData = "\n". + 'DROPBOX_TOKEN='.$request->dropbox_token."\n". + 'DROPBOX_KEY='.$request->dropbox_key."\n". + 'DROPBOX_SECRET="'.$request->dropbox_secret."\"\n". + 'DROPBOX_APP='.$request->dropbox_app."\n". + 'DROPBOX_ROOT='.$request->dropbox_root."\n"; + + break; + } + + return [ + 'old_disk_data' => $oldDiskData, + 'new_disk_data' => $newDiskData, + 'default_driver' => $defaultDriver, + 'old_default_driver' => $oldDefaultDriver, + ]; + } + + /** + * Save sanctum statful domain to the .env file. + * + * @param DomainEnvironmentRequest $request + * @return array + */ + public function saveDomainVariables(DomainEnvironmentRequest $request) + { + try { + file_put_contents($this->envPath, str_replace( + 'SANCTUM_STATEFUL_DOMAINS='.env('SANCTUM_STATEFUL_DOMAINS'), + 'SANCTUM_STATEFUL_DOMAINS='.$request->app_domain, + file_get_contents($this->envPath) + )); + + file_put_contents($this->envPath, str_replace( + 'SESSION_DOMAIN='.config('session.domain'), + 'SESSION_DOMAIN='.explode(':', $request->app_domain)[0], + file_get_contents($this->envPath) + )); + } catch (Exception $e) { + return [ + 'error' => 'domain_verification_failed' + ]; + } + + return [ + 'success' => 'domain_variable_save_successfully' + ]; + } +} diff --git a/crater/app/Space/FilePermissionChecker.php b/crater/app/Space/FilePermissionChecker.php new file mode 100644 index 0000000..e8d30ce --- /dev/null +++ b/crater/app/Space/FilePermissionChecker.php @@ -0,0 +1,83 @@ +results['permissions'] = []; + + $this->results['errors'] = null; + } + + /** + * Check for the folders permissions. + * + * @param array $folders + * @return array + */ + public function check(array $folders) + { + foreach ($folders as $folder => $permission) { + if (! ($this->getPermission($folder) >= $permission)) { + $this->addFileAndSetErrors($folder, $permission, false); + } else { + $this->addFile($folder, $permission, true); + } + } + + return $this->results; + } + + /** + * Get a folder permission. + * + * @param $folder + * @return string + */ + private function getPermission($folder) + { + return substr(sprintf('%o', fileperms(base_path($folder))), -4); + } + + /** + * Add the file to the list of results. + * + * @param $folder + * @param $permission + * @param $isSet + */ + private function addFile($folder, $permission, $isSet) + { + array_push($this->results['permissions'], [ + 'folder' => $folder, + 'permission' => $permission, + 'isSet' => $isSet, + ]); + } + + /** + * Add the file and set the errors. + * + * @param $folder + * @param $permission + * @param $isSet + */ + private function addFileAndSetErrors($folder, $permission, $isSet) + { + $this->addFile($folder, $permission, $isSet); + + $this->results['errors'] = true; + } +} diff --git a/crater/app/Space/ModuleInstaller.php b/crater/app/Space/ModuleInstaller.php new file mode 100644 index 0000000..d0d9968 --- /dev/null +++ b/crater/app/Space/ModuleInstaller.php @@ -0,0 +1,231 @@ + 100, 'track_redirects' => true], $token); + + if ($response && ($response->getStatusCode() == 401)) { + return response()->json(['error' => 'invalid_token']); + } + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + $data = json_decode($data); + + return ModuleResource::collection(collect($data->modules)); + } + + public static function getModule($module) + { + $data = null; + if (env('APP_ENV') === 'development') { + $url = 'api/marketplace/modules/'.$module.'?is_dev=1'; + } else { + $url = 'api/marketplace/modules/'.$module; + } + + $token = Setting::getSetting('api_token'); + $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true], $token); + + if ($response && ($response->getStatusCode() == 401)) { + return (object)['success' => false, 'error' => 'invalid_token']; + } + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + $data = json_decode($data); + + return $data; + } + + public static function upload($request) + { + // Create temp directory + $temp_dir = storage_path('app/temp-'.md5(mt_rand())); + + if (! File::isDirectory($temp_dir)) { + File::makeDirectory($temp_dir); + } + + $path = $request->file('avatar')->storeAs( + 'temp-'.md5(mt_rand()), + $request->module.'.zip', + 'local' + ); + + return $path; + } + + public static function download($module, $version) + { + $data = null; + $path = null; + + if (env('APP_ENV') === 'development') { + $url = "api/marketplace/modules/file/{$module}?version={$version}&is_dev=1"; + } else { + $url = "api/marketplace/modules/file/{$module}?version={$version}"; + } + + $token = Setting::getSetting('api_token'); + $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true], $token); + + // Exception + if ($response instanceof RequestException) { + return [ + 'success' => false, + 'error' => 'Download Exception', + 'data' => [ + 'path' => $path, + ], + ]; + } + + if ($response && ($response->getStatusCode() == 401 || $response->getStatusCode() == 404 || $response->getStatusCode() == 500)) { + return json_decode($response->getBody()->getContents()); + } + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + // Create temp directory + $temp_dir = storage_path('app/temp-'.md5(mt_rand())); + + if (! File::isDirectory($temp_dir)) { + File::makeDirectory($temp_dir); + } + + $zip_file_path = $temp_dir.'/upload.zip'; + + // Add content to the Zip file + $uploaded = is_int(file_put_contents($zip_file_path, $data)) ? true : false; + + if (! $uploaded) { + return false; + } + + return [ + 'success' => true, + 'path' => $zip_file_path + ]; + } + + public static function unzip($module, $zip_file_path) + { + if (! file_exists($zip_file_path)) { + throw new \Exception('Zip file not found'); + } + + $temp_extract_dir = storage_path('app/temp2-'.md5(mt_rand())); + + if (! File::isDirectory($temp_extract_dir)) { + File::makeDirectory($temp_extract_dir); + } + // Unzip the file + $zip = new ZipArchive(); + + if ($zip->open($zip_file_path)) { + $zip->extractTo($temp_extract_dir); + } + + $zip->close(); + + // Delete zip file + File::delete($zip_file_path); + + return $temp_extract_dir; + } + + public static function copyFiles($module, $temp_extract_dir) + { + if (! File::isDirectory(base_path('Modules'))) { + File::makeDirectory(base_path('Modules')); + } + + // Delete Existing Module directory + if (! File::isDirectory(base_path('Modules').'/'.$module)) { + File::deleteDirectory(base_path('Modules').'/'.$module); + } + + if (! File::copyDirectory($temp_extract_dir, base_path('Modules').'/')) { + return false; + } + + // Delete temp directory + File::deleteDirectory($temp_extract_dir); + + return true; + } + + public static function deleteFiles($json) + { + $files = json_decode($json); + + foreach ($files as $file) { + \File::delete(base_path($file)); + } + + return true; + } + + public static function complete($module, $version) + { + Module::register(); + + Artisan::call("module:migrate $module --force"); + Artisan::call("module:seed $module --force"); + Artisan::call("module:enable $module"); + + $module = ModelsModule::updateOrCreate(['name' => $module], ['version' => $version, 'installed' => true, 'enabled' => true]); + + ModuleInstalledEvent::dispatch($module); + ModuleEnabledEvent::dispatch($module); + + return true; + } + + public static function checkToken(String $token) + { + $url = 'api/marketplace/ping'; + $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true], $token); + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + + return response()->json(json_decode($data)); + } + + return response()->json(['error' => 'invalid_token']); + } +} diff --git a/crater/app/Space/RequirementsChecker.php b/crater/app/Space/RequirementsChecker.php new file mode 100755 index 0000000..48d808f --- /dev/null +++ b/crater/app/Space/RequirementsChecker.php @@ -0,0 +1,236 @@ + $requirement) { + switch ($type) { + // check php requirements + case 'php': + foreach ($requirements[$type] as $requirement) { + $results['requirements'][$type][$requirement] = true; + + if (! extension_loaded($requirement)) { + $results['requirements'][$type][$requirement] = false; + + $results['errors'] = true; + } + } + + break; + // check apache requirements + case 'apache': + foreach ($requirements[$type] as $requirement) { + // if function doesn't exist we can't check apache modules + if (function_exists('apache_get_modules')) { + $results['requirements'][$type][$requirement] = true; + + if (! in_array($requirement, apache_get_modules())) { + $results['requirements'][$type][$requirement] = false; + + $results['errors'] = true; + } + } + } + + break; + } + } + + return $results; + } + + /** + * Check PHP version requirement. + * + * @return array + */ + public function checkPHPVersion(string $minPhpVersion = null) + { + $minVersionPhp = $minPhpVersion; + $currentPhpVersion = $this->getPhpVersionInfo(); + $supported = false; + + if ($minPhpVersion == null) { + $minVersionPhp = $this->getMinPhpVersion(); + } + + if (version_compare($currentPhpVersion['version'], $minVersionPhp) >= 0) { + $supported = true; + } + + $phpStatus = [ + 'full' => $currentPhpVersion['full'], + 'current' => $currentPhpVersion['version'], + 'minimum' => $minVersionPhp, + 'supported' => $supported, + ]; + + return $phpStatus; + } + + /** + * Get current Php version information. + * + * @return array + */ + private static function getPhpVersionInfo() + { + $currentVersionFull = PHP_VERSION; + preg_match("#^\d+(\.\d+)*#", $currentVersionFull, $filtered); + $currentVersion = $filtered[0]; + + return [ + 'full' => $currentVersionFull, + 'version' => $currentVersion, + ]; + } + + /** + * Get minimum PHP version ID. + * + * @return string _minPhpVersion + */ + protected function getMinPhpVersion() + { + return $this->_minPhpVersion; + } + + /** + * Check PHP version requirement. + * + * @return array + */ + public function checkMysqlVersion($conn) + { + $version_info = $conn->getAttribute(PDO::ATTR_SERVER_VERSION); + + $isMariaDb = Str::contains($version_info, 'MariaDB'); + + $minVersionMysql = $isMariaDb ? config('crater.min_mariadb_version') : config('crater.min_mysql_version'); + + $currentMysqlVersion = $this->getMysqlVersionInfo($conn); + + $supported = false; + + if (version_compare($currentMysqlVersion, $minVersionMysql) >= 0) { + $supported = true; + } + + $phpStatus = [ + 'current' => $currentMysqlVersion, + 'minimum' => $minVersionMysql, + 'supported' => $supported, + ]; + + return $phpStatus; + } + + /** + * Get current Mysql version information. + * + * @return string + */ + private static function getMysqlVersionInfo($pdo) + { + $version = $pdo->query('select version()')->fetchColumn(); + + preg_match("/^[0-9\.]+/", $version, $match); + + return $match[0]; + } + + /** + * Check Sqlite version requirement. + * + * @return array + */ + public function checkSqliteVersion(string $minSqliteVersion = null) + { + $minVersionSqlite = $minSqliteVersion; + $currentSqliteVersion = $this->getSqliteVersionInfo(); + $supported = false; + + if (version_compare($currentSqliteVersion, $minVersionSqlite) >= 0) { + $supported = true; + } + + $phpStatus = [ + 'current' => $currentSqliteVersion, + 'minimum' => $minVersionSqlite, + 'supported' => $supported, + ]; + + return $phpStatus; + } + + /** + * Get current Sqlite version information. + * + * @return string + */ + private static function getSqliteVersionInfo() + { + $currentVersion = SQLite3::version(); + + return $currentVersion['versionString']; + } + + /** + * Check Pgsql version requirement. + * + * @return array + */ + public function checkPgsqlVersion($conn, string $minPgsqlVersion = null) + { + $minVersionPgsql = $minPgsqlVersion; + $currentPgsqlVersion = $this->getPgsqlVersionInfo($conn); + $supported = false; + + if (version_compare($currentPgsqlVersion, $minVersionPgsql) >= 0) { + $supported = true; + } + + $phpStatus = [ + 'current' => $currentPgsqlVersion, + 'minimum' => $minVersionPgsql, + 'supported' => $supported, + ]; + + return $phpStatus; + } + + /** + * Get current Pgsql version information. + * + * @return string + */ + private static function getPgsqlVersionInfo($conn) + { + $currentVersion = pg_version($conn); + + return $currentVersion['server']; + } +} diff --git a/crater/app/Space/SiteApi.php b/crater/app/Space/SiteApi.php new file mode 100644 index 0000000..e10a4e9 --- /dev/null +++ b/crater/app/Space/SiteApi.php @@ -0,0 +1,35 @@ + false, 'base_uri' => config('crater.base_url').'/']); + + $headers['headers'] = [ + 'Accept' => 'application/json', + 'Referer' => url('/'), + 'crater' => Setting::getSetting('version'), + 'Authorization' => "Bearer {$token}", + ]; + + $data['http_errors'] = false; + + $data = array_merge($data, $headers); + + try { + $result = $client->get($url, $data); + } catch (RequestException $e) { + $result = $e; + } + + return $result; + } +} diff --git a/crater/app/Space/TimeZones.php b/crater/app/Space/TimeZones.php new file mode 100644 index 0000000..d83a5b9 --- /dev/null +++ b/crater/app/Space/TimeZones.php @@ -0,0 +1,431 @@ + 'Pacific/Midway', 'key' => '(UTC-11:00) Midway'], + ['value' => 'Pacific/Niue', 'key' => '(UTC-11:00) Niue'], + ['value' => 'Pacific/Pago_Pago', 'key' => '(UTC-11:00) Pago Pago'], + ['value' => 'America/Adak', 'key' => '(UTC-10:00) Adak'], + ['value' => 'Pacific/Honolulu', 'key' => '(UTC-10:00) Honolulu'], + ['value' => 'Pacific/Johnston', 'key' => '(UTC-10:00) Johnston'], + ['value' => 'Pacific/Rarotonga', 'key' => '(UTC-10:00) Rarotonga'], + ['value' => 'Pacific/Tahiti', 'key' => '(UTC-10:00) Tahiti'], + ['value' => 'Pacific/Marquesas', 'key' => '(UTC-09:30) Marquesas'], + ['value' => 'America/Anchorage', 'key' => '(UTC-09:00) Anchorage'], + ['value' => 'Pacific/Gambier', 'key' => '(UTC-09:00) Gambier'], + ['value' => 'America/Juneau', 'key' => '(UTC-09:00) Juneau'], + ['value' => 'America/Nome', 'key' => '(UTC-09:00) Nome'], + ['value' => 'America/Sitka', 'key' => '(UTC-09:00) Sitka'], + ['value' => 'America/Yakutat', 'key' => '(UTC-09:00) Yakutat'], + ['value' => 'America/Dawson', 'key' => '(UTC-08:00) Dawson'], + ['value' => 'America/Los_Angeles', 'key' => '(UTC-08:00) Los Angeles'], + ['value' => 'America/Metlakatla', 'key' => '(UTC-08:00) Metlakatla'], + ['value' => 'Pacific/Pitcairn', 'key' => '(UTC-08:00) Pitcairn'], + ['value' => 'America/Santa_Isabel', 'key' => '(UTC-08:00) Santa Isabel'], + ['value' => 'America/Tijuana', 'key' => '(UTC-08:00) Tijuana'], + ['value' => 'America/Vancouver', 'key' => '(UTC-08:00) Vancouver'], + ['value' => 'America/Whitehorse', 'key' => '(UTC-08:00) Whitehorse'], + ['value' => 'America/Boise', 'key' => '(UTC-07:00) Boise'], + ['value' => 'America/Cambridge_Bay', 'key' => '(UTC-07:00) Cambridge Bay'], + ['value' => 'America/Chihuahua', 'key' => '(UTC-07:00) Chihuahua'], + ['value' => 'America/Creston', 'key' => '(UTC-07:00) Creston'], + ['value' => 'America/Dawson_Creek', 'key' => '(UTC-07:00) Dawson Creek'], + ['value' => 'America/Denver', 'key' => '(UTC-07:00) Denver'], + ['value' => 'America/Edmonton', 'key' => '(UTC-07:00) Edmonton'], + ['value' => 'America/Hermosillo', 'key' => '(UTC-07:00) Hermosillo'], + ['value' => 'America/Inuvik', 'key' => '(UTC-07:00) Inuvik'], + ['value' => 'America/Mazatlan', 'key' => '(UTC-07:00) Mazatlan'], + ['value' => 'America/Ojinaga', 'key' => '(UTC-07:00) Ojinaga'], + ['value' => 'America/Phoenix', 'key' => '(UTC-07:00) Phoenix'], + ['value' => 'America/Shiprock', 'key' => '(UTC-07:00) Shiprock'], + ['value' => 'America/Yellowknife', 'key' => '(UTC-07:00) Yellowknife'], + ['value' => 'America/Bahia_Banderas', 'key' => '(UTC-06:00) Bahia Banderas'], + ['value' => 'America/Belize', 'key' => '(UTC-06:00) Belize'], + ['value' => 'America/North_Dakota/Beulah', 'key' => '(UTC-06:00) Beulah'], + ['value' => 'America/Cancun', 'key' => '(UTC-06:00) Cancun'], + ['value' => 'America/North_Dakota/Center', 'key' => '(UTC-06:00) Center'], + ['value' => 'America/Chicago', 'key' => '(UTC-06:00) Chicago'], + ['value' => 'America/Costa_Rica', 'key' => '(UTC-06:00) Costa Rica'], + ['value' => 'Pacific/Easter', 'key' => '(UTC-06:00) Easter'], + ['value' => 'America/El_Salvador', 'key' => '(UTC-06:00) El Salvador'], + ['value' => 'Pacific/Galapagos', 'key' => '(UTC-06:00) Galapagos'], + ['value' => 'America/Guatemala', 'key' => '(UTC-06:00) Guatemala'], + ['value' => 'America/Indiana/Knox', 'key' => '(UTC-06:00) Knox'], + ['value' => 'America/Managua', 'key' => '(UTC-06:00) Managua'], + ['value' => 'America/Matamoros', 'key' => '(UTC-06:00) Matamoros'], + ['value' => 'America/Menominee', 'key' => '(UTC-06:00) Menominee'], + ['value' => 'America/Merida', 'key' => '(UTC-06:00) Merida'], + ['value' => 'America/Mexico_City', 'key' => '(UTC-06:00) Mexico City'], + ['value' => 'America/Monterrey', 'key' => '(UTC-06:00) Monterrey'], + ['value' => 'America/North_Dakota/New_Salem', 'key' => '(UTC-06:00) New Salem'], + ['value' => 'America/Rainy_River', 'key' => '(UTC-06:00) Rainy River'], + ['value' => 'America/Rankin_Inlet', 'key' => '(UTC-06:00) Rankin Inlet'], + ['value' => 'America/Regina', 'key' => '(UTC-06:00) Regina'], + ['value' => 'America/Resolute', 'key' => '(UTC-06:00) Resolute'], + ['value' => 'America/Swift_Current', 'key' => '(UTC-06:00) Swift Current'], + ['value' => 'America/Tegucigalpa', 'key' => '(UTC-06:00) Tegucigalpa'], + ['value' => 'America/Indiana/Tell_City', 'key' => '(UTC-06:00) Tell City'], + ['value' => 'America/Winnipeg', 'key' => '(UTC-06:00) Winnipeg'], + ['value' => 'America/Atikokan', 'key' => '(UTC-05:00) Atikokan'], + ['value' => 'America/Bogota', 'key' => '(UTC-05:00) Bogota'], + ['value' => 'America/Cayman', 'key' => '(UTC-05:00) Cayman'], + ['value' => 'America/Detroit', 'key' => '(UTC-05:00) Detroit'], + ['value' => 'America/Grand_Turk', 'key' => '(UTC-05:00) Grand Turk'], + ['value' => 'America/Guayaquil', 'key' => '(UTC-05:00) Guayaquil'], + ['value' => 'America/Havana', 'key' => '(UTC-05:00) Havana'], + ['value' => 'America/Indiana/Indianapolis', 'key' => '(UTC-05:00) Indianapolis'], + ['value' => 'America/Iqaluit', 'key' => '(UTC-05:00) Iqaluit'], + ['value' => 'America/Jamaica', 'key' => '(UTC-05:00) Jamaica'], + ['value' => 'America/Lima', 'key' => '(UTC-05:00) Lima'], + ['value' => 'America/Kentucky/Louisville', 'key' => '(UTC-05:00) Louisville'], + ['value' => 'America/Indiana/Marengo', 'key' => '(UTC-05:00) Marengo'], + ['value' => 'America/Kentucky/Monticello', 'key' => '(UTC-05:00) Monticello'], + ['value' => 'America/Montreal', 'key' => '(UTC-05:00) Montreal'], + ['value' => 'America/Nassau', 'key' => '(UTC-05:00) Nassau'], + ['value' => 'America/New_York', 'key' => '(UTC-05:00) New York'], + ['value' => 'America/Nipigon', 'key' => '(UTC-05:00) Nipigon'], + ['value' => 'America/Panama', 'key' => '(UTC-05:00) Panama'], + ['value' => 'America/Pangnirtung', 'key' => '(UTC-05:00) Pangnirtung'], + ['value' => 'America/Indiana/Petersburg', 'key' => '(UTC-05:00) Petersburg'], + ['value' => 'America/Port-au-Prince', 'key' => '(UTC-05:00) Port-au-Prince'], + ['value' => 'America/Thunder_Bay', 'key' => '(UTC-05:00) Thunder Bay'], + ['value' => 'America/Toronto', 'key' => '(UTC-05:00) Toronto'], + ['value' => 'America/Indiana/Vevay', 'key' => '(UTC-05:00) Vevay'], + ['value' => 'America/Indiana/Vincennes', 'key' => '(UTC-05:00) Vincennes'], + ['value' => 'America/Indiana/Winamac', 'key' => '(UTC-05:00) Winamac'], + ['value' => 'America/Caracas', 'key' => '(UTC-04:30) Caracas'], + ['value' => 'America/Anguilla', 'key' => '(UTC-04:00) Anguilla'], + ['value' => 'America/Antigua', 'key' => '(UTC-04:00) Antigua'], + ['value' => 'America/Aruba', 'key' => '(UTC-04:00) Aruba'], + ['value' => 'America/Asuncion', 'key' => '(UTC-04:00) Asuncion'], + ['value' => 'America/Barbados', 'key' => '(UTC-04:00) Barbados'], + ['value' => 'Atlantic/Bermuda', 'key' => '(UTC-04:00) Bermuda'], + ['value' => 'America/Blanc-Sablon', 'key' => '(UTC-04:00) Blanc-Sablon'], + ['value' => 'America/Boa_Vista', 'key' => '(UTC-04:00) Boa Vista'], + ['value' => 'America/Campo_Grande', 'key' => '(UTC-04:00) Campo Grande'], + ['value' => 'America/Cuiaba', 'key' => '(UTC-04:00) Cuiaba'], + ['value' => 'America/Curacao', 'key' => '(UTC-04:00) Curacao'], + ['value' => 'America/Dominica', 'key' => '(UTC-04:00) Dominica'], + ['value' => 'America/Eirunepe', 'key' => '(UTC-04:00) Eirunepe'], + ['value' => 'America/Glace_Bay', 'key' => '(UTC-04:00) Glace Bay'], + ['value' => 'America/Goose_Bay', 'key' => '(UTC-04:00) Goose Bay'], + ['value' => 'America/Grenada', 'key' => '(UTC-04:00) Grenada'], + ['value' => 'America/Guadeloupe', 'key' => '(UTC-04:00) Guadeloupe'], + ['value' => 'America/Guyana', 'key' => '(UTC-04:00) Guyana'], + ['value' => 'America/Halifax', 'key' => '(UTC-04:00) Halifax'], + ['value' => 'America/Kralendijk', 'key' => '(UTC-04:00) Kralendijk'], + ['value' => 'America/La_Paz', 'key' => '(UTC-04:00) La Paz'], + ['value' => 'America/Lower_Princes', 'key' => '(UTC-04:00) Lower Princes'], + ['value' => 'America/Manaus', 'key' => '(UTC-04:00) Manaus'], + ['value' => 'America/Marigot', 'key' => '(UTC-04:00) Marigot'], + ['value' => 'America/Martinique', 'key' => '(UTC-04:00) Martinique'], + ['value' => 'America/Moncton', 'key' => '(UTC-04:00) Moncton'], + ['value' => 'America/Montserrat', 'key' => '(UTC-04:00) Montserrat'], + ['value' => 'Antarctica/Palmer', 'key' => '(UTC-04:00) Palmer'], + ['value' => 'America/Port_of_Spain', 'key' => '(UTC-04:00) Port of Spain'], + ['value' => 'America/Porto_Velho', 'key' => '(UTC-04:00) Porto Velho'], + ['value' => 'America/Puerto_Rico', 'key' => '(UTC-04:00) Puerto Rico'], + ['value' => 'America/Rio_Branco', 'key' => '(UTC-04:00) Rio Branco'], + ['value' => 'America/Santiago', 'key' => '(UTC-04:00) Santiago'], + ['value' => 'America/Santo_Domingo', 'key' => '(UTC-04:00) Santo Domingo'], + ['value' => 'America/St_Barthelemy', 'key' => '(UTC-04:00) St. Barthelemy'], + ['value' => 'America/St_Kitts', 'key' => '(UTC-04:00) St. Kitts'], + ['value' => 'America/St_Lucia', 'key' => '(UTC-04:00) St. Lucia'], + ['value' => 'America/St_Thomas', 'key' => '(UTC-04:00) St. Thomas'], + ['value' => 'America/St_Vincent', 'key' => '(UTC-04:00) St. Vincent'], + ['value' => 'America/Thule', 'key' => '(UTC-04:00) Thule'], + ['value' => 'America/Tortola', 'key' => '(UTC-04:00) Tortola'], + ['value' => 'America/St_Johns', 'key' => '(UTC-03:30) St. Johns'], + ['value' => 'America/Araguaina', 'key' => '(UTC-03:00) Araguaina'], + ['value' => 'America/Bahia', 'key' => '(UTC-03:00) Bahia'], + ['value' => 'America/Belem', 'key' => '(UTC-03:00) Belem'], + ['value' => 'America/Argentina/Buenos_Aires', 'key' => '(UTC-03:00) Buenos Aires'], + ['value' => 'America/Argentina/Catamarca', 'key' => '(UTC-03:00) Catamarca'], + ['value' => 'America/Cayenne', 'key' => '(UTC-03:00) Cayenne'], + ['value' => 'America/Argentina/Cordoba', 'key' => '(UTC-03:00) Cordoba'], + ['value' => 'America/Fortaleza', 'key' => '(UTC-03:00) Fortaleza'], + ['value' => 'America/Godthab', 'key' => '(UTC-03:00) Godthab'], + ['value' => 'America/Argentina/Jujuy', 'key' => '(UTC-03:00) Jujuy'], + ['value' => 'America/Argentina/La_Rioja', 'key' => '(UTC-03:00) La Rioja'], + ['value' => 'America/Maceio', 'key' => '(UTC-03:00) Maceio'], + ['value' => 'America/Argentina/Mendoza', 'key' => '(UTC-03:00) Mendoza'], + ['value' => 'America/Miquelon', 'key' => '(UTC-03:00) Miquelon'], + ['value' => 'America/Montevideo', 'key' => '(UTC-03:00) Montevideo'], + ['value' => 'America/Paramaribo', 'key' => '(UTC-03:00) Paramaribo'], + ['value' => 'America/Recife', 'key' => '(UTC-03:00) Recife'], + ['value' => 'America/Argentina/Rio_Gallegos', 'key' => '(UTC-03:00) Rio Gallegos'], + ['value' => 'Antarctica/Rothera', 'key' => '(UTC-03:00) Rothera'], + ['value' => 'America/Argentina/Salta', 'key' => '(UTC-03:00) Salta'], + ['value' => 'America/Argentina/San_Juan', 'key' => '(UTC-03:00) San Juan'], + ['value' => 'America/Argentina/San_Luis', 'key' => '(UTC-03:00) San Luis'], + ['value' => 'America/Santarem', 'key' => '(UTC-03:00) Santarem'], + ['value' => 'America/Sao_Paulo', 'key' => '(UTC-03:00) Sao Paulo'], + ['value' => 'Atlantic/Stanley', 'key' => '(UTC-03:00) Stanley'], + ['value' => 'America/Argentina/Tucuman', 'key' => '(UTC-03:00) Tucuman'], + ['value' => 'America/Argentina/Ushuaia', 'key' => '(UTC-03:00) Ushuaia'], + ['value' => 'America/Noronha', 'key' => '(UTC-02:00) Noronha'], + ['value' => 'Atlantic/South_Georgia', 'key' => '(UTC-02:00) South Georgia'], + ['value' => 'Atlantic/Azores', 'key' => '(UTC-01:00) Azores'], + ['value' => 'Atlantic/Cape_Verde', 'key' => '(UTC-01:00) Cape Verde'], + ['value' => 'America/Scoresbysund', 'key' => '(UTC-01:00) Scoresbysund'], + ['value' => 'Africa/Abidjan', 'key' => '(UTC+00:00) Abidjan'], + ['value' => 'Africa/Accra', 'key' => '(UTC+00:00) Accra'], + ['value' => 'Africa/Bamako', 'key' => '(UTC+00:00) Bamako'], + ['value' => 'Africa/Banjul', 'key' => '(UTC+00:00) Banjul'], + ['value' => 'Africa/Bissau', 'key' => '(UTC+00:00) Bissau'], + ['value' => 'Atlantic/Canary', 'key' => '(UTC+00:00) Canary'], + ['value' => 'Africa/Casablanca', 'key' => '(UTC+00:00) Casablanca'], + ['value' => 'Africa/Conakry', 'key' => '(UTC+00:00) Conakry'], + ['value' => 'Africa/Dakar', 'key' => '(UTC+00:00) Dakar'], + ['value' => 'America/Danmarkshavn', 'key' => '(UTC+00:00) Danmarkshavn'], + ['value' => 'Europe/Dublin', 'key' => '(UTC+00:00) Dublin'], + ['value' => 'Africa/El_Aaiun', 'key' => '(UTC+00:00) El Aaiun'], + ['value' => 'Atlantic/Faroe', 'key' => '(UTC+00:00) Faroe'], + ['value' => 'Africa/Freetown', 'key' => '(UTC+00:00) Freetown'], + ['value' => 'Europe/Guernsey', 'key' => '(UTC+00:00) Guernsey'], + ['value' => 'Europe/Isle_of_Man', 'key' => '(UTC+00:00) Isle of Man'], + ['value' => 'Europe/Jersey', 'key' => '(UTC+00:00) Jersey'], + ['value' => 'Europe/Lisbon', 'key' => '(UTC+00:00) Lisbon'], + ['value' => 'Africa/Lome', 'key' => '(UTC+00:00) Lome'], + ['value' => 'Europe/London', 'key' => '(UTC+00:00) London'], + ['value' => 'Atlantic/Madeira', 'key' => '(UTC+00:00) Madeira'], + ['value' => 'Africa/Monrovia', 'key' => '(UTC+00:00) Monrovia'], + ['value' => 'Africa/Nouakchott', 'key' => '(UTC+00:00) Nouakchott'], + ['value' => 'Africa/Ouagadougou', 'key' => '(UTC+00:00) Ouagadougou'], + ['value' => 'Atlantic/Reykjavik', 'key' => '(UTC+00:00) Reykjavik'], + ['value' => 'Africa/Sao_Tome', 'key' => '(UTC+00:00) Sao Tome'], + ['value' => 'Atlantic/St_Helena', 'key' => '(UTC+00:00) St. Helena'], + ['value' => 'UTC', 'key' => '(UTC+00:00) UTC'], + ['value' => 'Africa/Algiers', 'key' => '(UTC+01:00) Algiers'], + ['value' => 'Europe/Amsterdam', 'key' => '(UTC+01:00) Amsterdam'], + ['value' => 'Europe/Andorra', 'key' => '(UTC+01:00) Andorra'], + ['value' => 'Africa/Bangui', 'key' => '(UTC+01:00) Bangui'], + ['value' => 'Europe/Belgrade', 'key' => '(UTC+01:00) Belgrade'], + ['value' => 'Europe/Berlin', 'key' => '(UTC+01:00) Berlin'], + ['value' => 'Europe/Bratislava', 'key' => '(UTC+01:00) Bratislava'], + ['value' => 'Africa/Brazzaville', 'key' => '(UTC+01:00) Brazzaville'], + ['value' => 'Europe/Brussels', 'key' => '(UTC+01:00) Brussels'], + ['value' => 'Europe/Budapest', 'key' => '(UTC+01:00) Budapest'], + ['value' => 'Europe/Busingen', 'key' => '(UTC+01:00) Busingen'], + ['value' => 'Africa/Ceuta', 'key' => '(UTC+01:00) Ceuta'], + ['value' => 'Europe/Copenhagen', 'key' => '(UTC+01:00) Copenhagen'], + ['value' => 'Africa/Douala', 'key' => '(UTC+01:00) Douala'], + ['value' => 'Europe/Gibraltar', 'key' => '(UTC+01:00) Gibraltar'], + ['value' => 'Africa/Kinshasa', 'key' => '(UTC+01:00) Kinshasa'], + ['value' => 'Africa/Lagos', 'key' => '(UTC+01:00) Lagos'], + ['value' => 'Africa/Libreville', 'key' => '(UTC+01:00) Libreville'], + ['value' => 'Europe/Ljubljana', 'key' => '(UTC+01:00) Ljubljana'], + ['value' => 'Arctic/Longyearbyen', 'key' => '(UTC+01:00) Longyearbyen'], + ['value' => 'Africa/Luanda', 'key' => '(UTC+01:00) Luanda'], + ['value' => 'Europe/Luxembourg', 'key' => '(UTC+01:00) Luxembourg'], + ['value' => 'Europe/Madrid', 'key' => '(UTC+01:00) Madrid'], + ['value' => 'Africa/Malabo', 'key' => '(UTC+01:00) Malabo'], + ['value' => 'Europe/Malta', 'key' => '(UTC+01:00) Malta'], + ['value' => 'Europe/Monaco', 'key' => '(UTC+01:00) Monaco'], + ['value' => 'Africa/Ndjamena', 'key' => '(UTC+01:00) Ndjamena'], + ['value' => 'Africa/Niamey', 'key' => '(UTC+01:00) Niamey'], + ['value' => 'Europe/Oslo', 'key' => '(UTC+01:00) Oslo'], + ['value' => 'Europe/Paris', 'key' => '(UTC+01:00) Paris'], + ['value' => 'Europe/Podgorica', 'key' => '(UTC+01:00) Podgorica'], + ['value' => 'Africa/Porto-Novo', 'key' => '(UTC+01:00) Porto-Novo'], + ['value' => 'Europe/Prague', 'key' => '(UTC+01:00) Prague'], + ['value' => 'Europe/Rome', 'key' => '(UTC+01:00) Rome'], + ['value' => 'Europe/San_Marino', 'key' => '(UTC+01:00) San Marino'], + ['value' => 'Europe/Sarajevo', 'key' => '(UTC+01:00) Sarajevo'], + ['value' => 'Europe/Skopje', 'key' => '(UTC+01:00) Skopje'], + ['value' => 'Europe/Stockholm', 'key' => '(UTC+01:00) Stockholm'], + ['value' => 'Europe/Tirane', 'key' => '(UTC+01:00) Tirane'], + ['value' => 'Africa/Tripoli', 'key' => '(UTC+01:00) Tripoli'], + ['value' => 'Africa/Tunis', 'key' => '(UTC+01:00) Tunis'], + ['value' => 'Europe/Vaduz', 'key' => '(UTC+01:00) Vaduz'], + ['value' => 'Europe/Vatican', 'key' => '(UTC+01:00) Vatican'], + ['value' => 'Europe/Vienna', 'key' => '(UTC+01:00) Vienna'], + ['value' => 'Europe/Warsaw', 'key' => '(UTC+01:00) Warsaw'], + ['value' => 'Africa/Windhoek', 'key' => '(UTC+01:00) Windhoek'], + ['value' => 'Europe/Zagreb', 'key' => '(UTC+01:00) Zagreb'], + ['value' => 'Europe/Zurich', 'key' => '(UTC+01:00) Zurich'], + ['value' => 'Europe/Athens', 'key' => '(UTC+02:00) Athens'], + ['value' => 'Asia/Beirut', 'key' => '(UTC+02:00) Beirut'], + ['value' => 'Africa/Blantyre', 'key' => '(UTC+02:00) Blantyre'], + ['value' => 'Europe/Bucharest', 'key' => '(UTC+02:00) Bucharest'], + ['value' => 'Africa/Bujumbura', 'key' => '(UTC+02:00) Bujumbura'], + ['value' => 'Africa/Cairo', 'key' => '(UTC+02:00) Cairo'], + ['value' => 'Europe/Chisinau', 'key' => '(UTC+02:00) Chisinau'], + ['value' => 'Asia/Damascus', 'key' => '(UTC+02:00) Damascus'], + ['value' => 'Africa/Gaborone', 'key' => '(UTC+02:00) Gaborone'], + ['value' => 'Asia/Gaza', 'key' => '(UTC+02:00) Gaza'], + ['value' => 'Africa/Harare', 'key' => '(UTC+02:00) Harare'], + ['value' => 'Asia/Hebron', 'key' => '(UTC+02:00) Hebron'], + ['value' => 'Europe/Helsinki', 'key' => '(UTC+02:00) Helsinki'], + ['value' => 'Europe/Istanbul', 'key' => '(UTC+02:00) Istanbul'], + ['value' => 'Asia/Jerusalem', 'key' => '(UTC+02:00) Jerusalem'], + ['value' => 'Africa/Johannesburg', 'key' => '(UTC+02:00) Johannesburg'], + ['value' => 'Europe/Kiev', 'key' => '(UTC+02:00) Kiev'], + ['value' => 'Africa/Kigali', 'key' => '(UTC+02:00) Kigali'], + ['value' => 'Africa/Lubumbashi', 'key' => '(UTC+02:00) Lubumbashi'], + ['value' => 'Africa/Lusaka', 'key' => '(UTC+02:00) Lusaka'], + ['value' => 'Africa/Maputo', 'key' => '(UTC+02:00) Maputo'], + ['value' => 'Europe/Mariehamn', 'key' => '(UTC+02:00) Mariehamn'], + ['value' => 'Africa/Maseru', 'key' => '(UTC+02:00) Maseru'], + ['value' => 'Africa/Mbabane', 'key' => '(UTC+02:00) Mbabane'], + ['value' => 'Asia/Nicosia', 'key' => '(UTC+02:00) Nicosia'], + ['value' => 'Europe/Riga', 'key' => '(UTC+02:00) Riga'], + ['value' => 'Europe/Simferopol', 'key' => '(UTC+02:00) Simferopol'], + ['value' => 'Europe/Sofia', 'key' => '(UTC+02:00) Sofia'], + ['value' => 'Europe/Tallinn', 'key' => '(UTC+02:00) Tallinn'], + ['value' => 'Europe/Uzhgorod', 'key' => '(UTC+02:00) Uzhgorod'], + ['value' => 'Europe/Vilnius', 'key' => '(UTC+02:00) Vilnius'], + ['value' => 'Europe/Zaporozhye', 'key' => '(UTC+02:00) Zaporozhye'], + ['value' => 'Africa/Addis_Ababa', 'key' => '(UTC+03:00) Addis Ababa'], + ['value' => 'Asia/Aden', 'key' => '(UTC+03:00) Aden'], + ['value' => 'Asia/Amman', 'key' => '(UTC+03:00) Amman'], + ['value' => 'Indian/Antananarivo', 'key' => '(UTC+03:00) Antananarivo'], + ['value' => 'Africa/Asmara', 'key' => '(UTC+03:00) Asmara'], + ['value' => 'Asia/Baghdad', 'key' => '(UTC+03:00) Baghdad'], + ['value' => 'Asia/Bahrain', 'key' => '(UTC+03:00) Bahrain'], + ['value' => 'Indian/Comoro', 'key' => '(UTC+03:00) Comoro'], + ['value' => 'Africa/Dar_es_Salaam', 'key' => '(UTC+03:00) Dar es Salaam'], + ['value' => 'Africa/Djibouti', 'key' => '(UTC+03:00) Djibouti'], + ['value' => 'Africa/Juba', 'key' => '(UTC+03:00) Juba'], + ['value' => 'Europe/Kaliningrad', 'key' => '(UTC+03:00) Kaliningrad'], + ['value' => 'Africa/Kampala', 'key' => '(UTC+03:00) Kampala'], + ['value' => 'Africa/Khartoum', 'key' => '(UTC+03:00) Khartoum'], + ['value' => 'Asia/Kuwait', 'key' => '(UTC+03:00) Kuwait'], + ['value' => 'Indian/Mayotte', 'key' => '(UTC+03:00) Mayotte'], + ['value' => 'Europe/Minsk', 'key' => '(UTC+03:00) Minsk'], + ['value' => 'Africa/Mogadishu', 'key' => '(UTC+03:00) Mogadishu'], + ['value' => 'Africa/Nairobi', 'key' => '(UTC+03:00) Nairobi'], + ['value' => 'Asia/Qatar', 'key' => '(UTC+03:00) Qatar'], + ['value' => 'Asia/Riyadh', 'key' => '(UTC+03:00) Riyadh'], + ['value' => 'Antarctica/Syowa', 'key' => '(UTC+03:00) Syowa'], + ['value' => 'Asia/Tehran', 'key' => '(UTC+03:30) Tehran'], + ['value' => 'Asia/Baku', 'key' => '(UTC+04:00) Baku'], + ['value' => 'Asia/Dubai', 'key' => '(UTC+04:00) Dubai'], + ['value' => 'Indian/Mahe', 'key' => '(UTC+04:00) Mahe'], + ['value' => 'Indian/Mauritius', 'key' => '(UTC+04:00) Mauritius'], + ['value' => 'Europe/Moscow', 'key' => '(UTC+04:00) Moscow'], + ['value' => 'Asia/Muscat', 'key' => '(UTC+04:00) Muscat'], + ['value' => 'Indian/Reunion', 'key' => '(UTC+04:00) Reunion'], + ['value' => 'Europe/Samara', 'key' => '(UTC+04:00) Samara'], + ['value' => 'Asia/Tbilisi', 'key' => '(UTC+04:00) Tbilisi'], + ['value' => 'Europe/Volgograd', 'key' => '(UTC+04:00) Volgograd'], + ['value' => 'Asia/Yerevan', 'key' => '(UTC+04:00) Yerevan'], + ['value' => 'Asia/Kabul', 'key' => '(UTC+04:30) Kabul'], + ['value' => 'Asia/Aqtau', 'key' => '(UTC+05:00) Aqtau'], + ['value' => 'Asia/Aqtobe', 'key' => '(UTC+05:00) Aqtobe'], + ['value' => 'Asia/Ashgabat', 'key' => '(UTC+05:00) Ashgabat'], + ['value' => 'Asia/Dushanbe', 'key' => '(UTC+05:00) Dushanbe'], + ['value' => 'Asia/Karachi', 'key' => '(UTC+05:00) Karachi'], + ['value' => 'Indian/Kerguelen', 'key' => '(UTC+05:00) Kerguelen'], + ['value' => 'Indian/Maldives', 'key' => '(UTC+05:00) Maldives'], + ['value' => 'Antarctica/Mawson', 'key' => '(UTC+05:00) Mawson'], + ['value' => 'Asia/Oral', 'key' => '(UTC+05:00) Oral'], + ['value' => 'Asia/Samarkand', 'key' => '(UTC+05:00) Samarkand'], + ['value' => 'Asia/Tashkent', 'key' => '(UTC+05:00) Tashkent'], + ['value' => 'Asia/Colombo', 'key' => '(UTC+05:30) Colombo'], + ['value' => 'Asia/Kolkata', 'key' => '(UTC+05:30) Kolkata'], + ['value' => 'Asia/Kathmandu', 'key' => '(UTC+05:45) Kathmandu'], + ['value' => 'Asia/Almaty', 'key' => '(UTC+06:00) Almaty'], + ['value' => 'Asia/Bishkek', 'key' => '(UTC+06:00) Bishkek'], + ['value' => 'Indian/Chagos', 'key' => '(UTC+06:00) Chagos'], + ['value' => 'Asia/Dhaka', 'key' => '(UTC+06:00) Dhaka'], + ['value' => 'Asia/Qyzylorda', 'key' => '(UTC+06:00) Qyzylorda'], + ['value' => 'Asia/Thimphu', 'key' => '(UTC+06:00) Thimphu'], + ['value' => 'Antarctica/Vostok', 'key' => '(UTC+06:00) Vostok'], + ['value' => 'Asia/Yekaterinburg', 'key' => '(UTC+06:00) Yekaterinburg'], + ['value' => 'Indian/Cocos', 'key' => '(UTC+06:30) Cocos'], + ['value' => 'Asia/Rangoon', 'key' => '(UTC+06:30) Rangoon'], + ['value' => 'Asia/Bangkok', 'key' => '(UTC+07:00) Bangkok'], + ['value' => 'Indian/Christmas', 'key' => '(UTC+07:00) Christmas'], + ['value' => 'Antarctica/Davis', 'key' => '(UTC+07:00) Davis'], + ['value' => 'Asia/Ho_Chi_Minh', 'key' => '(UTC+07:00) Ho Chi Minh'], + ['value' => 'Asia/Hovd', 'key' => '(UTC+07:00) Hovd'], + ['value' => 'Asia/Jakarta', 'key' => '(UTC+07:00) Jakarta'], + ['value' => 'Asia/Novokuznetsk', 'key' => '(UTC+07:00) Novokuznetsk'], + ['value' => 'Asia/Novosibirsk', 'key' => '(UTC+07:00) Novosibirsk'], + ['value' => 'Asia/Omsk', 'key' => '(UTC+07:00) Omsk'], + ['value' => 'Asia/Phnom_Penh', 'key' => '(UTC+07:00) Phnom Penh'], + ['value' => 'Asia/Pontianak', 'key' => '(UTC+07:00) Pontianak'], + ['value' => 'Asia/Vientiane', 'key' => '(UTC+07:00) Vientiane'], + ['value' => 'Asia/Brunei', 'key' => '(UTC+08:00) Brunei'], + ['value' => 'Antarctica/Casey', 'key' => '(UTC+08:00) Casey'], + ['value' => 'Asia/Choibalsan', 'key' => '(UTC+08:00) Choibalsan'], + ['value' => 'Asia/Chongqing', 'key' => '(UTC+08:00) Chongqing'], + ['value' => 'Asia/Harbin', 'key' => '(UTC+08:00) Harbin'], + ['value' => 'Asia/Hong_Kong', 'key' => '(UTC+08:00) Hong Kong'], + ['value' => 'Asia/Kashgar', 'key' => '(UTC+08:00) Kashgar'], + ['value' => 'Asia/Krasnoyarsk', 'key' => '(UTC+08:00) Krasnoyarsk'], + ['value' => 'Asia/Kuala_Lumpur', 'key' => '(UTC+08:00) Kuala Lumpur'], + ['value' => 'Asia/Kuching', 'key' => '(UTC+08:00) Kuching'], + ['value' => 'Asia/Macau', 'key' => '(UTC+08:00) Macau'], + ['value' => 'Asia/Makassar', 'key' => '(UTC+08:00) Makassar'], + ['value' => 'Asia/Manila', 'key' => '(UTC+08:00) Manila'], + ['value' => 'Australia/Perth', 'key' => '(UTC+08:00) Perth'], + ['value' => 'Asia/Shanghai', 'key' => '(UTC+08:00) Shanghai'], + ['value' => 'Asia/Singapore', 'key' => '(UTC+08:00) Singapore'], + ['value' => 'Asia/Taipei', 'key' => '(UTC+08:00) Taipei'], + ['value' => 'Asia/Ulaanbaatar', 'key' => '(UTC+08:00) Ulaanbaatar'], + ['value' => 'Asia/Urumqi', 'key' => '(UTC+08:00) Urumqi'], + ['value' => 'Australia/Eucla', 'key' => '(UTC+08:45) Eucla'], + ['value' => 'Asia/Dili', 'key' => '(UTC+09:00) Dili'], + ['value' => 'Asia/Irkutsk', 'key' => '(UTC+09:00) Irkutsk'], + ['value' => 'Asia/Jayapura', 'key' => '(UTC+09:00) Jayapura'], + ['value' => 'Pacific/Palau', 'key' => '(UTC+09:00) Palau'], + ['value' => 'Asia/Pyongyang', 'key' => '(UTC+09:00) Pyongyang'], + ['value' => 'Asia/Seoul', 'key' => '(UTC+09:00) Seoul'], + ['value' => 'Asia/Tokyo', 'key' => '(UTC+09:00) Tokyo'], + ['value' => 'Australia/Adelaide', 'key' => '(UTC+09:30) Adelaide'], + ['value' => 'Australia/Broken_Hill', 'key' => '(UTC+09:30) Broken Hill'], + ['value' => 'Australia/Darwin', 'key' => '(UTC+09:30) Darwin'], + ['value' => 'Australia/Brisbane', 'key' => '(UTC+10:00) Brisbane'], + ['value' => 'Pacific/Chuuk', 'key' => '(UTC+10:00) Chuuk'], + ['value' => 'Australia/Currie', 'key' => '(UTC+10:00) Currie'], + ['value' => 'Antarctica/DumontDUrville', 'key' => '(UTC+10:00) DumontDUrville'], + ['value' => 'Pacific/Guam', 'key' => '(UTC+10:00) Guam'], + ['value' => 'Australia/Hobart', 'key' => '(UTC+10:00) Hobart'], + ['value' => 'Asia/Khandyga', 'key' => '(UTC+10:00) Khandyga'], + ['value' => 'Australia/Lindeman', 'key' => '(UTC+10:00) Lindeman'], + ['value' => 'Australia/Melbourne', 'key' => '(UTC+10:00) Melbourne'], + ['value' => 'Pacific/Port_Moresby', 'key' => '(UTC+10:00) Port Moresby'], + ['value' => 'Pacific/Saipan', 'key' => '(UTC+10:00) Saipan'], + ['value' => 'Australia/Sydney', 'key' => '(UTC+10:00) Sydney'], + ['value' => 'Asia/Yakutsk', 'key' => '(UTC+10:00) Yakutsk'], + ['value' => 'Australia/Lord_Howe', 'key' => '(UTC+10:30) Lord Howe'], + ['value' => 'Pacific/Efate', 'key' => '(UTC+11:00) Efate'], + ['value' => 'Pacific/Guadalcanal', 'key' => '(UTC+11:00) Guadalcanal'], + ['value' => 'Pacific/Kosrae', 'key' => '(UTC+11:00) Kosrae'], + ['value' => 'Antarctica/Macquarie', 'key' => '(UTC+11:00) Macquarie'], + ['value' => 'Pacific/Noumea', 'key' => '(UTC+11:00) Noumea'], + ['value' => 'Pacific/Pohnpei', 'key' => '(UTC+11:00) Pohnpei'], + ['value' => 'Asia/Sakhalin', 'key' => '(UTC+11:00) Sakhalin'], + ['value' => 'Asia/Ust-Nera', 'key' => '(UTC+11:00) Ust-Nera'], + ['value' => 'Asia/Vladivostok', 'key' => '(UTC+11:00) Vladivostok'], + ['value' => 'Pacific/Norfolk', 'key' => '(UTC+11:30) Norfolk'], + ['value' => 'Asia/Anadyr', 'key' => '(UTC+12:00) Anadyr'], + ['value' => 'Pacific/Auckland', 'key' => '(UTC+12:00) Auckland'], + ['value' => 'Pacific/Fiji', 'key' => '(UTC+12:00) Fiji'], + ['value' => 'Pacific/Funafuti', 'key' => '(UTC+12:00) Funafuti'], + ['value' => 'Asia/Kamchatka', 'key' => '(UTC+12:00) Kamchatka'], + ['value' => 'Pacific/Kwajalein', 'key' => '(UTC+12:00) Kwajalein'], + ['value' => 'Asia/Magadan', 'key' => '(UTC+12:00) Magadan'], + ['value' => 'Pacific/Majuro', 'key' => '(UTC+12:00) Majuro'], + ['value' => 'Antarctica/McMurdo', 'key' => '(UTC+12:00) McMurdo'], + ['value' => 'Pacific/Nauru', 'key' => '(UTC+12:00) Nauru'], + ['value' => 'Antarctica/South_Pole', 'key' => '(UTC+12:00) South Pole'], + ['value' => 'Pacific/Tarawa', 'key' => '(UTC+12:00) Tarawa'], + ['value' => 'Pacific/Wake', 'key' => '(UTC+12:00) Wake'], + ['value' => 'Pacific/Wallis', 'key' => '(UTC+12:00) Wallis'], + ['value' => 'Pacific/Chatham', 'key' => '(UTC+12:45) Chatham'], + ['value' => 'Pacific/Apia', 'key' => '(UTC+13:00) Apia'], + ['value' => 'Pacific/Enderbury', 'key' => '(UTC+13:00) Enderbury'], + ['value' => 'Pacific/Fakaofo', 'key' => '(UTC+13:00) Fakaofo'], + ['value' => 'Pacific/Tongatapu', 'key' => '(UTC+13:00) Tongatapu'], + ['value' => 'Pacific/Kiritimati', 'key' => '(UTC+14:00) Kiritimati'], + ]; + } +} diff --git a/crater/app/Space/Updater.php b/crater/app/Space/Updater.php new file mode 100644 index 0000000..f14b084 --- /dev/null +++ b/crater/app/Space/Updater.php @@ -0,0 +1,159 @@ + 100, 'track_redirects' => true]); + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + $data = json_decode($data); + + if ($data->success && $data->version && property_exists($data->version, 'extensions')) { + $extensions = $data->version->extensions; + $extensionData = []; + foreach (json_decode($extensions) as $extension) { + $extensionData[$extension] = phpversion($extension) ? true : false; + } + $extensionData['php'.'('.$data->version->minimum_php_version.')'] = version_compare(phpversion(), $data->version->minimum_php_version, ">="); + $data->version->extensions = $extensionData; + } + + return $data; + } + + public static function download($new_version, $is_cmd = 0) + { + $data = null; + $path = null; + + if (env('APP_ENV') === 'development') { + $url = 'downloads/file/'.$new_version.'?type=update&is_dev=1&is_cmd='.$is_cmd; + } else { + $url = 'downloads/file/'.$new_version.'?type=update&is_cmd='.$is_cmd; + } + + $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true]); + + // Exception + if ($response instanceof RequestException) { + return [ + 'success' => false, + 'error' => 'Download Exception', + 'data' => [ + 'path' => $path, + ], + ]; + } + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + // Create temp directory + $temp_dir = storage_path('app/temp-'.md5(mt_rand())); + + if (! File::isDirectory($temp_dir)) { + File::makeDirectory($temp_dir); + } + + $zip_file_path = $temp_dir.'/upload.zip'; + + // Add content to the Zip file + $uploaded = is_int(file_put_contents($zip_file_path, $data)) ? true : false; + + if (! $uploaded) { + return false; + } + + return $zip_file_path; + } + + public static function unzip($zip_file_path) + { + if (! file_exists($zip_file_path)) { + throw new \Exception('Zip file not found'); + } + + $temp_extract_dir = storage_path('app/temp2-'.md5(mt_rand())); + + if (! File::isDirectory($temp_extract_dir)) { + File::makeDirectory($temp_extract_dir); + } + // Unzip the file + $zip = new ZipArchive(); + + if ($zip->open($zip_file_path)) { + $zip->extractTo($temp_extract_dir); + } + + $zip->close(); + + // Delete zip file + File::delete($zip_file_path); + + return $temp_extract_dir; + } + + public static function copyFiles($temp_extract_dir) + { + if (! File::copyDirectory($temp_extract_dir.'/Crater', base_path())) { + return false; + } + + // Delete temp directory + File::deleteDirectory($temp_extract_dir); + + return true; + } + + public static function deleteFiles($json) + { + $files = json_decode($json); + + foreach ($files as $file) { + \File::delete(base_path($file)); + } + + return true; + } + + public static function migrateUpdate() + { + Artisan::call('migrate --force'); + + return true; + } + + public static function finishUpdate($installed, $version) + { + event(new UpdateFinished($installed, $version)); + + return [ + 'success' => true, + 'error' => false, + 'data' => [], + ]; + } +} diff --git a/crater/app/Space/helpers.php b/crater/app/Space/helpers.php new file mode 100644 index 0000000..8e14fe0 --- /dev/null +++ b/crater/app/Space/helpers.php @@ -0,0 +1,196 @@ +has('database_created')) { + return CompanySetting::getSetting($key, $company_id); + } +} + +/** + * Get app setting + * + * @param $company_id + * @return string + */ +function get_app_setting($key) +{ + if (\Storage::disk('local')->has('database_created')) { + return Setting::getSetting($key); + } +} + +/** + * Get page title + * + * @param $company_id + * @return string + */ +function get_page_title($company_id) +{ + $routeName = Route::currentRouteName(); + + $pageTitle = null; + $defaultPageTitle = 'Crater - Self Hosted Invoicing Platform'; + + if (\Storage::disk('local')->has('database_created')) { + if ($routeName === 'customer.dashboard') { + $pageTitle = CompanySetting::getSetting('customer_portal_page_title', $company_id); + + return $pageTitle ? $pageTitle : $defaultPageTitle; + } + + $pageTitle = Setting::getSetting('admin_page_title'); + + return $pageTitle ? $pageTitle : $defaultPageTitle; + } +} + +/** + * Set Active Path + * + * @param $path + * @param string $active + * @return string + */ +function set_active($path, $active = 'active') +{ + return call_user_func_array('Request::is', (array)$path) ? $active : ''; +} + +/** + * @param $path + * @return mixed + */ +function is_url($path) +{ + return call_user_func_array('Request::is', (array)$path); +} + +/** + * @param string $type + * @return string + */ +function getCustomFieldValueKey(string $type) +{ + switch ($type) { + case 'Input': + return 'string_answer'; + + case 'TextArea': + return 'string_answer'; + + case 'Phone': + return 'number_answer'; + + case 'Url': + return 'string_answer'; + + case 'Number': + return 'number_answer'; + + case 'Dropdown': + return 'string_answer'; + + case 'Switch': + return 'boolean_answer'; + + case 'Date': + return 'date_answer'; + + case 'Time': + return 'time_answer'; + + case 'DateTime': + return 'date_time_answer'; + + default: + return 'string_answer'; + } +} + +/** + * @param $money + * @return formated_money + */ +function format_money_pdf($money, $currency = null) +{ + $money = $money / 100; + + if (! $currency) { + $currency = Currency::findOrFail(CompanySetting::getSetting('currency', 1)); + } + + $format_money = number_format( + $money, + $currency->precision, + $currency->decimal_separator, + $currency->thousand_separator + ); + + $currency_with_symbol = ''; + if ($currency->swap_currency_symbol) { + $currency_with_symbol = $format_money.''.$currency->symbol.''; + } else { + $currency_with_symbol = ''.$currency->symbol.''.$format_money; + } + + return $currency_with_symbol; +} + +/** + * @param $string + * @return string + */ +function clean_slug($model, $title, $id = 0) +{ + // Normalize the title + $slug = Str::upper('CUSTOM_'.$model.'_'.Str::slug($title, '_')); + + // Get any that could possibly be related. + // This cuts the queries down by doing it once. + $allSlugs = getRelatedSlugs($model, $slug, $id); + + // If we haven't used it before then we are all good. + if (! $allSlugs->contains('slug', $slug)) { + return $slug; + } + + // Just append numbers like a savage until we find not used. + for ($i = 1; $i <= 10; $i++) { + $newSlug = $slug.'_'.$i; + if (! $allSlugs->contains('slug', $newSlug)) { + return $newSlug; + } + } + + throw new \Exception('Can not create a unique slug'); +} + +function getRelatedSlugs($type, $slug, $id = 0) +{ + return CustomField::select('slug')->where('slug', 'like', $slug.'%') + ->where('model_type', $type) + ->where('id', '<>', $id) + ->get(); +} + +function respondJson($error, $message) +{ + return response()->json([ + 'error' => $error, + 'message' => $message + ], 422); +} diff --git a/crater/app/Traits/ExchangeRateProvidersTrait.php b/crater/app/Traits/ExchangeRateProvidersTrait.php new file mode 100644 index 0000000..bd493d7 --- /dev/null +++ b/crater/app/Traits/ExchangeRateProvidersTrait.php @@ -0,0 +1,213 @@ +json(); + + if (array_key_exists('success', $response)) { + if ($response["success"] == false) { + return respondJson($response["error"]["message"], $response["error"]["message"]); + } + } + + return response()->json([ + 'exchangeRate' => array_values($response["rates"]), + ], 200); + + break; + + case 'currency_layer': + $url = "http://api.currencylayer.com/live?access_key=".$filter['key']."&source={$baseCurrencyCode}¤cies={$currencyCode}"; + $response = Http::get($url)->json(); + + if (array_key_exists('success', $response)) { + if ($response["success"] == false) { + return respondJson($response["error"]["info"], $response["error"]["info"]); + } + } + + return response()->json([ + 'exchangeRate' => array_values($response['quotes']), + ], 200); + + break; + + case 'open_exchange_rate': + $url = "https://openexchangerates.org/api/latest.json?app_id=".$filter['key']."&base={$baseCurrencyCode}&symbols={$currencyCode}"; + $response = Http::get($url)->json(); + + if (array_key_exists("error", $response)) { + return respondJson($response["message"], $response["description"]); + } + + return response()->json([ + 'exchangeRate' => array_values($response["rates"]), + ], 200); + + break; + + case 'currency_converter': + $url = $this->getCurrencyConverterUrl($filter['driver_config']); + $url = $url."/api/v7/convert?apiKey=".$filter['key']; + + $query = "{$baseCurrencyCode}_{$currencyCode}"; + $url = $url."&q={$query}"."&compact=y"; + $response = Http::get($url)->json(); + + return response()->json([ + 'exchangeRate' => array_values($response[$query]), + ], 200); + + break; + } + } + + public function getCurrencyConverterUrl($data) + { + switch ($data['type']) { + case 'PREMIUM': + return "https://api.currconv.com"; + + break; + + case 'PREPAID': + return "https://prepaid.currconv.com"; + + break; + + case 'FREE': + return "https://free.currconv.com"; + + break; + + case 'DEDICATED': + return $data['url']; + + break; + } + } + + public function getSupportedCurrencies($request) + { + $message = 'Please Enter Valid Provider Key.'; + $error = 'invalid_key'; + + $server_message = 'Server not responding'; + $error_message = 'server_error'; + + switch ($request->driver) { + case 'currency_freak': + $url = "https://api.currencyfreaks.com/currency-symbols"; + $response = Http::get($url)->json(); + $checkKey = $this->getUrl($request); + + if ($response == null || $checkKey == null) { + return respondJson($error_message, $server_message); + } + + if (array_key_exists('success', $checkKey) && array_key_exists('error', $checkKey)) { + if ($checkKey['error']['status'] == 404) { + return respondJson($error, $message); + } + } + + return response()->json(['supportedCurrencies' => array_keys($response)]); + + break; + + case 'currency_layer': + $url = "http://api.currencylayer.com/list?access_key=".$request->key; + $response = Http::get($url)->json(); + + if ($response == null) { + return respondJson($error_message, $server_message); + } + + if (array_key_exists('currencies', $response)) { + return response()->json(['supportedCurrencies' => array_keys($response['currencies'])]); + } + + return respondJson($error, $message); + + break; + + case 'open_exchange_rate': + $url = "https://openexchangerates.org/api/currencies.json"; + $response = Http::get($url)->json(); + $checkKey = $this->getUrl($request); + + if ($response == null || $checkKey == null) { + return respondJson($error_message, $server_message); + } + + if (array_key_exists('error', $checkKey)) { + if ($checkKey['status'] == 401) { + return respondJson($error, $message); + } + } + + return response()->json(['supportedCurrencies' => array_keys($response)]); + + break; + + case 'currency_converter': + $response = $this->getUrl($request); + + if ($response == null) { + return respondJson($error_message, $server_message); + } + + if (array_key_exists('results', $response)) { + return response()->json(['supportedCurrencies' => array_keys($response['results'])]); + } + + return respondJson($error, $message); + + break; + } + } + + public function getUrl($request) + { + switch ($request->driver) { + case 'currency_freak': + $url = "https://api.currencyfreaks.com/latest?apikey=".$request->key."&symbols=INR&base=USD"; + + return Http::get($url)->json(); + + break; + + case 'currency_layer': + $url = "http://api.currencylayer.com/live?access_key=".$request->key."&source=INR¤cies=USD"; + + return Http::get($url)->json(); + + break; + + case 'open_exchange_rate': + $url = "https://openexchangerates.org/api/latest.json?app_id=".$request->key."&base=INR&symbols=USD"; + + return Http::get($url)->json(); + + break; + + case 'currency_converter': + $url = $this->getCurrencyConverterUrl($request)."/api/v7/currencies?apiKey=".$request->key; + + return Http::get($url)->json(); + + break; + } + } +} diff --git a/crater/app/Traits/GeneratesMenuTrait.php b/crater/app/Traits/GeneratesMenuTrait.php new file mode 100644 index 0000000..b3c4f1d --- /dev/null +++ b/crater/app/Traits/GeneratesMenuTrait.php @@ -0,0 +1,25 @@ +items->toArray() as $data) { + if ($user->checkAccess($data)) { + $menu[] = [ + 'title' => $data->title, + 'link' => $data->link->path['url'], + 'icon' => $data->data['icon'], + 'name' => $data->data['name'], + 'group' => $data->data['group'], + ]; + } + } + + return $menu; + } +} diff --git a/crater/app/Traits/GeneratesPdfTrait.php b/crater/app/Traits/GeneratesPdfTrait.php new file mode 100644 index 0000000..6d26829 --- /dev/null +++ b/crater/app/Traits/GeneratesPdfTrait.php @@ -0,0 +1,184 @@ +getGeneratedPDF($collection_name); + if ($pdf && file_exists($pdf['path'])) { + return response()->make(file_get_contents($pdf['path']), 200, [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="'.$pdf['file_name'].'"', + ]); + } + + $locale = CompanySetting::getSetting('language', $this->company_id); + + App::setLocale($locale); + + $pdf = $this->getPDFData(); + + return response()->make($pdf->stream(), 200, [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="'.$this[$collection_name.'_number'].'.pdf"', + ]); + } + + public function getGeneratedPDF($collection_name) + { + try { + $media = $this->getMedia($collection_name)->first(); + + if ($media) { + $file_disk = FileDisk::find($media->custom_properties['file_disk_id']); + + if (! $file_disk) { + return false; + } + + $file_disk->setConfig(); + + $path = null; + + if ($file_disk->driver == 'local') { + $path = $media->getPath(); + } else { + $path = $media->getTemporaryUrl(Carbon::now()->addMinutes(5)); + } + + return collect([ + 'path' => $path, + 'file_name' => $media->file_name, + ]); + } + } catch (\Exception $e) { + return false; + } + + return false; + } + + public function generatePDF($collection_name, $file_name, $deleteExistingFile = false) + { + $save_pdf_to_disk = CompanySetting::getSetting('save_pdf_to_disk', $this->company_id); + + if ($save_pdf_to_disk == 'NO') { + return 0; + } + + $locale = CompanySetting::getSetting('language', $this->company_id); + + App::setLocale($locale); + + $pdf = $this->getPDFData(); + + \Storage::disk('local')->put('temp/'.$collection_name.'/'.$this->id.'/temp.pdf', $pdf->output()); + + if ($deleteExistingFile) { + $this->clearMediaCollection($this->id); + } + + $file_disk = FileDisk::whereSetAsDefault(true)->first(); + + if ($file_disk) { + $file_disk->setConfig(); + } + + $media = \Storage::disk('local')->path('temp/'.$collection_name.'/'.$this->id.'/temp.pdf'); + + try { + $this->addMedia($media) + ->withCustomProperties(['file_disk_id' => $file_disk->id]) + ->usingFileName($file_name.'.pdf') + ->toMediaCollection($collection_name, config('filesystems.default')); + + \Storage::disk('local')->deleteDirectory('temp/'.$collection_name.'/'.$this->id); + + return true; + } catch (\Exception $e) { + return $e->getMessage(); + } + } + + public function getFieldsArray() + { + $customer = $this->customer; + $shippingAddress = $customer->shippingAddress ?? new Address(); + $billingAddress = $customer->billingAddress ?? new Address(); + $companyAddress = $this->company->address ?? new Address(); + + $fields = [ + '{SHIPPING_ADDRESS_NAME}' => $shippingAddress->name, + '{SHIPPING_COUNTRY}' => $shippingAddress->country_name, + '{SHIPPING_STATE}' => $shippingAddress->state, + '{SHIPPING_CITY}' => $shippingAddress->city, + '{SHIPPING_ADDRESS_STREET_1}' => $shippingAddress->address_street_1, + '{SHIPPING_ADDRESS_STREET_2}' => $shippingAddress->address_street_2, + '{SHIPPING_PHONE}' => $shippingAddress->phone, + '{SHIPPING_ZIP_CODE}' => $shippingAddress->zip, + '{BILLING_ADDRESS_NAME}' => $billingAddress->name, + '{BILLING_COUNTRY}' => $billingAddress->country_name, + '{BILLING_STATE}' => $billingAddress->state, + '{BILLING_CITY}' => $billingAddress->city, + '{BILLING_ADDRESS_STREET_1}' => $billingAddress->address_street_1, + '{BILLING_ADDRESS_STREET_2}' => $billingAddress->address_street_2, + '{BILLING_PHONE}' => $billingAddress->phone, + '{BILLING_ZIP_CODE}' => $billingAddress->zip, + '{COMPANY_NAME}' => $this->company->name, + '{COMPANY_COUNTRY}' => $companyAddress->country_name, + '{COMPANY_STATE}' => $companyAddress->state, + '{COMPANY_CITY}' => $companyAddress->city, + '{COMPANY_ADDRESS_STREET_1}' => $companyAddress->address_street_1, + '{COMPANY_ADDRESS_STREET_2}' => $companyAddress->address_street_2, + '{COMPANY_PHONE}' => $companyAddress->phone, + '{COMPANY_ZIP_CODE}' => $companyAddress->zip, + '{CONTACT_DISPLAY_NAME}' => $customer->name, + '{PRIMARY_CONTACT_NAME}' => $customer->contact_name, + '{CONTACT_EMAIL}' => $customer->email, + '{CONTACT_PHONE}' => $customer->phone, + '{CONTACT_WEBSITE}' => $customer->website, + ]; + + $customFields = $this->fields; + $customerCustomFields = $this->customer->fields; + + foreach ($customFields as $customField) { + $fields['{'.$customField->customField->slug.'}'] = $customField->defaultAnswer; + } + + foreach ($customerCustomFields as $customField) { + $fields['{'.$customField->customField->slug.'}'] = $customField->defaultAnswer; + } + + foreach ($fields as $key => $field) { + $fields[$key] = htmlspecialchars($field, ENT_QUOTES, 'UTF-8'); + } + + return $fields; + } + + public function getFormattedString($format) + { + $values = array_merge($this->getFieldsArray(), $this->getExtraFields()); + + $str = nl2br(strtr($format, $values)); + + $str = preg_replace('/{(.*?)}/', '', $str); + + $str = preg_replace("/<[^\/>]*>([\s]?)*<\/[^>]*>/", '', $str); + + $str = str_replace("

", "", $str); + + $str = str_replace("

", "
", $str); + + return $str; + } +} diff --git a/crater/app/Traits/HasCustomFieldsTrait.php b/crater/app/Traits/HasCustomFieldsTrait.php new file mode 100644 index 0000000..d24d21d --- /dev/null +++ b/crater/app/Traits/HasCustomFieldsTrait.php @@ -0,0 +1,81 @@ +morphMany('Crater\Models\CustomFieldValue', 'custom_field_valuable'); + } + + protected static function booted() + { + static::deleting(function ($data) { + if ($data->fields()->exists()) { + $data->fields()->delete(); + } + }); + } + + public function addCustomFields($customFields) + { + foreach ($customFields as $field) { + if (! is_array($field)) { + $field = (array)$field; + } + $customField = CustomField::find($field['id']); + + $customFieldValue = [ + 'type' => $customField->type, + 'custom_field_id' => $customField->id, + 'company_id' => $customField->company_id, + getCustomFieldValueKey($customField->type) => $field['value'], + ]; + + $this->fields()->create($customFieldValue); + } + } + + public function updateCustomFields($customFields) + { + foreach ($customFields as $field) { + if (! is_array($field)) { + $field = (array)$field; + } + + $customField = CustomField::find($field['id']); + $customFieldValue = $this->fields()->firstOrCreate([ + 'custom_field_id' => $customField->id, + 'type' => $customField->type, + 'company_id' => $this->company_id, + ]); + + $type = getCustomFieldValueKey($customField->type); + $customFieldValue->$type = $field['value']; + $customFieldValue->save(); + } + } + + public function getCustomFieldBySlug($slug) + { + return $this->fields() + ->with('customField') + ->whereHas('customField', function ($query) use ($slug) { + $query->where('slug', $slug); + })->first(); + } + + public function getCustomFieldValueBySlug($slug) + { + $value = $this->getCustomFieldBySlug($slug); + + if ($value) { + return $value->defaultAnswer; + } + + return null; + } +} diff --git a/crater/artisan b/crater/artisan new file mode 100644 index 0000000..5c23e2e --- /dev/null +++ b/crater/artisan @@ -0,0 +1,53 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/crater/bootstrap/app.php b/crater/bootstrap/app.php new file mode 100644 index 0000000..fe20b69 --- /dev/null +++ b/crater/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + Illuminate\Contracts\Http\Kernel::class, + Crater\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + Crater\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + Crater\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/crater/bootstrap/cache/.gitignore b/crater/bootstrap/cache/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/crater/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/crater/composer.json b/crater/composer.json new file mode 100644 index 0000000..e8dbec7 --- /dev/null +++ b/crater/composer.json @@ -0,0 +1,94 @@ +{ + "name": "crater-invoice/crater", + "description": "Free & Open Source Invoice App for Individuals & Small Businesses. https://craterapp.com", + "keywords": [ + "framework", + "laravel" + ], + "license": "MIT", + "type": "project", + "require": { + "php": "^7.4 || ^8.0", + "aws/aws-sdk-php": "^3.142", + "barryvdh/laravel-dompdf": "^0.9.0", + "crater-invoice/modules": "^1.0.0", + "doctrine/dbal": "^2.10", + "dragonmantank/cron-expression": "^3.1", + "fideloper/proxy": "^4.0", + "fruitcake/laravel-cors": "^1.0", + "guzzlehttp/guzzle": "^7.0.1", + "innocenzi/laravel-vite": "^0.1.1", + "intervention/image": "^2.3", + "jasonmccreary/laravel-test-assertions": "^2.0", + "laravel/framework": "^8.0", + "laravel/helpers": "^1.1", + "laravel/sanctum": "^2.6", + "laravel/tinker": "^2.0", + "laravel/ui": "^3.0", + "lavary/laravel-menu": "^1.8", + "league/flysystem-aws-s3-v3": "^1.0", + "predis/predis": "^1.1", + "silber/bouncer": "v1.0.0-rc.10", + "spatie/flysystem-dropbox": "^1.2", + "spatie/laravel-backup": "^6.11", + "spatie/laravel-medialibrary": "^8.7", + "vinkla/hashids": "^9.0" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "^2.6", + "beyondcode/laravel-dump-server": "^1.0", + "facade/ignition": "^2.3.6", + "friendsofphp/php-cs-fixer": "^3.8", + "fakerphp/faker": "^1.9.1", + "mockery/mockery": "^1.3.1", + "nunomaduro/collision": "^5.0", + "pestphp/pest": "^1.0", + "pestphp/pest-plugin-faker": "^1.0", + "pestphp/pest-plugin-laravel": "^1.0", + "pestphp/pest-plugin-parallel": "^0.2.1", + "phpunit/phpunit": "^9.3" + }, + "autoload": { + "psr-4": { + "Crater\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/", + "Modules\\": "Modules/" + }, + "files": [ + "app/Space/helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "scripts": { + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + } +} diff --git a/crater/composer.lock b/crater/composer.lock new file mode 100644 index 0000000..a398fda --- /dev/null +++ b/crater/composer.lock @@ -0,0 +1,11860 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "9e3c5088770d95d7d258550fe2c0f07d", + "packages": [ + { + "name": "asm89/stack-cors", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/asm89/stack-cors.git", + "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/asm89/stack-cors/zipball/b9c31def6a83f84b4d4a40d35996d375755f0e08", + "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/http-foundation": "~2.7|~3.0|~4.0|~5.0", + "symfony/http-kernel": "~2.7|~3.0|~4.0|~5.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8.10", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Asm89\\Stack\\": "src/Asm89/Stack/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander", + "email": "iam.asm89@gmail.com" + } + ], + "description": "Cross-origin resource sharing library and stack middleware", + "homepage": "https://github.com/asm89/stack-cors", + "keywords": [ + "cors", + "stack" + ], + "support": { + "issues": "https://github.com/asm89/stack-cors/issues", + "source": "https://github.com/asm89/stack-cors/tree/1.3.0" + }, + "time": "2019-12-24T22:41:47+00:00" + }, + { + "name": "aws/aws-crt-php", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "3942776a8c99209908ee0b287746263725685732" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/3942776a8c99209908ee0b287746263725685732", + "reference": "3942776a8c99209908ee0b287746263725685732", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.4.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.0.2" + }, + "time": "2021-09-03T22:57:30+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.225.1", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "b795c9c14997dac771f66d1f6cbadb62c742373a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b795c9c14997dac771f66d1f6cbadb62c742373a", + "reference": "b795c9c14997dac771f66d1f6cbadb62c742373a", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.0.2", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3 || ^6.2.1 || ^7.0", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.7.0 || ^2.1.1", + "mtdowling/jmespath.php": "^2.6", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^4.8.35 || ^5.6.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.225.1" + }, + "time": "2022-06-09T18:19:43+00:00" + }, + { + "name": "barryvdh/laravel-dompdf", + "version": "v0.9.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "5b99e1f94157d74e450f4c97e8444fcaffa2144b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/5b99e1f94157d74e450f4c97e8444fcaffa2144b", + "reference": "5b99e1f94157d74e450f4c97e8444fcaffa2144b", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^1", + "illuminate/support": "^5.5|^6|^7|^8", + "php": "^7.1 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ], + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade" + } + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v0.9.0" + }, + "funding": [ + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2020-12-27T12:05:53+00:00" + }, + { + "name": "brick/math", + "version": "0.9.3", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", + "vimeo/psalm": "4.9.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.9.3" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/brick/math", + "type": "tidelift" + } + ], + "time": "2021-08-15T20:50:18+00:00" + }, + { + "name": "crater-invoice/modules", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/crater-invoice/modules.git", + "reference": "996f80cb279416ef7da5a32f6e119ff9ce703591" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/crater-invoice/modules/zipball/996f80cb279416ef7da5a32f6e119ff9ce703591", + "reference": "996f80cb279416ef7da5a32f6e119ff9ce703591", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "laravel/framework": "^8.0", + "mockery/mockery": "~1.0", + "orchestra/testbench": "^6.2", + "phpstan/phpstan": "^0.12.14", + "phpunit/phpunit": "^8.5", + "spatie/phpunit-snapshot-assertions": "^2.1.0|^4.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Nwidart\\Modules\\LaravelModulesServiceProvider" + ], + "aliases": { + "Module": "Nwidart\\Modules\\Facades\\Module" + } + }, + "branch-alias": { + "dev-master": "8.0-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Nwidart\\Modules\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Crater Module Management Package", + "keywords": [ + "crater", + "laravel", + "module", + "modules", + "rad" + ], + "support": { + "issues": "https://github.com/crater-invoice/modules/issues", + "source": "https://github.com/crater-invoice/modules/tree/1.0.0" + }, + "time": "2021-12-21T14:18:56+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "0992cc19268b259a39e86f296da5f0677841f42c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c", + "reference": "0992cc19268b259a39e86f296da5f0677841f42c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^3.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1" + }, + "time": "2021-08-13T13:06:58+00:00" + }, + { + "name": "doctrine/cache", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "shasum": "" + }, + "require": { + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", + "keywords": [ + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" + ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" + }, + { + "name": "doctrine/dbal", + "version": "2.13.9", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "c480849ca3ad6706a39c970cdfe6888fa8a058b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/c480849ca3ad6706a39c970cdfe6888fa8a058b8", + "reference": "c480849ca3ad6706a39c970cdfe6888fa8a058b8", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.0|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1.0", + "ext-pdo": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "doctrine/coding-standard": "9.0.0", + "jetbrains/phpstorm-stubs": "2021.1", + "phpstan/phpstan": "1.4.6", + "phpunit/phpunit": "^7.5.20|^8.5|9.5.16", + "psalm/plugin-phpunit": "0.16.1", + "squizlabs/php_codesniffer": "3.6.2", + "symfony/cache": "^4.4", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", + "vimeo/psalm": "4.22.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlanywhere", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/2.13.9" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2022-05-02T20:28:55+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2020-05-29T18:28:51+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:16:43+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-03-03T08:28:38+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-02-28T11:07:21+00:00" + }, + { + "name": "dompdf/dompdf", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "5031045d9640b38cfc14aac9667470df09c9e090" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/5031045d9640b38cfc14aac9667470df09c9e090", + "reference": "5031045d9640b38cfc14aac9667470df09c9e090", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "phenx/php-font-lib": "^0.5.4", + "phenx/php-svg-lib": "^0.3.3 || ^0.4.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + }, + { + "name": "Brian Sweeney", + "email": "eclecticgeek@gmail.com" + }, + { + "name": "Gabriel Bull", + "email": "me@gabrielbull.com" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v1.2.2" + }, + "time": "2022-04-27T13:50:54+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-01-18T15:43:28+00:00" + }, + { + "name": "egulias/email-validator", + "version": "2.1.25", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^1.0.1", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.10" + }, + "require-dev": { + "dominicsayers/isemail": "^3.0.7", + "phpunit/phpunit": "^4.8.36|^7.5.15", + "satooshi/php-coveralls": "^1.0.1" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2020-12-29T14:50:06+00:00" + }, + { + "name": "facade/ignition-contracts", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", + "role": "Developer" + } + ], + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", + "keywords": [ + "contracts", + "flare", + "ignition" + ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, + "time": "2020-10-16T08:27:54+00:00" + }, + { + "name": "fideloper/proxy", + "version": "4.4.1", + "source": { + "type": "git", + "url": "https://github.com/fideloper/TrustedProxy.git", + "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/c073b2bd04d1c90e04dc1b787662b558dd65ade0", + "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0", + "php": ">=5.4.0" + }, + "require-dev": { + "illuminate/http": "^5.0|^6.0|^7.0|^8.0|^9.0", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Fideloper\\Proxy\\TrustedProxyServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Fideloper\\Proxy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Fidao", + "email": "fideloper@gmail.com" + } + ], + "description": "Set trusted proxies for Laravel", + "keywords": [ + "load balancing", + "proxy", + "trusted proxy" + ], + "support": { + "issues": "https://github.com/fideloper/TrustedProxy/issues", + "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.1" + }, + "time": "2020-10-22T13:48:01+00:00" + }, + { + "name": "fruitcake/laravel-cors", + "version": "v1.0.6", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/laravel-cors.git", + "reference": "1d127dbec313e2e227d65e0c483765d8d7559bf6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/1d127dbec313e2e227d65e0c483765d8d7559bf6", + "reference": "1d127dbec313e2e227d65e0c483765d8d7559bf6", + "shasum": "" + }, + "require": { + "asm89/stack-cors": "^1.3", + "illuminate/contracts": "^5.5|^6.0|^7.0|^8.0", + "illuminate/support": "^5.5|^6.0|^7.0|^8.0", + "php": ">=7", + "symfony/http-foundation": "^3.3|^4.0|^5.0", + "symfony/http-kernel": "^3.3|^4.0|^5.0" + }, + "require-dev": { + "laravel/framework": "^5.5|^6.0|^7.0|^8.0", + "orchestra/testbench": "^3.5|^4.0|^5.0|^6.0", + "phpro/grumphp": "^0.16|^0.17", + "phpunit/phpunit": "^6.0|^7.0|^8.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "laravel": { + "providers": [ + "Fruitcake\\Cors\\CorsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application", + "keywords": [ + "api", + "cors", + "crossdomain", + "laravel" + ], + "support": { + "issues": "https://github.com/fruitcake/laravel-cors/issues", + "source": "https://github.com/fruitcake/laravel-cors/tree/1.0" + }, + "funding": [ + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2020-04-28T08:47:37+00:00" + }, + { + "name": "graham-campbell/guzzle-factory", + "version": "v5.1.0", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Guzzle-Factory.git", + "reference": "d9168bbceeb33bd15eb7dee50325a3ff6bc1e8a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Guzzle-Factory/zipball/d9168bbceeb33bd15eb7dee50325a3ff6bc1e8a3", + "reference": "d9168bbceeb33bd15eb7dee50325a3ff6bc1e8a3", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.4.3", + "guzzlehttp/psr7": "^2.2.1", + "php": "^7.4.15 || ^8.0.2" + }, + "require-dev": { + "graham-campbell/analyzer": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\GuzzleFactory\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Provides A Simple Guzzle Factory With Good Defaults", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Guzzle", + "Guzzle Factory", + "Guzzle-Factory", + "http" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Guzzle-Factory/issues", + "source": "https://github.com/GrahamCampbell/Guzzle-Factory/tree/v5.1.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/guzzle-factory", + "type": "tidelift" + } + ], + "time": "2022-05-30T20:32:48+00:00" + }, + { + "name": "graham-campbell/manager", + "version": "v4.7.0", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Laravel-Manager.git", + "reference": "b4cafa6491b9c92ecf7ce17521580050a27b8308" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Laravel-Manager/zipball/b4cafa6491b9c92ecf7ce17521580050a27b8308", + "reference": "b4cafa6491b9c92ecf7ce17521580050a27b8308", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "php": "^7.1.3 || ^8.0" + }, + "require-dev": { + "graham-campbell/analyzer": "^2.4 || ^3.0", + "graham-campbell/testbench-core": "^3.4", + "mockery/mockery": "^1.3.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Manager Provides Some Manager Functionality For Laravel", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Laravel Manager", + "Laravel-Manager", + "connector", + "framework", + "interface", + "laravel", + "manager" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Laravel-Manager/issues", + "source": "https://github.com/GrahamCampbell/Laravel-Manager/tree/v4.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/manager", + "type": "tidelift" + } + ], + "time": "2022-01-24T01:59:19+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "e3ff079b22820c2029d4c2a87796b6a0b8716ad8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/e3ff079b22820c2029d4c2a87796b6a0b8716ad8", + "reference": "e3ff079b22820c2029d4c2a87796b6a0b8716ad8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-06-09T21:39:15+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/83260bb50b8fc753c72d14dc1621a2dac31877ee", + "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-06-09T08:26:02+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "hashids/hashids", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/vinkla/hashids.git", + "reference": "8cab111f78e0bd9c76953b082919fc9e251761be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vinkla/hashids/zipball/8cab111f78e0bd9c76953b082919fc9e251761be", + "reference": "8cab111f78e0bd9c76953b082919fc9e251761be", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0 || ^9.4", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-bcmath": "Required to use BC Math arbitrary precision mathematics (*).", + "ext-gmp": "Required to use GNU multiple precision mathematics (*)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Hashids\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Akimov", + "email": "ivan@barreleye.com" + }, + { + "name": "Vincent Klaiber", + "email": "hello@doubledip.se" + } + ], + "description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers", + "homepage": "https://hashids.org/php", + "keywords": [ + "bitly", + "decode", + "encode", + "hash", + "hashid", + "hashids", + "ids", + "obfuscate", + "youtube" + ], + "support": { + "issues": "https://github.com/vinkla/hashids/issues", + "source": "https://github.com/vinkla/hashids/tree/4.1.0" + }, + "time": "2020-11-26T19:24:33+00:00" + }, + { + "name": "innocenzi/laravel-vite", + "version": "0.1.27", + "source": { + "type": "git", + "url": "https://github.com/innocenzi/laravel-vite.git", + "reference": "50bc07018bd6a2b6c88e074c62bfb0909275f327" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/innocenzi/laravel-vite/zipball/50bc07018bd6a2b6c88e074c62bfb0909275f327", + "reference": "50bc07018bd6a2b6c88e074c62bfb0909275f327", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "^1.0", + "guzzlehttp/guzzle": "^6.0|^7.2", + "illuminate/contracts": "^8.0|^9.0", + "php": "^7.4|^8.0", + "spatie/laravel-package-tools": "^1.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2.1", + "orchestra/testbench": "^6.0|^7.0", + "pestphp/pest": "^1.20.0", + "phpunit/phpunit": "^9.3", + "symfony/process": "^5.3|^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Innocenzi\\Vite\\ViteServiceProvider" + ], + "aliases": { + "Vite": "Innocenzi\\Vite\\ViteFacade" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Innocenzi\\Vite\\": "src", + "Innocenzi\\Vite\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Enzo Innocenzi", + "email": "enzo@innocenzi.dev", + "role": "Developer" + } + ], + "description": "Vite integration for Laravel", + "homepage": "https://github.com/innocenzi/laravel-vite", + "keywords": [ + "innocenzi", + "laravel-vite" + ], + "support": { + "issues": "https://github.com/innocenzi/laravel-vite/issues", + "source": "https://github.com/innocenzi/laravel-vite/tree/0.1.27" + }, + "funding": [ + { + "url": "https://github.com/innocenzi", + "type": "github" + } + ], + "time": "2022-01-31T14:50:39+00:00" + }, + { + "name": "intervention/image", + "version": "2.7.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "04be355f8d6734c826045d02a1079ad658322dad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", + "reference": "04be355f8d6734c826045d02a1079ad658322dad", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1 || ^2.0", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15" + }, + "suggest": { + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + }, + "laravel": { + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ], + "aliases": { + "Image": "Intervention\\Image\\Facades\\Image" + } + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/2.7.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + } + ], + "time": "2022-05-21T17:30:32+00:00" + }, + { + "name": "jasonmccreary/laravel-test-assertions", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/jasonmccreary/laravel-test-assertions.git", + "reference": "2547c0366f1ee9a2d58b031468b26edc63faef4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jasonmccreary/laravel-test-assertions/zipball/2547c0366f1ee9a2d58b031468b26edc63faef4e", + "reference": "2547c0366f1ee9a2d58b031468b26edc63faef4e", + "shasum": "" + }, + "require": { + "illuminate/testing": "^8.0|^9.0", + "mockery/mockery": "^1.4.2", + "php": ">=7.3|^8.0", + "phpunit/phpunit": "^9.3.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "JMac\\Testing\\AdditionalAssertionsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "JMac\\Testing\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jason McCreary", + "email": "jason@pureconcepts.net" + } + ], + "description": "A set of helpful assertions when testing Laravel applications.", + "support": { + "issues": "https://github.com/jasonmccreary/laravel-test-assertions/issues", + "source": "https://github.com/jasonmccreary/laravel-test-assertions/tree/v2.1.1" + }, + "time": "2022-03-08T14:47:39+00:00" + }, + { + "name": "laravel/framework", + "version": "v8.83.16", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "6be5abd144faf517879af7298e9d79f06f250f75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/6be5abd144faf517879af7298e9d79f06f250f75", + "reference": "6be5abd144faf517879af7298e9d79f06f250f75", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^1.4|^2.0", + "dragonmantank/cron-expression": "^3.0.2", + "egulias/email-validator": "^2.1.10", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "laravel/serializable-closure": "^1.0", + "league/commonmark": "^1.3|^2.0.2", + "league/flysystem": "^1.1", + "monolog/monolog": "^2.0", + "nesbot/carbon": "^2.53.1", + "opis/closure": "^3.6", + "php": "^7.3|^8.0", + "psr/container": "^1.0", + "psr/log": "^1.0|^2.0", + "psr/simple-cache": "^1.0", + "ramsey/uuid": "^4.2.2", + "swiftmailer/swiftmailer": "^6.3", + "symfony/console": "^5.4", + "symfony/error-handler": "^5.4", + "symfony/finder": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/mime": "^5.4", + "symfony/process": "^5.4", + "symfony/routing": "^5.4", + "symfony/var-dumper": "^5.4", + "tijsverkoyen/css-to-inline-styles": "^2.2.2", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^1.6.1" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.198.1", + "doctrine/dbal": "^2.13.3|^3.1.4", + "filp/whoops": "^2.14.3", + "guzzlehttp/guzzle": "^6.5.5|^7.0.1", + "league/flysystem-cached-adapter": "^1.0", + "mockery/mockery": "^1.4.4", + "orchestra/testbench-core": "^6.27", + "pda/pheanstalk": "^4.0", + "phpunit/phpunit": "^8.5.19|^9.5.8", + "predis/predis": "^1.1.9", + "symfony/cache": "^5.4" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", + "ext-bcmath": "Required to use the multiple_of validation rule.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", + "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", + "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", + "mockery/mockery": "Required to use mocking (^1.4.4).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^8.5.19|^9.5.8).", + "predis/predis": "Required to use the predis connector (^1.1.9).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^5.4).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", + "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-06-07T15:09:06+00:00" + }, + { + "name": "laravel/helpers", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/helpers.git", + "reference": "c28b0ccd799d58564c41a62395ac9511a1e72931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/helpers/zipball/c28b0ccd799d58564c41a62395ac9511a1e72931", + "reference": "c28b0ccd799d58564c41a62395ac9511a1e72931", + "shasum": "" + }, + "require": { + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "php": "^7.1.3|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries@laravel.com" + } + ], + "description": "Provides backwards compatibility for helpers in the latest Laravel release.", + "keywords": [ + "helpers", + "laravel" + ], + "support": { + "source": "https://github.com/laravel/helpers/tree/v1.5.0" + }, + "time": "2022-01-12T15:58:51+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v2.15.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473", + "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^6.9|^7.0|^8.0|^9.0", + "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0", + "illuminate/database": "^6.9|^7.0|^8.0|^9.0", + "illuminate/support": "^6.9|^7.0|^8.0|^9.0", + "php": "^7.2|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", + "phpunit/phpunit": "^8.0|^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2022-04-08T13:39:49+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "pestphp/pest": "^1.18", + "phpstan/phpstan": "^0.12.98", + "symfony/var-dumper": "^5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2022-05-16T17:09:47+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.7.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "dff39b661e827dae6e092412f976658df82dbac5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/dff39b661e827dae6e092412f976658df82dbac5", + "reference": "dff39b661e827dae6e092412f976658df82dbac5", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.10.4|^0.11.1", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.7.2" + }, + "time": "2022-03-23T12:38:24+00:00" + }, + { + "name": "laravel/ui", + "version": "v3.4.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/ui.git", + "reference": "65ec5c03f7fee2c8ecae785795b829a15be48c2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/ui/zipball/65ec5c03f7fee2c8ecae785795b829a15be48c2c", + "reference": "65ec5c03f7fee2c8ecae785795b829a15be48c2c", + "shasum": "" + }, + "require": { + "illuminate/console": "^8.42|^9.0", + "illuminate/filesystem": "^8.42|^9.0", + "illuminate/support": "^8.82|^9.0", + "illuminate/validation": "^8.42|^9.0", + "php": "^7.3|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^6.23|^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Ui\\": "src/", + "Illuminate\\Foundation\\Auth\\": "auth-backend/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel UI utilities and presets.", + "keywords": [ + "laravel", + "ui" + ], + "support": { + "source": "https://github.com/laravel/ui/tree/v3.4.6" + }, + "time": "2022-05-20T13:38:08+00:00" + }, + { + "name": "lavary/laravel-menu", + "version": "v1.8.3", + "source": { + "type": "git", + "url": "https://github.com/lavary/laravel-menu.git", + "reference": "4f14cb9b11cfcd9839d1d87c806632c84a94d3be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lavary/laravel-menu/zipball/4f14cb9b11cfcd9839d1d87c806632c84a94d3be", + "reference": "4f14cb9b11cfcd9839d1d87c806632c84a94d3be", + "shasum": "" + }, + "require": { + "illuminate/support": ">=5.0", + "illuminate/view": ">=5.0", + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Lavary\\Menu\\ServiceProvider" + ], + "aliases": { + "Menu": "Lavary\\Menu\\Facade" + } + } + }, + "autoload": { + "psr-0": { + "Lavary\\Menu\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lavary", + "email": "mrl.8081@gmail.com" + } + ], + "description": "A quick way to create menus in Laravel 5", + "homepage": "https://github.com/lavary/laravel-menu", + "keywords": [ + "laravel" + ], + "support": { + "issues": "https://github.com/lavary/laravel-menu/issues", + "source": "https://github.com/lavary/laravel-menu/tree/v1.8.3" + }, + "time": "2021-02-22T16:41:26+00:00" + }, + { + "name": "league/commonmark", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "0da1dca5781dd3cfddbe328224d9a7a62571addc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/0da1dca5781dd3cfddbe328224d9a7a62571addc", + "reference": "0da1dca5781dd3cfddbe328224d9a7a62571addc", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^0.12.88 || ^1.0.0", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2022-06-07T21:28:26+00:00" + }, + { + "name": "league/config", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", + "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.90", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2021-08-14T12:15:32+00:00" + }, + { + "name": "league/flysystem", + "version": "1.1.9", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "094defdb4a7001845300334e7c1ee2335925ef99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/094defdb4a7001845300334e7c1ee2335925ef99", + "reference": "094defdb4a7001845300334e7c1ee2335925ef99", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/mime-type-detection": "^1.3", + "php": "^7.2.5 || ^8.0" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/prophecy": "^1.11.1", + "phpunit/phpunit": "^8.5.8" + }, + "suggest": { + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/1.1.9" + }, + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2021-12-09T09:40:50+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "1.0.29", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "4e25cc0582a36a786c31115e419c6e40498f6972" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972", + "reference": "4e25cc0582a36a786c31115e419c6e40498f6972", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.20.0", + "league/flysystem": "^1.0.40", + "php": ">=5.5.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "^2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3v3\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for the AWS S3 SDK v3.x", + "support": { + "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues", + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29" + }, + "time": "2020-10-08T18:58:37+00:00" + }, + { + "name": "league/glide", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/glide.git", + "reference": "257e0c3612ef3dc57eb7f90cb741198151a45a5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/glide/zipball/257e0c3612ef3dc57eb7f90cb741198151a45a5f", + "reference": "257e0c3612ef3dc57eb7f90cb741198151a45a5f", + "shasum": "" + }, + "require": { + "intervention/image": "^2.4", + "league/flysystem": "^1.0", + "php": "^7.2|^8.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "phpunit/php-token-stream": "^3.1|^4.0", + "phpunit/phpunit": "^8.5|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Glide\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "homepage": "http://reinink.ca" + }, + { + "name": "Titouan Galopin", + "email": "galopintitouan@gmail.com", + "homepage": "https://titouangalopin.com" + } + ], + "description": "Wonderfully easy on-demand image manipulation library with an HTTP based API.", + "homepage": "http://glide.thephpleague.com", + "keywords": [ + "ImageMagick", + "editing", + "gd", + "image", + "imagick", + "league", + "manipulation", + "processing" + ], + "support": { + "issues": "https://github.com/thephpleague/glide/issues", + "source": "https://github.com/thephpleague/glide/tree/1.7.1" + }, + "time": "2022-04-27T04:03:46+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-04-17T13:12:02+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/211e9ba1530ea5260b45d90c9ea252f56ec52729", + "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729", + "shasum": "" + }, + "require": { + "myclabs/php-enum": "^1.5", + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "ext-zip": "*", + "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.4", + "phpunit/phpunit": "^8.5.8 || ^9.4.2", + "vimeo/psalm": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.1" + }, + "funding": [ + { + "url": "https://opencollective.com/zipstream", + "type": "open_collective" + } + ], + "time": "2022-05-18T15:52:06+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.5.0" + }, + "time": "2022-01-20T13:18:17+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "5579edf28aee1190a798bfa5be8bc16c563bd524" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5579edf28aee1190a798bfa5be8bc16c563bd524", + "reference": "5579edf28aee1190a798bfa5be8bc16c563bd524", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-06-09T08:59:12+00:00" + }, + { + "name": "mtdowling/jmespath.php", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1" + }, + "time": "2021-06-14T00:11:39+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "myclabs/php-enum", + "version": "1.8.3", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "b942d263c641ddb5190929ff840c68f78713e937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937", + "reference": "b942d263c641ddb5190929ff840c68f78713e937", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^4.6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/1.8.3" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", + "type": "tidelift" + } + ], + "time": "2021-07-05T08:18:36+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.58.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/97a34af22bde8d0ac20ab34b29d7bfe360902055", + "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.0", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.54 || ^1.0", + "phpunit/php-file-iterator": "^2.0.5", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2022-04-25T19:31:17+00:00" + }, + { + "name": "nette/schema", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df", + "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df", + "shasum": "" + }, + "require": { + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", + "php": ">=7.1 <8.2" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "phpstan/phpstan-nette": "^0.12", + "tracy/tracy": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.2.2" + }, + "time": "2021-10-15T11:40:02+00:00" + }, + { + "name": "nette/utils", + "version": "v3.2.7", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99", + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99", + "shasum": "" + }, + "require": { + "php": ">=7.2 <8.2" + }, + "conflict": { + "nette/di": "<3.0.6" + }, + "require-dev": { + "nette/tester": "~2.0", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.3" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v3.2.7" + }, + "time": "2022-01-24T11:29:14+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.14.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + }, + "time": "2022-05-31T20:59:12+00:00" + }, + { + "name": "opis/closure", + "version": "3.6.3", + "source": { + "type": "git", + "url": "https://github.com/opis/closure.git", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0" + }, + "require-dev": { + "jeremeamia/superclosure": "^2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Opis\\Closure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", + "homepage": "https://opis.io/closure", + "keywords": [ + "anonymous functions", + "closure", + "function", + "serializable", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/opis/closure/issues", + "source": "https://github.com/opis/closure/tree/3.6.3" + }, + "time": "2022-01-27T09:35:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phenx/php-font-lib", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4", + "shasum": "" + }, + "require": { + "ext-mbstring": "*" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4" + }, + "time": "2021-12-17T19:44:54+00:00" + }, + { + "name": "phenx/php-svg-lib", + "version": "0.4.1", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "4498b5df7b08e8469f0f8279651ea5de9626ed02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/4498b5df7b08e8469f0f8279651ea5de9626ed02", + "reference": "4498b5df7b08e8469f0f8279651ea5de9626ed02", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^7.2 || ^7.3 || ^7.4 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/0.4.1" + }, + "time": "2022-03-07T12:52:04+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "77a32518733312af16a44300404e945338981de3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, + "time": "2022-03-15T21:29:03+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-03-07T09:28:20+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "predis/predis", + "version": "v1.1.10", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e", + "reference": "a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v1.1.10" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2022-01-05T17:46:08+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/2.0.0" + }, + "time": "2021-07-14T16:41:46+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.11.5", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "c23686f9c48ca202710dbb967df8385a952a2daf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/c23686f9c48ca202710dbb967df8385a952a2daf", + "reference": "c23686f9c48ca202710dbb967df8385a952a2daf", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^4.0 || ^3.1", + "php": "^8.0 || ^7.0.8", + "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.11.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.11.5" + }, + "time": "2022-05-27T18:03:49+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8", + "symfony/polyfill-php81": "^1.23" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "ergebnis/composer-normalize": "^2.6", + "fakerphp/faker": "^1.5", + "hamcrest/hamcrest-php": "^2", + "jangregor/phpstan-prophecy": "^0.8", + "mockery/mockery": "^1.3", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^0.12.32", + "phpstan/phpstan-mockery": "^0.12.5", + "phpstan/phpstan-phpunit": "^0.12.11", + "phpunit/phpunit": "^8.5 || ^9", + "psy/psysh": "^0.10.4", + "slevomat/coding-standard": "^6.3", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2021-10-10T03:01:02+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "shasum": "" + }, + "require": { + "brick/math": "^0.8 || ^0.9", + "ext-ctype": "*", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "moontoast/math": "^1.1", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-mockery": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^8.5 || ^9", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-ctype": "Enables faster processing of character classification using ctype functions.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.3.1" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2022-03-27T21:42:02+00:00" + }, + { + "name": "sabberworm/php-css-parser", + "version": "8.4.0", + "source": { + "type": "git", + "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=5.6.20" + }, + "require-dev": { + "codacy/coverage": "^1.4", + "phpunit/phpunit": "^4.8.36" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0" + }, + "time": "2021-12-11T13:40:54+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-03T09:37:03+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T14:18:36+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-03-15T09:54:48+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "silber/bouncer", + "version": "v1.0.0-rc.10", + "source": { + "type": "git", + "url": "https://github.com/JosephSilber/bouncer.git", + "reference": "429262a84414569be2fad1fad417f05d752c075b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JosephSilber/bouncer/zipball/429262a84414569be2fad1fad417f05d752c075b", + "reference": "429262a84414569be2fad1fad417f05d752c075b", + "shasum": "" + }, + "require": { + "illuminate/auth": "^6.0|^7.0|^8.0", + "illuminate/cache": "^6.0|^7.0|^8.0", + "illuminate/container": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0", + "illuminate/database": "^6.0|^7.0|^8.0", + "php": "^7.2|^8.0" + }, + "require-dev": { + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/events": "^6.0|^7.0|^8.0", + "larapack/dd": "^1.1", + "mockery/mockery": "^1.3.3", + "phpunit/phpunit": "^8.0|^9.0" + }, + "suggest": { + "illuminate/console": "Allows running the bouncer:clean artisan command", + "illuminate/events": "Required for multi-tenancy support" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Silber\\Bouncer\\BouncerServiceProvider" + ], + "aliases": { + "Bouncer": "Silber\\Bouncer\\BouncerFacade" + } + } + }, + "autoload": { + "psr-4": { + "Silber\\Bouncer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joseph Silber", + "email": "contact@josephsilber.com" + } + ], + "description": "Eloquent roles and abilities.", + "keywords": [ + "abilities", + "acl", + "capabilities", + "eloquent", + "laravel", + "permissions", + "roles" + ], + "support": { + "issues": "https://github.com/JosephSilber/bouncer/issues", + "source": "https://github.com/JosephSilber/bouncer/tree/v1.0.0-rc.10" + }, + "time": "2020-12-08T15:31:20+00:00" + }, + { + "name": "spatie/db-dumper", + "version": "2.21.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/db-dumper.git", + "reference": "05e5955fb882008a8947c5a45146d86cfafa10d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/05e5955fb882008a8947c5a45146d86cfafa10d1", + "reference": "05e5955fb882008a8947c5a45146d86cfafa10d1", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "symfony/process": "^4.2|^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\DbDumper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Dump databases", + "homepage": "https://github.com/spatie/db-dumper", + "keywords": [ + "database", + "db-dumper", + "dump", + "mysqldump", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/db-dumper/issues", + "source": "https://github.com/spatie/db-dumper/tree/2.21.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2021-02-24T14:56:42+00:00" + }, + { + "name": "spatie/dropbox-api", + "version": "1.20.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/dropbox-api.git", + "reference": "f7563632fa6e4970b895805169688be273fcbf19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/dropbox-api/zipball/f7563632fa6e4970b895805169688be273fcbf19", + "reference": "f7563632fa6e4970b895805169688be273fcbf19", + "shasum": "" + }, + "require": { + "graham-campbell/guzzle-factory": "^3.0|^4.0|^5.0", + "guzzlehttp/guzzle": "^6.2|^7.0", + "php": "^7.1|^8.0" + }, + "conflict": { + "guzzlehttp/psr7": "<1.7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Dropbox\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex.vanderbist@gmail.com", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A minimal implementation of Dropbox API v2", + "homepage": "https://github.com/spatie/dropbox-api", + "keywords": [ + "Dropbox-API", + "api", + "dropbox", + "spatie", + "v2" + ], + "support": { + "issues": "https://github.com/spatie/dropbox-api/issues", + "source": "https://github.com/spatie/dropbox-api/tree/1.20.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-03-29T06:41:44+00:00" + }, + { + "name": "spatie/flysystem-dropbox", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/flysystem-dropbox.git", + "reference": "8b6b072f217343b875316ca6a4203dd59f04207a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/flysystem-dropbox/zipball/8b6b072f217343b875316ca6a4203dd59f04207a", + "reference": "8b6b072f217343b875316ca6a4203dd59f04207a", + "shasum": "" + }, + "require": { + "league/flysystem": "^1.0.20", + "php": "^7.0 || ^8.0", + "spatie/dropbox-api": "^1.1.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.11 || ^9.4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\FlysystemDropbox\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex.vanderbist@gmail.com", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Flysystem Adapter for the Dropbox v2 API", + "homepage": "https://github.com/spatie/flysystem-dropbox", + "keywords": [ + "Flysystem", + "api", + "dropbox", + "flysystem-dropbox", + "spatie", + "v2" + ], + "support": { + "issues": "https://github.com/spatie/flysystem-dropbox/issues", + "source": "https://github.com/spatie/flysystem-dropbox/tree/1.2.3" + }, + "time": "2020-11-28T22:17:09+00:00" + }, + { + "name": "spatie/image", + "version": "1.10.6", + "source": { + "type": "git", + "url": "https://github.com/spatie/image.git", + "reference": "897e819848096ea8eee8ed4a3531c6166f9a99e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/image/zipball/897e819848096ea8eee8ed4a3531c6166f9a99e0", + "reference": "897e819848096ea8eee8ed4a3531c6166f9a99e0", + "shasum": "" + }, + "require": { + "ext-exif": "*", + "ext-json": "*", + "ext-mbstring": "*", + "league/glide": "^1.6", + "php": "^7.2|^8.0", + "spatie/image-optimizer": "^1.1", + "spatie/temporary-directory": "^1.0|^2.0", + "symfony/process": "^3.0|^4.0|^5.0|^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.21|^9.5.4", + "symfony/var-dumper": "^4.0|^5.0|^6.0", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Image\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Manipulate images with an expressive API", + "homepage": "https://github.com/spatie/image", + "keywords": [ + "image", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/image/issues", + "source": "https://github.com/spatie/image/tree/1.10.6" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2021-12-21T10:01:09+00:00" + }, + { + "name": "spatie/image-optimizer", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/image-optimizer.git", + "reference": "6db75529cbf8fa84117046a9d513f277aead90a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/6db75529cbf8fa84117046a9d513f277aead90a0", + "reference": "6db75529cbf8fa84117046a9d513f277aead90a0", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.3|^8.0", + "psr/log": "^1.0 | ^2.0 | ^3.0", + "symfony/process": "^4.2|^5.0|^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.21|^9.4.4", + "symfony/var-dumper": "^4.2|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ImageOptimizer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily optimize images using PHP", + "homepage": "https://github.com/spatie/image-optimizer", + "keywords": [ + "image-optimizer", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/image-optimizer/issues", + "source": "https://github.com/spatie/image-optimizer/tree/1.6.2" + }, + "time": "2021-12-21T10:08:05+00:00" + }, + { + "name": "spatie/laravel-backup", + "version": "6.16.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-backup.git", + "reference": "332fae80b12cacb9e4161824ba195d984b28c8fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/332fae80b12cacb9e4161824ba195d984b28c8fb", + "reference": "332fae80b12cacb9e4161824ba195d984b28c8fb", + "shasum": "" + }, + "require": { + "ext-zip": "^1.14.0", + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0", + "illuminate/events": "^6.0|^7.0|^8.0", + "illuminate/filesystem": "^6.0|^7.0|^8.0", + "illuminate/notifications": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "league/flysystem": "^1.0.49", + "php": "^7.3|^8.0", + "spatie/db-dumper": "^2.12", + "spatie/temporary-directory": "^1.1", + "symfony/finder": "^4.2|^5.0" + }, + "require-dev": { + "laravel/slack-notification-channel": "^2.3", + "league/flysystem-aws-s3-v3": "^1.0", + "mockery/mockery": "^1.4.2", + "orchestra/testbench": "4.*|5.*|6.*", + "phpunit/phpunit": "^8.4|^9.0" + }, + "suggest": { + "laravel/slack-notification-channel": "Required for sending notifications via Slack" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Backup\\BackupServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Helpers/functions.php" + ], + "psr-4": { + "Spatie\\Backup\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A Laravel package to backup your application", + "homepage": "https://github.com/spatie/laravel-backup", + "keywords": [ + "backup", + "database", + "laravel-backup", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-backup/issues", + "source": "https://github.com/spatie/laravel-backup/tree/6.16.5" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2021-09-12T10:04:18+00:00" + }, + { + "name": "spatie/laravel-medialibrary", + "version": "8.10.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-medialibrary.git", + "reference": "448e8389cadc79f42c3c96c7c9491b57015702d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/448e8389cadc79f42c3c96c7c9491b57015702d4", + "reference": "448e8389cadc79f42c3c96c7c9491b57015702d4", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-json": "*", + "illuminate/bus": "^6.18|^7.0|^8.0", + "illuminate/console": "^6.18|^7.0|^8.0", + "illuminate/database": "^6.18|^7.0|^8.0", + "illuminate/pipeline": "^6.18|^7.0|^8.0", + "illuminate/support": "^6.18|^7.0|^8.0", + "league/flysystem": "^1.0.64", + "maennchen/zipstream-php": "^1.0|^2.0", + "php": "^7.4|^8.0", + "spatie/image": "^1.4.0", + "spatie/temporary-directory": "^1.1", + "symfony/console": "^4.4|^5.0" + }, + "conflict": { + "php-ffmpeg/php-ffmpeg": "<0.6.1" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.133.11", + "doctrine/dbal": "^2.5.2", + "ext-pdo_sqlite": "*", + "ext-zip": "*", + "guzzlehttp/guzzle": "^6.3|^7.0", + "league/flysystem-aws-s3-v3": "^1.0.23", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^4.0|^5.0|^6.0", + "php-ffmpeg/php-ffmpeg": "^0.17.0", + "phpunit/phpunit": "^9.1", + "spatie/pdf-to-image": "^2.0", + "spatie/phpunit-snapshot-assertions": "^4.0" + }, + "suggest": { + "league/flysystem-aws-s3-v3": "Required to use AWS S3 file storage", + "php-ffmpeg/php-ffmpeg": "Required for generating video thumbnails", + "spatie/pdf-to-image": "Required for generating thumbsnails of PDFs and SVGs" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\MediaLibrary\\MediaLibraryServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\MediaLibrary\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Associate files with Eloquent models", + "homepage": "https://github.com/spatie/laravel-medialibrary", + "keywords": [ + "cms", + "conversion", + "downloads", + "images", + "laravel", + "laravel-medialibrary", + "media", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-medialibrary/issues", + "source": "https://github.com/spatie/laravel-medialibrary/tree/8.10.2" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2021-05-22T09:23:57+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.11.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "baeb3df0ebb3a541394fdaf8cbe6115bf4034a59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/baeb3df0ebb3a541394fdaf8cbe6115bf4034a59", + "reference": "baeb3df0ebb3a541394fdaf8cbe6115bf4034a59", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^7.0|^8.0|^9.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.4", + "orchestra/testbench": "^5.0|^6.23|^7.0", + "phpunit/phpunit": "^9.4", + "spatie/test-time": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.11.3" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-03-15T20:01:36+00:00" + }, + { + "name": "spatie/temporary-directory", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/temporary-directory.git", + "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/f517729b3793bca58f847c5fd383ec16f03ffec6", + "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\TemporaryDirectory\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "php", + "spatie", + "temporary-directory" + ], + "support": { + "issues": "https://github.com/spatie/temporary-directory/issues", + "source": "https://github.com/spatie/temporary-directory/tree/1.3.0" + }, + "time": "2020-11-09T15:54:21+00:00" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", + "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.0|^3.1", + "php": ">=7.0.0", + "symfony/polyfill-iconv": "^1.0", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "symfony/phpunit-bridge": "^4.4|^5.4" + }, + "suggest": { + "ext-intl": "Needed to support internationalized email addresses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "https://swiftmailer.symfony.com", + "keywords": [ + "email", + "mail", + "mailer" + ], + "support": { + "issues": "https://github.com/swiftmailer/swiftmailer/issues", + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", + "type": "tidelift" + } + ], + "abandoned": "symfony/mailer", + "time": "2021-10-18T15:26:12+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/829d5d1bf60b2efeb0887b7436873becc71a45eb", + "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-18T06:17:34+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v6.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "1955d595c12c111629cc814d3f2a2ff13580508a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1955d595c12c111629cc814d3f2a2ff13580508a", + "reference": "1955d595c12c111629cc814d3f2a2ff13580508a", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v6.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "c116cda1f51c678782768dce89a45f13c949455d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c116cda1f51c678782768dce89a45f13c949455d", + "reference": "c116cda1f51c678782768dce89a45f13c949455d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/serializer": "^4.4|^5.0|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T13:57:48+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "5c85b58422865d42c6eb46f7693339056db098a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5c85b58422865d42c6eb46f7693339056db098a8", + "reference": "5c85b58422865d42c6eb46f7693339056db098a8", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/event-dispatcher-contracts": "^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-05T16:45:52+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/9b630f3427f3ebe7cd346c277a1408b00249dad9", + "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-15T08:07:45+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "6b0d0e4aca38d57605dcd11e2416994b38774522" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6b0d0e4aca38d57605dcd11e2416994b38774522", + "reference": "6b0d0e4aca38d57605dcd11e2416994b38774522", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-17T15:07:29+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "34b121ad3dc761f35fe1346d2f15618f8cbf77f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/34b121ad3dc761f35fe1346d2f15618f8cbf77f8", + "reference": "34b121ad3dc761f35fe1346d2f15618f8cbf77f8", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^5.0|^6.0", + "symfony/http-foundation": "^5.3.7|^6.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.0", + "symfony/config": "<5.0", + "symfony/console": "<4.4", + "symfony/dependency-injection": "<5.3", + "symfony/doctrine-bridge": "<5.0", + "symfony/form": "<5.0", + "symfony/http-client": "<5.0", + "symfony/mailer": "<5.0", + "symfony/messenger": "<5.0", + "symfony/translation": "<5.0", + "symfony/twig-bridge": "<5.0", + "symfony/validator": "<5.0", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^5.0|^6.0", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^5.3|^6.0", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "symfony/translation": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-27T07:09:08+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "2b3802a24e48d0cfccf885173d2aac91e73df92e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/2b3802a24e48d0cfccf885173d2aac91e73df92e", + "reference": "2b3802a24e48d0cfccf885173d2aac91e73df92e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/property-access": "^4.4|^5.1|^6.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/serializer": "^5.2|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T10:24:18+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "143f1881e655bebca1312722af8068de235ae5dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/143f1881e655bebca1312722af8068de235ae5dc", + "reference": "143f1881e655bebca1312722af8068de235ae5dc", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "433d05519ce6990bf3530fba6957499d327395c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", + "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8", + "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2", + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", + "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-08T05:07:18+00:00" + }, + { + "name": "symfony/routing", + "version": "v5.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "e07817bb6244ea33ef5ad31abc4a9288bef3f2f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/e07817bb6244ea33ef5ad31abc4a9288bef3f2f7", + "reference": "e07817bb6244ea33ef5ad31abc4a9288bef3f2f7", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<5.3", + "symfony/dependency-injection": "<4.4", + "symfony/yaml": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.3|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v5.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-18T21:45:37+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-13T20:07:29+00:00" + }, + { + "name": "symfony/string", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "df9f03d595aa2d446498ba92fe803a519b2c43cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/df9f03d595aa2d446498ba92fe803a519b2c43cc", + "reference": "df9f03d595aa2d446498ba92fe803a519b2c43cc", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-22T08:18:02+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "9ba011309943955a3807b8236c17cff3b88f67b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/9ba011309943955a3807b8236c17cff3b88f67b6", + "reference": "9ba011309943955a3807b8236c17cff3b88f67b6", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-06T14:27:17+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "af52239a330fafd192c773795520dc2dd62b5657" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/af52239a330fafd192c773795520dc2dd62b5657", + "reference": "af52239a330fafd192c773795520dc2dd62b5657", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<4.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/uid": "^5.1|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T10:24:18+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/da444caae6aca7a19c0c140f68c6182e337d5b1c", + "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5 || ^7.0 || ^8.0", + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.4" + }, + "time": "2021-12-08T09:12:39+00:00" + }, + { + "name": "vinkla/hashids", + "version": "9.1.0", + "source": { + "type": "git", + "url": "https://github.com/vinkla/laravel-hashids.git", + "reference": "cb0086db96cdb49816465adc97e3a024c8ee9767" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vinkla/laravel-hashids/zipball/cb0086db96cdb49816465adc97e3a024c8ee9767", + "reference": "cb0086db96cdb49816465adc97e3a024c8ee9767", + "shasum": "" + }, + "require": { + "graham-campbell/manager": "^4.4", + "hashids/hashids": "^4.1", + "illuminate/contracts": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "graham-campbell/analyzer": "^3.0", + "graham-campbell/testbench": "^5.4", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.1-dev" + }, + "laravel": { + "providers": [ + "Vinkla\\Hashids\\HashidsServiceProvider" + ], + "aliases": { + "Hashids": "Vinkla\\Hashids\\Facades\\Hashids" + } + } + }, + "autoload": { + "psr-4": { + "Vinkla\\Hashids\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Vincent Klaiber", + "email": "hello@doubledip.se" + } + ], + "description": "A Hashids bridge for Laravel", + "keywords": [ + "hashids", + "laravel" + ], + "support": { + "issues": "https://github.com/vinkla/laravel-hashids/issues", + "source": "https://github.com/vinkla/laravel-hashids/tree/9.1.0" + }, + "time": "2020-11-26T19:38:22+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a", + "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/1.6.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-01-24T18:55:24+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "barryvdh/laravel-ide-helper", + "version": "v2.12.3", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "3ba1e2573b38f72107b8aacc4ee177fcab30a550" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/3ba1e2573b38f72107b8aacc4ee177fcab30a550", + "reference": "3ba1e2573b38f72107b8aacc4ee177fcab30a550", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.0.6", + "composer/pcre": "^1 || ^2 || ^3", + "doctrine/dbal": "^2.6 || ^3", + "ext-json": "*", + "illuminate/console": "^8 || ^9", + "illuminate/filesystem": "^8 || ^9", + "illuminate/support": "^8 || ^9", + "nikic/php-parser": "^4.7", + "php": "^7.3 || ^8.0", + "phpdocumentor/type-resolver": "^1.1.0" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "friendsofphp/php-cs-fixer": "^2", + "illuminate/config": "^8 || ^9", + "illuminate/view": "^8 || ^9", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^6 || ^7", + "phpunit/phpunit": "^8.5 || ^9", + "spatie/phpunit-snapshot-assertions": "^3 || ^4", + "vimeo/psalm": "^3.12" + }, + "suggest": { + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.12.3" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2022-03-06T14:33:42+00:00" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.0.6", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "6b69015d83d3daf9004a71a89f26e27d27ef6a16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/6b69015d83d3daf9004a71a89f26e27d27ef6a16", + "reference": "6b69015d83d3daf9004a71a89f26e27d27ef6a16", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0,<4.5" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "support": { + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.0.6" + }, + "time": "2018-12-13T10:34:14+00:00" + }, + { + "name": "beyondcode/laravel-dump-server", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/beyondcode/laravel-dump-server.git", + "reference": "33a19c632655c1d4f16c0bc67ce6ba0504116b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beyondcode/laravel-dump-server/zipball/33a19c632655c1d4f16c0bc67ce6ba0504116b8a", + "reference": "33a19c632655c1d4f16c0bc67ce6ba0504116b8a", + "shasum": "" + }, + "require": { + "illuminate/console": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0|^9.0", + "illuminate/http": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0|^9.0", + "illuminate/support": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0|^9.0", + "php": ">=7.2.5", + "symfony/var-dumper": "^5.0|^6.0" + }, + "require-dev": { + "larapack/dd": "^1.0", + "phpunit/phpunit": "^7.0|^9.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "BeyondCode\\DumpServer\\DumpServerServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "BeyondCode\\DumpServer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marcel Pociot", + "email": "marcel@beyondco.de", + "homepage": "https://beyondco.de", + "role": "Developer" + } + ], + "description": "Symfony Var-Dump Server for Laravel", + "homepage": "https://github.com/beyondcode/laravel-dump-server", + "keywords": [ + "beyondcode", + "laravel-dump-server" + ], + "support": { + "issues": "https://github.com/beyondcode/laravel-dump-server/issues", + "source": "https://github.com/beyondcode/laravel-dump-server/tree/1.8.0" + }, + "time": "2022-02-12T12:37:34+00:00" + }, + { + "name": "brianium/paratest", + "version": "v6.4.4", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "589cdb23728b2a19872945580b95d8aa2c6619da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/589cdb23728b2a19872945580b95d8aa2c6619da", + "reference": "589cdb23728b2a19872945580b95d8aa2c6619da", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "php": "^7.3 || ^8.0", + "phpunit/php-code-coverage": "^9.2.11", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-timer": "^5.0.3", + "phpunit/phpunit": "^9.5.14", + "sebastian/environment": "^5.1.3", + "symfony/console": "^5.4.0 || ^6.0.0", + "symfony/process": "^5.4.0 || ^6.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0.0", + "ext-posix": "*", + "infection/infection": "^0.26.5", + "malukenho/mcbumpface": "^1.1.5", + "squizlabs/php_codesniffer": "^3.6.2", + "symfony/filesystem": "^v5.4.0 || ^6.0.0", + "vimeo/psalm": "^4.20.0" + }, + "bin": [ + "bin/paratest" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v6.4.4" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2022-03-28T07:55:11+00:00" + }, + { + "name": "composer/pcre", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T20:21:48+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.2" + }, + "time": "2021-08-05T19:00:23+00:00" + }, + { + "name": "facade/flare-client-php", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/facade/flare-client-php.git", + "reference": "b2adf1512755637d0cef4f7d1b54301325ac78ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/flare-client-php/zipball/b2adf1512755637d0cef4f7d1b54301325ac78ed", + "reference": "b2adf1512755637d0cef4f7d1b54301325ac78ed", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "~1.0", + "illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0", + "php": "^7.1|^8.0", + "symfony/http-foundation": "^3.3|^4.1|^5.0", + "symfony/mime": "^3.4|^4.0|^5.1", + "symfony/var-dumper": "^3.4|^4.0|^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "phpunit/phpunit": "^7.5.16", + "spatie/phpunit-snapshot-assertions": "^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Facade\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/facade/flare-client-php", + "keywords": [ + "exception", + "facade", + "flare", + "reporting" + ], + "support": { + "issues": "https://github.com/facade/flare-client-php/issues", + "source": "https://github.com/facade/flare-client-php/tree/1.9.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2021-09-13T12:16:46+00:00" + }, + { + "name": "facade/ignition", + "version": "2.17.5", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition.git", + "reference": "1d71996f83c9a5a7807331b8986ac890352b7a0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition/zipball/1d71996f83c9a5a7807331b8986ac890352b7a0c", + "reference": "1d71996f83c9a5a7807331b8986ac890352b7a0c", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "facade/flare-client-php": "^1.9.1", + "facade/ignition-contracts": "^1.0.2", + "illuminate/support": "^7.0|^8.0", + "monolog/monolog": "^2.0", + "php": "^7.2.5|^8.0", + "symfony/console": "^5.0", + "symfony/var-dumper": "^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "livewire/livewire": "^2.4", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^5.0|^6.0", + "psalm/plugin-laravel": "^1.2" + }, + "suggest": { + "laravel/telescope": "^3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Facade\\Ignition\\IgnitionServiceProvider" + ], + "aliases": { + "Flare": "Facade\\Ignition\\Facades\\Flare" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Facade\\Ignition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://github.com/facade/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/facade/ignition/issues", + "source": "https://github.com/facade/ignition" + }, + "time": "2022-02-23T18:31:24+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/d7f08a622b3346766325488aa32ddc93ccdecc75", + "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "symfony/phpunit-bridge": "^4.4 || ^5.2" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.19-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.19.0" + }, + "time": "2022-02-02T17:38:57+00:00" + }, + { + "name": "filp/whoops", + "version": "2.14.5", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.14.5" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2022-01-07T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", + "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", + "shasum": "" + }, + "require": { + "composer/semver": "^3.2", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^1.13", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "php-cs-fixer/diff": "^2.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php81": "^1.25", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^1.5", + "mikey179/vfsstream": "^1.6.10", + "php-coveralls/php-coveralls": "^2.5.2", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "symfony/phpunit-bridge": "^6.0", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2022-03-18T17:20:59+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v5.11.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "8b610eef8582ccdc05d8f2ab23305e2d37049461" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/8b610eef8582ccdc05d8f2ab23305e2d37049461", + "reference": "8b610eef8582ccdc05d8f2ab23305e2d37049461", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "^1.0", + "filp/whoops": "^2.14.3", + "php": "^7.3 || ^8.0", + "symfony/console": "^5.0" + }, + "require-dev": { + "brianium/paratest": "^6.1", + "fideloper/proxy": "^4.4.1", + "fruitcake/laravel-cors": "^2.0.3", + "laravel/framework": "8.x-dev", + "nunomaduro/larastan": "^0.6.2", + "nunomaduro/mock-final-classes": "^1.0", + "orchestra/testbench": "^6.0", + "phpstan/phpstan": "^0.12.64", + "phpunit/phpunit": "^9.5.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2022-01-10T16:22:52+00:00" + }, + { + "name": "pestphp/pest", + "version": "v1.21.3", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "66f69617f1e01032e009f783136f129de3476689" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/66f69617f1e01032e009f783136f129de3476689", + "reference": "66f69617f1e01032e009f783136f129de3476689", + "shasum": "" + }, + "require": { + "nunomaduro/collision": "^5.10.0|^6.0", + "pestphp/pest-plugin": "^1.0.0", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^9.5.5" + }, + "require-dev": { + "illuminate/console": "^8.47.0", + "illuminate/support": "^8.47.0", + "laravel/dusk": "^6.15.0", + "pestphp/pest-dev-tools": "dev-master", + "pestphp/pest-plugin-parallel": "^1.0" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + }, + "pest": { + "plugins": [ + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Environment" + ] + }, + "laravel": { + "providers": [ + "Pest\\Laravel\\PestServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v1.21.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/lukeraymonddowning", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/octoper", + "type": "github" + }, + { + "url": "https://github.com/olivernybroe", + "type": "github" + }, + { + "url": "https://github.com/owenvoke", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2022-05-12T19:10:25+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "fc8519de148699fe612d9c669be60554cd2db4fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/fc8519de148699fe612d9c669be60554cd2db4fa", + "reference": "fc8519de148699fe612d9c669be60554cd2db4fa", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1 || ^2.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "pestphp/pest": "<1.0" + }, + "require-dev": { + "composer/composer": "^1.10.19", + "pestphp/pest": "^1.0", + "pestphp/pest-dev-tools": "dev-master" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2021-01-03T15:53:42+00:00" + }, + { + "name": "pestphp/pest-plugin-faker", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-faker.git", + "reference": "9d93419f1f47ffd856ee544317b2f9144a129044" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-faker/zipball/9d93419f1f47ffd856ee544317b2f9144a129044", + "reference": "9d93419f1f47ffd856ee544317b2f9144a129044", + "shasum": "" + }, + "require": { + "fakerphp/faker": "^1.9.1", + "pestphp/pest": "^1.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Faker.php" + ], + "psr-4": { + "Pest\\Faker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Faker Plugin", + "keywords": [ + "faker", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-faker/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2021-01-03T15:42:35+00:00" + }, + { + "name": "pestphp/pest-plugin-laravel", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-laravel.git", + "reference": "64996218006570f6f58f3c7ebb6f0c7bfb3c60b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/64996218006570f6f58f3c7ebb6f0c7bfb3c60b9", + "reference": "64996218006570f6f58f3c7ebb6f0c7bfb3c60b9", + "shasum": "" + }, + "require": { + "laravel/framework": "^7.0 || ^8.0 || ^9.0", + "pestphp/pest": "^1.7", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "orchestra/testbench": "^5.12.1 || ^6.7.2", + "pestphp/pest-dev-tools": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Laravel Plugin", + "keywords": [ + "framework", + "laravel", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2022-01-13T17:09:04+00:00" + }, + { + "name": "pestphp/pest-plugin-parallel", + "version": "v0.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-parallel.git", + "reference": "6da63df8878cc279b0ff78aa78a1c5f04f054677" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-parallel/zipball/6da63df8878cc279b0ff78aa78a1c5f04f054677", + "reference": "6da63df8878cc279b0ff78aa78a1c5f04f054677", + "shasum": "" + }, + "require": { + "brianium/paratest": "^6.3", + "pestphp/pest-plugin": "^1.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "laravel/framework": "<8.55", + "nunomaduro/collision": "<5.8", + "pestphp/pest": "<1.16" + }, + "require-dev": { + "pestphp/pest": "dev-master", + "pestphp/pest-dev-tools": "dev-master" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Parallel\\Plugin" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Autoload.php", + "build/RunnerWorker.php", + "build/BaseRunner.php" + ], + "psr-4": { + "Pest\\Parallel\\": "src/" + }, + "exclude-from-classmap": [ + "ParaTest\\Runners\\PHPUnit\\Worker\\RunnerWorker", + "ParaTest\\Runners\\PHPUnit\\BaseRunner" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Parallel Plugin", + "keywords": [ + "framework", + "parallel", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-parallel/tree/v0.2.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/lukeraymonddowning", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/octoper", + "type": "github" + }, + { + "url": "https://github.com/olivernybroe", + "type": "github" + }, + { + "url": "https://github.com/owenvoke", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2021-08-25T11:03:56+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "sebastian/diff v3 backport support for PHP 5.6+", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + }, + "time": "2020-10-14T08:32:19+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "bf7b9d2ee692b6df2a41017d6023a2fe732d240c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/bf7b9d2ee692b6df2a41017d6023a2fe732d240c", + "reference": "bf7b9d2ee692b6df2a41017d6023a2fe732d240c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-21T13:33:31+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.0.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "f2c1780607ec6502f2121d9729fd8150a655d337" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f2c1780607ec6502f2121d9729fd8150a655d337", + "reference": "f2c1780607ec6502f2121d9729fd8150a655d337", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.0.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-21T17:15:17+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^7.4 || ^8.0" + }, + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/crater/config/abilities.php b/crater/config/abilities.php new file mode 100644 index 0000000..278050a --- /dev/null +++ b/crater/config/abilities.php @@ -0,0 +1,420 @@ + [ + + // Customer + [ + "name" => "view customer", + "ability" => "view-customer", + "model" => Customer::class, + ], + [ + "name" => "create customer", + "ability" => "create-customer", + "model" => Customer::class, + "depends_on" => [ + 'view-customer', + 'view-custom-field', + ] + ], + [ + "name" => "edit customer", + "ability" => "edit-customer", + "model" => Customer::class, + "depends_on" => [ + 'view-customer', + 'view-custom-field', + ] + ], + [ + "name" => "delete customer", + "ability" => "delete-customer", + "model" => Customer::class, + "depends_on" => [ + 'view-customer', + ] + ], + + // Item + [ + "name" => "view item", + "ability" => "view-item", + "model" => Item::class, + ], + [ + "name" => "create item", + "ability" => "create-item", + "model" => Item::class, + "depends_on" => [ + 'view-item', + 'view-tax-type' + ] + ], + [ + "name" => "edit item", + "ability" => "edit-item", + "model" => Item::class, + "depends_on" => [ + 'view-item', + ] + ], + [ + "name" => "delete item", + "ability" => "delete-item", + "model" => Item::class, + "depends_on" => [ + 'view-item', + ] + ], + + // Tax Type + [ + "name" => "view tax type", + "ability" => "view-tax-type", + "model" => TaxType::class, + ], + [ + "name" => "create tax type", + "ability" => "create-tax-type", + "model" => TaxType::class, + "depends_on" => [ + 'view-tax-type', + ] + ], + [ + "name" => "edit tax type", + "ability" => "edit-tax-type", + "model" => TaxType::class, + "depends_on" => [ + 'view-tax-type', + ] + ], + [ + "name" => "delete tax type", + "ability" => "delete-tax-type", + "model" => TaxType::class, + "depends_on" => [ + 'view-tax-type', + ] + ], + + // Estimate + [ + "name" => "view estimate", + "ability" => "view-estimate", + "model" => Estimate::class, + ], + [ + "name" => "create estimate", + "ability" => "create-estimate", + "model" => Estimate::class, + "depends_on" => [ + 'view-estimate', + 'view-item', + 'view-tax-type', + 'view-customer', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "edit estimate", + "ability" => "edit-estimate", + "model" => Estimate::class, + "depends_on" => [ + 'view-item', + 'view-estimate', + 'view-tax-type', + 'view-customer', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "delete estimate", + "ability" => "delete-estimate", + "model" => Estimate::class, + "depends_on" => [ + 'view-estimate', + ] + ], + [ + "name" => "send estimate", + "ability" => "send-estimate", + "model" => Estimate::class, + ], + + // Invoice + [ + "name" => "view invoice", + "ability" => "view-invoice", + "model" => Invoice::class, + ], + [ + "name" => "create invoice", + "ability" => "create-invoice", + "model" => Invoice::class, + 'owner_only' => false, + "depends_on" => [ + 'view-item', + 'view-invoice', + 'view-tax-type', + 'view-customer', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "edit invoice", + "ability" => "edit-invoice", + "model" => Invoice::class, + "depends_on" => [ + 'view-item', + 'view-invoice', + 'view-tax-type', + 'view-customer', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "delete invoice", + "ability" => "delete-invoice", + "model" => Invoice::class, + "depends_on" => [ + 'view-invoice' + ] + ], + [ + "name" => "send invoice", + "ability" => "send-invoice", + "model" => Invoice::class, + ], + + // Recurring Invoice + [ + "name" => "view recurring invoice", + "ability" => "view-recurring-invoice", + "model" => RecurringInvoice::class, + ], + [ + "name" => "create recurring invoice", + "ability" => "create-recurring-invoice", + "model" => RecurringInvoice::class, + "depends_on" => [ + 'view-item', + 'view-recurring-invoice', + 'view-tax-type', + 'view-customer', + 'view-all-notes', + 'send-invoice' + ] + ], + [ + "name" => "edit recurring invoice", + "ability" => "edit-recurring-invoice", + "model" => RecurringInvoice::class, + "depends_on" => [ + 'view-item', + 'view-recurring-invoice', + 'view-tax-type', + 'view-customer', + 'view-all-notes', + 'send-invoice' + ] + ], + [ + "name" => "delete recurring invoice", + "ability" => "delete-recurring-invoice", + "model" => RecurringInvoice::class, + "depends_on" => [ + 'view-recurring-invoice', + ] + ], + + // Payment + [ + "name" => "view payment", + "ability" => "view-payment", + "model" => Payment::class, + ], + [ + "name" => "create payment", + "ability" => "create-payment", + "model" => Payment::class, + "depends_on" => [ + 'view-customer', + 'view-payment', + 'view-invoice', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "edit payment", + "ability" => "edit-payment", + "model" => Payment::class, + "depends_on" => [ + 'view-customer', + 'view-payment', + 'view-invoice', + 'view-custom-field', + 'view-all-notes' + ] + ], + [ + "name" => "delete payment", + "ability" => "delete-payment", + "model" => Payment::class, + "depends_on" => [ + 'view-payment', + ] + ], + [ + "name" => "send payment", + "ability" => "send-payment", + "model" => Payment::class, + ], + + // Expense + [ + "name" => "view expense", + "ability" => "view-expense", + "model" => Expense::class, + ], + [ + "name" => "create expense", + "ability" => "create-expense", + "model" => Expense::class, + "depends_on" => [ + 'view-customer', + 'view-expense', + 'view-custom-field', + ] + ], + [ + "name" => "edit expense", + "ability" => "edit-expense", + "model" => Expense::class, + "depends_on" => [ + 'view-customer', + 'view-expense', + 'view-custom-field', + ] + ], + [ + "name" => "delete expense", + "ability" => "delete-expense", + "model" => Expense::class, + "depends_on" => [ + 'view-expense', + ] + ], + + // Custom Field + [ + "name" => "view custom field", + "ability" => "view-custom-field", + "model" => CustomField::class, + ], + [ + "name" => "create custom field", + "ability" => "create-custom-field", + "model" => CustomField::class, + "depends_on" => [ + 'view-custom-field', + ] + ], + [ + "name" => "edit custom field", + "ability" => "edit-custom-field", + "model" => CustomField::class, + "depends_on" => [ + 'view-custom-field', + ] + ], + [ + "name" => "delete custom field", + "ability" => "delete-custom-field", + "model" => CustomField::class, + "depends_on" => [ + 'view-custom-field', + ] + ], + + // Financial Reports + [ + "name" => "view financial reports", + "ability" => "view-financial-reports", + "model" => null, + ], + + // Exchange Rate Provider + [ + "name" => "view exchange rate provider", + "ability" => "view-exchange-rate-provider", + "model" => ExchangeRateProvider::class, + 'owner_only' => false, + ], + [ + "name" => "create exchange rate provider", + "ability" => "create-exchange-rate-provider", + "model" => ExchangeRateProvider::class, + 'owner_only' => false, + "depends_on" => [ + 'view-exchange-rate-provider', + ] + ], + [ + "name" => "edit exchange rate provider", + "ability" => "edit-exchange-rate-provider", + "model" => ExchangeRateProvider::class, + 'owner_only' => false, + "depends_on" => [ + 'view-exchange-rate-provider', + ] + ], + [ + "name" => "delete exchange rate provider", + "ability" => "delete-exchange-rate-provider", + "model" => ExchangeRateProvider::class, + 'owner_only' => false, + "depends_on" => [ + 'view-exchange-rate-provider', + ] + ], + + // Settings + [ + "name" => "view company dashboard", + "ability" => "dashboard", + "model" => null, + ], + [ + "name" => "view all notes", + "ability" => "view-all-notes", + "model" => Note::class, + ], + [ + "name" => "manage notes", + "ability" => "manage-all-notes", + "model" => Note::class, + "depends_on" => [ + 'view-all-notes' + ] + ] + ] +]; diff --git a/crater/config/app.php b/crater/config/app.php new file mode 100644 index 0000000..473638b --- /dev/null +++ b/crater/config/app.php @@ -0,0 +1,226 @@ + 'Crater', + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services your application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + 'faker_locale' => 'en_US', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + + /* + * Laravel Framework Service Providers... + */ + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Notifications\NotificationServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + Lavary\Menu\ServiceProvider::class, + + /* + * Application Service Providers... + */ + Crater\Providers\AppServiceProvider::class, + Crater\Providers\AuthServiceProvider::class, + Crater\Providers\BroadcastServiceProvider::class, + Crater\Providers\EventServiceProvider::class, + Crater\Providers\RouteServiceProvider::class, + Crater\Providers\DropboxServiceProvider::class, + Crater\Providers\ViewServiceProvider::class, + Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + ], + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => [ + + 'App' => Illuminate\Support\Facades\App::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Bus' => Illuminate\Support\Facades\Bus::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Notification' => Illuminate\Support\Facades\Notification::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Http' => Illuminate\Support\Facades\Http::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'View' => Illuminate\Support\Facades\View::class, + 'Flash' => Laracasts\Flash\Flash::class, + // 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, + 'Pusher' => Pusher\Pusher::class, + 'Menu' => Lavary\Menu\Facade::class + ], +]; diff --git a/crater/config/auth.php b/crater/config/auth.php new file mode 100644 index 0000000..289cca1 --- /dev/null +++ b/crater/config/auth.php @@ -0,0 +1,130 @@ + [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + + 'api' => [ + 'driver' => 'token', + 'provider' => 'users', + 'hash' => false, + ], + + 'customer' => [ + 'driver' => 'session', + 'provider' => 'customers', + 'hash' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => \Crater\Models\User::class, + ], + + 'customers' => [ + 'driver' => 'eloquent', + 'model' => \Crater\Models\Customer::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => 'password_resets', + 'expire' => 60, + 'throttle' => 60, + ], + + 'customers' => [ + 'provider' => 'customers', + 'table' => 'password_resets', + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => 10800, + +]; diff --git a/crater/config/backup.php b/crater/config/backup.php new file mode 100644 index 0000000..d8a1455 --- /dev/null +++ b/crater/config/backup.php @@ -0,0 +1,241 @@ + [ + + /* + * The name of this application. You can use this name to monitor + * the backups. + */ + 'name' => env('APP_NAME', 'laravel-backup'), + + 'source' => [ + + 'files' => [ + + /* + * The list of directories and files that will be included in the backup. + */ + 'include' => [ + base_path(), + ], + + /* + * These directories and files will be excluded from the backup. + * + * Directories used by the backup process will automatically be excluded. + */ + 'exclude' => [ + base_path('vendor'), + base_path('node_modules'), + base_path('.git'), + ], + + /* + * Determines if symlinks should be followed. + */ + 'follow_links' => false, + + /* + * Determines if it should avoid unreadable folders. + */ + 'ignore_unreadable_directories' => false, + ], + + /* + * The names of the connections to the databases that should be backed up + * MySQL, PostgreSQL, SQLite and Mongo databases are supported. + * + * The content of the database dump may be customized for each connection + * by adding a 'dump' key to the connection settings in config/database.php. + * E.g. + * 'mysql' => [ + * ... + * 'dump' => [ + * 'excludeTables' => [ + * 'table_to_exclude_from_backup', + * 'another_table_to_exclude' + * ] + * ], + * ], + * + * If you are using only InnoDB tables on a MySQL server, you can + * also supply the useSingleTransaction option to avoid table locking. + * + * E.g. + * 'mysql' => [ + * ... + * 'dump' => [ + * 'useSingleTransaction' => true, + * ], + * ], + * + * For a complete list of available customization options, see https://github.com/spatie/db-dumper + */ + 'databases' => [ + 'mysql', + ], + ], + + /* + * The database dump can be compressed to decrease diskspace usage. + * + * Out of the box Laravel-backup supplies + * Spatie\DbDumper\Compressors\GzipCompressor::class. + * + * You can also create custom compressor. More info on that here: + * https://github.com/spatie/db-dumper#using-compression + * + * If you do not want any compressor at all, set it to null. + */ + 'database_dump_compressor' => null, + + 'destination' => [ + + /* + * The filename prefix used for the backup zip file. + */ + 'filename_prefix' => '', + + /* + * The disk names on which the backups will be stored. + */ + 'disks' => [ + 'local', + ], + ], + + /* + * The directory where the temporary files will be stored. + */ + 'temporary_directory' => storage_path('app/backup-temp'), + ], + + /* + * You can get notified when specific events occur. Out of the box you can use 'mail' and 'slack'. + * For Slack you need to install guzzlehttp/guzzle and laravel/slack-notification-channel. + * + * You can also use your own notification classes, just make sure the class is named after one of + * the `Spatie\Backup\Events` classes. + */ + 'notifications' => [ + + 'notifications' => [ + \Spatie\Backup\Notifications\Notifications\BackupHasFailed::class => [], + \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFound::class => [], + \Spatie\Backup\Notifications\Notifications\CleanupHasFailed::class => [], + \Spatie\Backup\Notifications\Notifications\BackupWasSuccessful::class => [], + \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFound::class => [], + \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessful::class => [], + ], + + /* + * Here you can specify the notifiable to which the notifications should be sent. The default + * notifiable will use the variables specified in this config file. + */ + 'notifiable' => \Spatie\Backup\Notifications\Notifiable::class, + + 'mail' => [ + 'to' => 'your@example.com', + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + ], + + 'slack' => [ + 'webhook_url' => '', + + /* + * If this is set to null the default channel of the webhook will be used. + */ + 'channel' => null, + + 'username' => null, + + 'icon' => null, + + ], + ], + + /* + * Here you can specify which backups should be monitored. + * If a backup does not meet the specified requirements the + * UnHealthyBackupWasFound event will be fired. + */ + 'monitor_backups' => [ + [ + 'name' => env('APP_NAME', 'laravel-backup'), + 'disks' => ['local'], + 'health_checks' => [ + \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1, + \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000, + ], + ], + + /* + [ + 'name' => 'name of the second app', + 'disks' => ['local', 's3'], + 'health_checks' => [ + \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1, + \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000, + ], + ], + */ + ], + + 'cleanup' => [ + /* + * The strategy that will be used to cleanup old backups. The default strategy + * will keep all backups for a certain amount of days. After that period only + * a daily backup will be kept. After that period only weekly backups will + * be kept and so on. + * + * No matter how you configure it the default strategy will never + * delete the newest backup. + */ + 'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class, + + 'default_strategy' => [ + + /* + * The number of days for which backups must be kept. + */ + 'keep_all_backups_for_days' => 7, + + /* + * The number of days for which daily backups must be kept. + */ + 'keep_daily_backups_for_days' => 16, + + /* + * The number of weeks for which one weekly backup must be kept. + */ + 'keep_weekly_backups_for_weeks' => 8, + + /* + * The number of months for which one monthly backup must be kept. + */ + 'keep_monthly_backups_for_months' => 4, + + /* + * The number of years for which one yearly backup must be kept. + */ + 'keep_yearly_backups_for_years' => 2, + + /* + * After cleaning up the backups remove the oldest backup until + * this amount of megabytes has been reached. + */ + 'delete_oldest_backups_when_using_more_megabytes_than' => 5000, + ], + ], + + 'queue' => [ + 'name' => env('BACKUP_QUEUE_NAME', 'backup'), + ], + +]; diff --git a/crater/config/broadcasting.php b/crater/config/broadcasting.php new file mode 100644 index 0000000..374f196 --- /dev/null +++ b/crater/config/broadcasting.php @@ -0,0 +1,59 @@ + env('BROADCAST_DRIVER', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => 'ap2', + 'encrypted' => true, + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; diff --git a/crater/config/cache.php b/crater/config/cache.php new file mode 100644 index 0000000..65c3beb --- /dev/null +++ b/crater/config/cache.php @@ -0,0 +1,93 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'cache', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'), +]; diff --git a/crater/config/compile.php b/crater/config/compile.php new file mode 100644 index 0000000..04807ea --- /dev/null +++ b/crater/config/compile.php @@ -0,0 +1,35 @@ + [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Compiled File Providers + |-------------------------------------------------------------------------- + | + | Here you may list service providers which define a "compiles" function + | that returns additional files that should be compiled, providing an + | easy way to get common files from any packages you are utilizing. + | + */ + + 'providers' => [ + // + ], + +]; diff --git a/crater/config/cors.php b/crater/config/cors.php new file mode 100644 index 0000000..558369d --- /dev/null +++ b/crater/config/cors.php @@ -0,0 +1,34 @@ + ['api/*'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/crater/config/crater.php b/crater/config/crater.php new file mode 100644 index 0000000..4de9810 --- /dev/null +++ b/crater/config/crater.php @@ -0,0 +1,507 @@ + '7.4.0', + + /* + * Minimum mysql version. + */ + + 'min_mysql_version' => '5.7.7', + + /* + * Minimum mariadb version. + */ + + 'min_mariadb_version' => '10.2.7', + + /* + * Minimum pgsql version. + */ + + 'min_pgsql_version' => '9.2.0', + + /* + * Minimum sqlite version. + */ + + 'min_sqlite_version' => '3.24.0', + + /* + * Marketplace url. + */ + 'base_url' => 'https://craterapp.com', + + /* + * List of languages supported by Crater. + */ + 'languages' => [ + ["code" => "ar", "name" => "Arabic"], + ["code" => "nl", "name" => "Dutch"], + ["code" => "en", "name" => "English"], + ["code" => "fr", "name" => "French"], + ["code" => "de", "name" => "German"], + ["code" => "ja", "name" => "Japanese"], + ["code" => "it", "name" => "Italian"], + ["code" => "lv", "name" => "Latvian"], + ["code" => "pl", "name" => "Polish"], + ["code" => "pt_BR", "name" => "Portuguese (Brazilian)"], + ["code" => "sr", "name" => "Serbian Latin"], + ["code" => "ko", "name" => "Korean"], + ["code" => "es", "name" => "Spanish"], + ["code" => "sv", "name" => "Svenska"], + ["code" => "sk", "name" => "Slovak"], + ["code" => "vi", "name" => "Tiếng Việt"], + ["code" => "cs", "name" => "Czech"], + ["code" => "el", "name" => "Greek"], + ["code" => "hr", "name" => "Crotian"], + ["code" => "th", "name" => "ไทย"], + ], + + /* + * List of Fiscal Years + */ + 'fiscal_years' => [ + ['key' => 'january-december' , 'value' => '1-12'], + ['key' => 'february-january' , 'value' => '2-1'], + ['key' => 'march-february' , 'value' => '3-2'], + ['key' => 'april-march' , 'value' => '4-3'], + ['key' => 'may-april' , 'value' => '5-4'], + ['key' => 'june-may' , 'value' => '6-5'], + ['key' => 'july-june' , 'value' => '7-6'], + ['key' => 'august-july' , 'value' => '8-7'], + ['key' => 'september-august' , 'value' => '9-8'], + ['key' => 'october-september', 'value' => '10-9'], + ['key' => 'november-october' , 'value' => '11-10'], + ['key' => 'december-november', 'value' => '12-11'], + ], + + /* + * List of convert estimate options + */ + 'convert_estimate_options' => [ + ['key' => 'settings.preferences.no_action', 'value' => 'no_action'], + ['key' => 'settings.preferences.delete_estimate', 'value' => 'delete_estimate'], + ['key' => 'settings.preferences.mark_estimate_as_accepted', 'value' => 'mark_estimate_as_accepted'], + ], + + /* + * List of retrospective edits + */ + 'retrospective_edits' => [ + ['key' => 'settings.preferences.allow', 'value' => 'allow'], + ['key' => 'settings.preferences.disable_on_invoice_partial_paid', 'value' => 'disable_on_invoice_partial_paid'], + ['key' => 'settings.preferences.disable_on_invoice_paid', 'value' => 'disable_on_invoice_paid'], + ['key' => 'settings.preferences.disable_on_invoice_sent', 'value' => 'disable_on_invoice_sent'], + ], + + /* + * List of setting menu + */ + 'setting_menu' => [ + [ + 'title' => 'settings.menu_title.account_settings', + 'group' => '', + 'name' => 'Account Settings', + 'link' => '/admin/settings/account-settings', + 'icon' => 'UserIcon', + 'owner_only' => false, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.company_information', + 'group' => '', + 'name' => 'Company information', + 'link' => '/admin/settings/company-info', + 'icon' => 'OfficeBuildingIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.preferences', + 'group' => '', + 'name' => 'Preferences', + 'link' => '/admin/settings/preferences', + 'icon' => 'CogIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.customization', + 'group' => '', + 'name' => 'Customization', + 'link' => '/admin/settings/customization', + 'icon' => 'PencilAltIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.roles.title', + 'group' => '', + 'name' => 'Roles', + 'link' => '/admin/settings/roles-settings', + 'icon' => 'UserGroupIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.exchange_rate', + 'group' => '', + 'name' => 'Exchange Rate Provider', + 'link' => '/admin/settings/exchange-rate-provider', + 'icon' => 'CashIcon', + 'owner_only' => false, + 'ability' => 'view-exchange-rate-provider', + 'model' => ExchangeRateProvider::class + ], + [ + 'title' => 'settings.menu_title.notifications', + 'group' => '', + 'name' => 'Notifications', + 'link' => '/admin/settings/notifications', + 'icon' => 'BellIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.tax_types', + 'group' => '', + 'name' => 'Tax types', + 'link' => '/admin/settings/tax-types', + 'icon' => 'CheckCircleIcon', + 'owner_only' => false, + 'ability' => 'view-tax-type', + 'model' => TaxType::class + ], + [ + 'title' => 'settings.menu_title.payment_modes', + 'group' => '', + 'name' => 'Payment modes', + 'link' => '/admin/settings/payment-mode', + 'icon' => 'CreditCardIcon', + 'owner_only' => false, + 'ability' => 'view-payment', + 'model' => Payment::class + ], + [ + 'title' => 'settings.menu_title.custom_fields', + 'group' => '', + 'name' => 'Custom fields', + 'link' => '/admin/settings/custom-fields', + 'icon' => 'CubeIcon', + 'owner_only' => false, + 'ability' => 'view-custom-field', + 'model' => CustomField::class + ], + [ + 'title' => 'settings.menu_title.notes', + 'group' => '', + 'name' => 'Notes', + 'link' => '/admin/settings/notes', + 'icon' => 'ClipboardCheckIcon', + 'owner_only' => false, + 'ability' => 'view-all-notes', + 'model' => Note::class + ], + [ + 'title' => 'settings.menu_title.expense_category', + 'group' => '', + 'name' => 'Expense Category', + 'link' => '/admin/settings/expense-category', + 'icon' => 'ClipboardListIcon', + 'owner_only' => false, + 'ability' => 'view-expense', + 'model' => Expense::class + ], + [ + 'title' => 'settings.mail.mail_config', + 'group' => '', + 'name' => 'Mail Configuration', + 'link' => '/admin/settings/mail-configuration', + 'icon' => 'MailIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.file_disk', + 'group' => '', + 'name' => 'File Disk', + 'link' => '/admin/settings/file-disk', + 'icon' => 'FolderIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.backup', + 'group' => '', + 'name' => 'Backup', + 'link' => '/admin/settings/backup', + 'icon' => 'DatabaseIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'settings.menu_title.update_app', + 'group' => '', + 'name' => 'Update App', + 'link' => '/admin/settings/update-app', + 'icon' => 'RefreshIcon', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + ], + + /* + * List of main menu + */ + 'main_menu' => [ + [ + 'title' => 'navigation.dashboard', + 'group' => 1, + 'link' => '/admin/dashboard', + 'icon' => 'HomeIcon', + 'name' => 'Dashboard', + 'owner_only' => false, + 'ability' => 'dashboard', + 'model' => '' + ], + [ + 'title' => 'navigation.customers', + 'group' => 1, + 'link' => '/admin/customers', + 'icon' => 'UserIcon', + 'name' => 'Customers', + 'owner_only' => false, + 'ability' => 'view-customer', + 'model' => Customer::class + ], + [ + 'title' => 'navigation.items', + 'group' => 1, + 'link' => '/admin/items', + 'icon' => 'StarIcon', + 'name' => 'Items', + 'owner_only' => false, + 'ability' => 'view-item', + 'model' => Item::class + ], + [ + 'title' => 'navigation.estimates', + 'group' => 2, + 'link' => '/admin/estimates', + 'icon' => 'DocumentIcon', + 'name' => 'Estimates', + 'owner_only' => false, + 'ability' => 'view-estimate', + 'model' => Estimate::class + ], + [ + 'title' => 'navigation.invoices', + 'group' => 2, + 'link' => '/admin/invoices', + 'icon' => 'DocumentTextIcon', + 'name' => 'Invoices', + 'owner_only' => false, + 'ability' => 'view-invoice', + 'model' => Invoice::class + ], + [ + 'title' => 'navigation.recurring-invoices', + 'group' => 2, + 'link' => '/admin/recurring-invoices', + 'icon' => 'DocumentTextIcon', + 'name' => 'Recurring Invoices', + 'owner_only' => false, + 'ability' => 'view-recurring-invoice', + 'model' => RecurringInvoice::class + ], + [ + 'title' => 'navigation.payments', + 'group' => 2, + 'link' => '/admin/payments', + 'icon' => 'CreditCardIcon', + 'name' => 'Payments', + 'owner_only' => false, + 'ability' => 'view-payment', + 'model' => Payment::class + ], + [ + 'title' => 'navigation.expenses', + 'group' => 2, + 'link' => '/admin/expenses', + 'icon' => 'CalculatorIcon', + 'name' => 'Expenses', + 'owner_only' => false, + 'ability' => 'view-expense', + 'model' => Expense::class + ], + [ + 'title' => 'navigation.modules', + 'group' => 3, + 'link' => '/admin/modules', + 'icon' => 'PuzzleIcon', + 'name' => 'Modules', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'navigation.users', + 'group' => 3, + 'link' => '/admin/users', + 'icon' => 'UsersIcon', + 'name' => 'Users', + 'owner_only' => true, + 'ability' => '', + 'model' => '' + ], + [ + 'title' => 'navigation.reports', + 'group' => 3, + 'link' => '/admin/reports', + 'icon' => 'ChartBarIcon', + 'name' => 'Reports', + 'owner_only' => false, + 'ability' => 'view-financial-reports', + 'model' => '' + ], + [ + 'title' => 'navigation.settings', + 'group' => 3, + 'link' => '/admin/settings', + 'icon' => 'CogIcon', + 'name' => 'Settings', + 'owner_only' => false, + 'ability' => '', + 'model' => '' + ], + ], + + /* + * List of customer portal menu + */ + 'customer_menu' => [ + [ + 'title' => 'Dashboard', + 'link' => '/customer/dashboard', + 'icon' => '', + 'name' => '', + 'ability' => '', + 'owner_only' => false, + 'group' => '', + 'model' => '' + ], + [ + 'title' => 'Invoices', + 'link' => '/customer/invoices', + 'icon' => '', + 'name' => '', + 'ability' => '', + 'owner_only' => false, + 'group' => '', + 'model' => '' + ], + [ + 'title' => 'Estimates', + 'link' => '/customer/estimates', + 'icon' => '', + 'name' => '', + 'owner_only' => false, + 'ability' => '', + 'group' => '', + 'model' => '' + ], + [ + 'title' => 'Payments', + 'link' => '/customer/payments', + 'icon' => '', + 'name' => '', + 'owner_only' => false, + 'ability' => '', + 'group' => '', + 'model' => '' + ], + [ + 'title' => 'Settings', + 'link' => '/customer/settings', + 'icon' => '', + 'name' => '', + 'owner_only' => false, + 'ability' => '', + 'group' => '', + 'model' => '' + ], + ], + + /* + * List of recurring invoice status + */ + 'recurring_invoice_status' => [ + 'create_status' => [ + ['key' => 'settings.preferences.active', 'value' => 'ACTIVE'], + ['key' => 'settings.preferences.on_hold', 'value' => 'ON_HOLD'] + ], + 'update_status' => [ + ['key' => 'settings.preferences.active', 'value' => 'ACTIVE'], + ['key' => 'settings.preferences.on_hold', 'value' => 'ON_HOLD'], + ['key' => 'settings.preferences.completed', 'value' => 'COMPLETED'], + ] + ], + + /* + * List of exchange rate provider (currency converter server's) + */ + 'currency_converter_servers' => [ + ['key' => 'settings.preferences.premium', 'value' => 'PREMIUM'], + ['key' => 'settings.preferences.prepaid', 'value' => 'PREPAID'], + ['key' => 'settings.preferences.free', 'value' => 'FREE'], + ['key' => 'settings.preferences.dedicated', 'value' => 'DEDICATED'], + ], + + /* + * List of exchange rate drivers + */ + 'exchange_rate_drivers' => [ + ['key' => 'settings.exchange_rate.currency_converter', 'value' => 'currency_converter'], + ['key' => 'settings.exchange_rate.currency_freak', 'value' => 'currency_freak'], + ['key' => 'settings.exchange_rate.currency_layer', 'value' => 'currency_layer'], + ['key' => 'settings.exchange_rate.open_exchange_rate', 'value' => 'open_exchange_rate'], + ], + + /* + * List of Custom field supported models + */ + 'custom_field_models' => [ + 'Customer', + 'Estimate', + 'Invoice', + 'Payment', + 'Expense', + ] +]; diff --git a/crater/config/database.php b/crater/config/database.php new file mode 100644 index 0000000..1f971c8 --- /dev/null +++ b/crater/config/database.php @@ -0,0 +1,129 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => false, + 'engine' => null, + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'schema' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'client' => 'predis', + + 'default' => [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => 0, + 'read_write_timeout' => 60, + ], + 'cache' => [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_CACHE_DB', 1), + ], + ], + +]; diff --git a/crater/config/dompdf.php b/crater/config/dompdf.php new file mode 100644 index 0000000..2203d14 --- /dev/null +++ b/crater/config/dompdf.php @@ -0,0 +1,244 @@ + false, // Throw an Exception on warnings from dompdf + 'orientation' => 'portrait', + 'defines' => [ + /** + * The location of the DOMPDF font directory + * + * The location of the directory where DOMPDF will store fonts and font metrics + * Note: This directory must exist and be writable by the webserver process. + * *Please note the trailing slash.* + * + * Notes regarding fonts: + * Additional .afm font metrics can be added by executing load_font.php from command line. + * + * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must + * be embedded in the pdf file or the PDF may not display correctly. This can significantly + * increase file size unless font subsetting is enabled. Before embedding a font please + * review your rights under the font license. + * + * Any font specification in the source HTML is translated to the closest font available + * in the font directory. + * + * The pdf standard "Base 14 fonts" are: + * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique, + * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, + * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic, + * Symbol, ZapfDingbats. + */ + "font_dir" => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782) + + /** + * The location of the DOMPDF font cache directory + * + * This directory contains the cached font metrics for the fonts used by DOMPDF. + * This directory can be the same as DOMPDF_FONT_DIR + * + * Note: This directory must exist and be writable by the webserver process. + */ + "font_cache" => storage_path('fonts/'), + + /** + * The location of a temporary directory. + * + * The directory specified must be writeable by the webserver process. + * The temporary directory is required to download remote images and when + * using the PFDLib back end. + */ + "temp_dir" => sys_get_temp_dir(), + + /** + * ==== IMPORTANT ==== + * + * dompdf's "chroot": Prevents dompdf from accessing system files or other + * files on the webserver. All local files opened by dompdf must be in a + * subdirectory of this directory. DO NOT set it to '/' since this could + * allow an attacker to use dompdf to read any files on the server. This + * should be an absolute path. + * This is only checked on command line call by dompdf.php, but not by + * direct class use like: + * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); + */ + "chroot" => realpath(base_path()), + + /** + * Whether to enable font subsetting or not. + */ + "enable_font_subsetting" => false, + + /** + * The PDF rendering backend to use + * + * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and + * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will + * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link + * Canvas_Factory} ultimately determines which rendering class to instantiate + * based on this setting. + * + * Both PDFLib & CPDF rendering backends provide sufficient rendering + * capabilities for dompdf, however additional features (e.g. object, + * image and font support, etc.) differ between backends. Please see + * {@link PDFLib_Adapter} for more information on the PDFLib backend + * and {@link CPDF_Adapter} and lib/class.pdf.php for more information + * on CPDF. Also see the documentation for each backend at the links + * below. + * + * The GD rendering backend is a little different than PDFLib and + * CPDF. Several features of CPDF and PDFLib are not supported or do + * not make any sense when creating image files. For example, + * multiple pages are not supported, nor are PDF 'objects'. Have a + * look at {@link GD_Adapter} for more information. GD support is + * experimental, so use it at your own risk. + * + * @link http://www.pdflib.com + * @link http://www.ros.co.nz/pdf + * @link http://www.php.net/image + */ + "pdf_backend" => "CPDF", + + /** + * PDFlib license key + * + * If you are using a licensed, commercial version of PDFlib, specify + * your license key here. If you are using PDFlib-Lite or are evaluating + * the commercial version of PDFlib, comment out this setting. + * + * @link http://www.pdflib.com + * + * If pdflib present in web server and auto or selected explicitly above, + * a real license code must exist! + */ + //"DOMPDF_PDFLIB_LICENSE" => "your license key here", + + /** + * html target media view which should be rendered into pdf. + * List of types and parsing rules for future extensions: + * http://www.w3.org/TR/REC-html40/types.html + * screen, tty, tv, projection, handheld, print, braille, aural, all + * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3. + * Note, even though the generated pdf file is intended for print output, + * the desired content might be different (e.g. screen or projection view of html file). + * Therefore allow specification of content here. + */ + "default_media_type" => "screen", + + /** + * The default paper size. + * + * North America standard is "letter"; other countries generally "a4" + * + * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.) + */ + "default_paper_size" => "a4", + + /** + * The default font family + * + * Used if no suitable fonts can be found. This must exist in the font folder. + * @var string + */ + "default_font" => "DejaVu Sans", + + /** + * Image DPI setting + * + * This setting determines the default DPI setting for images and fonts. The + * DPI may be overridden for inline images by explicitly setting the + * image's width & height style attributes (i.e. if the image's native + * width is 600 pixels and you specify the image's width as 72 points, + * the image will have a DPI of 600 in the rendered PDF. The DPI of + * background images can not be overridden and is controlled entirely + * via this parameter. + * + * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI). + * If a size in html is given as px (or without unit as image size), + * this tells the corresponding size in pt. + * This adjusts the relative sizes to be similar to the rendering of the + * html page in a reference browser. + * + * In pdf, always 1 pt = 1/72 inch + * + * Rendering resolution of various browsers in px per inch: + * Windows Firefox and Internet Explorer: + * SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:? + * Linux Firefox: + * about:config *resolution: Default:96 + * (xorg screen dimension in mm and Desktop font dpi settings are ignored) + * + * Take care about extra font/image zoom factor of browser. + * + * In images, size in pixel attribute, img css style, are overriding + * the real image dimension in px for rendering. + * + * @var int + */ + "dpi" => 96, + + /** + * Enable inline PHP + * + * If this setting is set to true then DOMPDF will automatically evaluate + * inline PHP contained within tags. + * + * Enabling this for documents you do not trust (e.g. arbitrary remote html + * pages) is a security risk. Set this option to false if you wish to process + * untrusted documents. + * + * @var bool + */ + "enable_php" => false, + + /** + * Enable inline Javascript + * + * If this setting is set to true then DOMPDF will automatically insert + * JavaScript code contained within tags. + * + * @var bool + */ + "enable_javascript" => true, + + /** + * Enable remote file access + * + * If this setting is set to true, DOMPDF will access remote sites for + * images and CSS files as required. + * This is required for part of test case www/test/image_variants.html through www/examples.php + * + * Attention! + * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and + * allowing remote access to dompdf.php or on allowing remote html code to be passed to + * $dompdf = new DOMPDF(, $dompdf->load_html(..., + * This allows anonymous users to download legally doubtful internet content which on + * tracing back appears to being downloaded by your server, or allows malicious php code + * in remote html pages to be executed by your server with your account privileges. + * + * @var bool + */ + "enable_remote" => true, + + /** + * A ratio applied to the fonts height to be more like browsers' line height + */ + "font_height_ratio" => 1.1, + + /** + * Use the more-than-experimental HTML5 Lib parser + */ + "enable_html5_parser" => true, + ], + + +]; diff --git a/crater/config/filesystems.php b/crater/config/filesystems.php new file mode 100644 index 0000000..f265b6f --- /dev/null +++ b/crater/config/filesystems.php @@ -0,0 +1,116 @@ + env('FILESYSTEM_DRIVER', 'local'), + + /* + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | + */ + + 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been setup for each driver as an example of the required options. + | + | Supported Drivers: "local", "ftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_KEY'), + 'secret' => env('AWS_SECRET'), + 'region' => env('AWS_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'root' => env('AWS_ROOT'), + ], + + 'media' => [ + 'driver' => 'local', + 'root' => public_path('media'), + ], + + 'doSpaces' => [ + 'type' => 'AwsS3', + 'driver' => 's3', + 'key' => env('DO_SPACES_KEY'), + 'secret' => env('DO_SPACES_SECRET'), + 'region' => env('DO_SPACES_REGION'), + 'bucket' => env('DO_SPACES_BUCKET'), + 'root' => env('DO_SPACES_ROOT'), + 'endpoint' => env('DO_SPACES_ENDPOINT'), + 'use_path_style_endpoint' => false, + ], + + 'dropbox' => [ + 'driver' => 'dropbox', + 'type' => 'DropboxV2', + 'token' => env('DROPBOX_TOKEN'), + 'key' => env('DROPBOX_KEY'), + 'secret' => env('DROPBOX_SECRET'), + 'app' => env('DROPBOX_APP'), + 'root' => env('DROPBOX_ROOT'), + ], + + 'views' => [ + 'driver' => 'local', + 'root' => resource_path('views'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/crater/config/hashids.php b/crater/config/hashids.php new file mode 100644 index 0000000..cd1b64b --- /dev/null +++ b/crater/config/hashids.php @@ -0,0 +1,77 @@ + 'main', + + /* + |-------------------------------------------------------------------------- + | Hashids Connections + |-------------------------------------------------------------------------- + | + | Here are each of the connections setup for your application. Example + | configuration has been included, but you may add as many connections as + | you would like. + | + */ + + 'connections' => [ + Invoice::class => [ + 'salt' => Invoice::class.config('app.key'), + 'length' => '20', + 'alphabet' => 'XKyIAR7mgt8jD2vbqPrOSVenNGpiYLx4M61T', + ], + Estimate::class => [ + 'salt' => Estimate::class.config('app.key'), + 'length' => '20', + 'alphabet' => 'yLJWP79M8rYVqbn1NXjulO6IUDdvekRQGo40', + ], + Payment::class => [ + 'salt' => Payment::class.config('app.key'), + 'length' => '20', + 'alphabet' => 'asqtW3eDRIxB65GYl7UVLS1dybn9XrKTZ4zO', + ], + Company::class => [ + 'salt' => Company::class.config('app.key'), + 'length' => '20', + 'alphabet' => 's0DxOFtEYEnuKPmP08Ch6A1iHlLmBTBVWms5', + ], + EmailLog::class => [ + 'salt' => EmailLog::class.config('app.key'), + 'length' => '20', + 'alphabet' => 'BRAMEz5str5UVe9oCqzoYY2oKgUi8wQQSmrR', + ], + Transaction::class => [ + 'salt' => Transaction::class.config('app.key'), + 'length' => '20', + 'alphabet' => 'ADyQWE8mgt7jF2vbnPrKLJenHVpiUIq4M12T', + ], + ], +]; diff --git a/crater/config/hashing.php b/crater/config/hashing.php new file mode 100644 index 0000000..00ed9e7 --- /dev/null +++ b/crater/config/hashing.php @@ -0,0 +1,45 @@ + 'bcrypt', + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 10), + ], + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + 'argon' => [ + 'memory' => 1024, + 'threads' => 2, + 'time' => 2, + ], +]; diff --git a/crater/config/image.php b/crater/config/image.php new file mode 100644 index 0000000..6798381 --- /dev/null +++ b/crater/config/image.php @@ -0,0 +1,20 @@ + 'gd', + +]; diff --git a/crater/config/installer.php b/crater/config/installer.php new file mode 100755 index 0000000..012d3fa --- /dev/null +++ b/crater/config/installer.php @@ -0,0 +1,51 @@ + [ + 'minPhpVersion' => '7.4.0', + ], + 'final' => [ + 'key' => true, + 'publish' => false, + ], + 'requirements' => [ + 'php' => [ + 'openssl', + 'pdo', + 'mbstring', + 'tokenizer', + 'JSON', + 'cURL', + 'zip', + ], + 'apache' => [ + 'mod_rewrite', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Folders Permissions + |-------------------------------------------------------------------------- + | + | This is the default Laravel folders permissions, if your application + | requires more permissions just add them to the array list below. + | + */ + 'permissions' => [ + 'storage/framework/' => '775', + 'storage/logs/' => '775', + 'bootstrap/cache/' => '775', + ], +]; diff --git a/crater/config/logging.php b/crater/config/logging.php new file mode 100644 index 0000000..c179887 --- /dev/null +++ b/crater/config/logging.php @@ -0,0 +1,80 @@ + env('LOG_CHANNEL', 'stack'), + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | + */ + 'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['daily'], + ], + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => 'debug', + ], + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => 'debug', + 'days' => 14, + ], + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => 'Laravel Log', + 'emoji' => ':boom:', + 'level' => 'critical', + ], + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => 'debug', + 'handler' => SyslogUdpHandler::class, + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + ], + ], + 'stderr' => [ + 'driver' => 'monolog', + 'handler' => StreamHandler::class, + 'with' => [ + 'stream' => 'php://stderr', + ], + ], + 'syslog' => [ + 'driver' => 'syslog', + 'level' => 'debug', + ], + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => 'debug', + ], + ], +]; diff --git a/crater/config/mail.php b/crater/config/mail.php new file mode 100644 index 0000000..9f1ff07 --- /dev/null +++ b/crater/config/mail.php @@ -0,0 +1,146 @@ + env('MAIL_DRIVER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Mailgun mail service which will provide reliable deliveries. + | + */ + + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to deliver e-mails to + | users of the application. Like the host we have set this value to + | stay compatible with the Mailgun e-mail application by default. + | + */ + + 'port' => env('MAIL_PORT', 587), + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'admin@crater.in'), + 'name' => env('MAIL_FROM_NAME', 'Crater'), + ], + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => env('MAIL_USERNAME'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Password + |-------------------------------------------------------------------------- + | + | Here you may set the password required by your SMTP server to send out + | messages from your application. This will be given to the server on + | connection so that the application will be able to send messages. + | + */ + + 'password' => env('MAIL_PASSWORD'), + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Log Channel + |-------------------------------------------------------------------------- + | + | If you are using the "log" driver, you may specify the logging channel + | if you prefer to keep mail messages separate from other log entries + | for simpler reading. Otherwise, the default channel will be used. + | + */ + + 'log_channel' => env('MAIL_LOG_CHANNEL'), +]; diff --git a/crater/config/media-library.php b/crater/config/media-library.php new file mode 100644 index 0000000..0391faa --- /dev/null +++ b/crater/config/media-library.php @@ -0,0 +1,166 @@ + env('MEDIA_DISK', 'public'), + + /* + * The maximum file size of an item in bytes. + * Adding a larger file will result in an exception. + */ + 'max_file_size' => 1024 * 1024 * 10, + + /* + * This queue will be used to generate derived and responsive images. + * Leave empty to use the default queue. + */ + 'queue_name' => '', + + /* + * The fully qualified class name of the media model. + */ + 'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class, + + 'remote' => [ + /* + * Any extra headers that should be included when uploading media to + * a remote disk. Even though supported headers may vary between + * different drivers, a sensible default has been provided. + * + * Supported by S3: CacheControl, Expires, StorageClass, + * ServerSideEncryption, Metadata, ACL, ContentEncoding + */ + 'extra_headers' => [ + 'CacheControl' => 'max-age=604800', + ], + ], + + 'responsive_images' => [ + + /* + * This class is responsible for calculating the target widths of the responsive + * images. By default we optimize for filesize and create variations that each are 20% + * smaller than the previous one. More info in the documentation. + * + * https://docs.spatie.be/laravel-medialibrary/v8/advanced-usage/generating-responsive-images + */ + 'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class, + + /* + * By default rendering media to a responsive image will add some javascript and a tiny placeholder. + * This ensures that the browser can already determine the correct layout. + */ + 'use_tiny_placeholders' => true, + + /* + * This class will generate the tiny placeholder used for progressive image loading. By default + * the medialibrary will use a tiny blurred jpg image. + */ + 'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class, + ], + + /* + * When converting Media instances to response the medialibrary will add + * a `loading` attribute to the `img` tag. Here you can set the default + * value of that attribute. + * + * Possible values: 'auto', 'lazy' and 'eager, + * + * More info: https://css-tricks.com/native-lazy-loading/ + */ + 'default_loading_attribute_value' => 'auto', + + /* + * This is the class that is responsible for naming conversion files. By default, + * it will use the filename of the original and concatenate the conversion name to it. + */ + 'conversion_file_namer' => \Spatie\MediaLibrary\Conversions\DefaultConversionFileNamer::class, + + /* + * The class that contains the strategy for determining a media file's path. + */ + 'path_generator' => \Crater\Generators\CustomPathGenerator::class, + + /* + * When urls to files get generated, this class will be called. Use the default + * if your files are stored locally above the site root or on s3. + */ + 'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class, + + /* + * Whether to activate versioning when urls to files get generated. + * When activated, this attaches a ?v=xx query string to the URL. + */ + 'version_urls' => false, + + /* + * The media library will try to optimize all converted images by removing + * metadata and applying a little bit of compression. These are + * the optimizers that will be used by default. + */ + 'image_optimizers' => [ + Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [ + '--strip-all', // this strips out all text information such as comments and EXIF data + '--all-progressive', // this will make sure the resulting image is a progressive one + ], + Spatie\ImageOptimizer\Optimizers\Pngquant::class => [ + '--force', // required parameter for this package + ], + Spatie\ImageOptimizer\Optimizers\Optipng::class => [ + '-i0', // this will result in a non-interlaced, progressive scanned image + '-o2', // this set the optimization level to two (multiple IDAT compression trials) + '-quiet', // required parameter for this package + ], + Spatie\ImageOptimizer\Optimizers\Svgo::class => [ + '--disable=cleanupIDs', // disabling because it is known to cause troubles + ], + Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [ + '-b', // required parameter for this package + '-O3', // this produces the slowest but best results + ], + ], + + /* + * These generators will be used to create an image of media files. + */ + 'image_generators' => [ + Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class, + Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class, + Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class, + Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class, + Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class, + ], + + /* + * The engine that should perform the image conversions. + * Should be either `gd` or `imagick`. + */ + 'image_driver' => env('IMAGE_DRIVER', 'gd'), + + /* + * FFMPEG & FFProbe binaries paths, only used if you try to generate video + * thumbnails and have installed the php-ffmpeg/php-ffmpeg composer + * dependency. + */ + 'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'), + 'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'), + + /* + * The path where to store temporary files while performing image conversions. + * If set to null, storage_path('media-library/temp') will be used. + */ + 'temporary_directory_path' => null, + + /* + * Here you can override the class names of the jobs used by this package. Make sure + * your custom jobs extend the ones provided by the package. + */ + 'jobs' => [ + 'perform_conversions' => \Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class, + 'generate_responsive_images' => \Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class, + ], +]; diff --git a/crater/config/modules.php b/crater/config/modules.php new file mode 100644 index 0000000..f756f60 --- /dev/null +++ b/crater/config/modules.php @@ -0,0 +1,286 @@ + 'Modules', + + /* + |-------------------------------------------------------------------------- + | Module Stubs + |-------------------------------------------------------------------------- + | + | Default module stubs. + | + */ + + 'stubs' => [ + 'enabled' => false, + 'path' => base_path('vendor/nwidart/laravel-modules/src/Commands/stubs'), + 'files' => [ + 'routes/web' => 'Routes/web.php', + 'routes/api' => 'Routes/api.php', + 'views/index' => 'Resources/views/index.blade.php', + 'views/master' => 'Resources/views/layouts/master.blade.php', + 'scaffold/config' => 'Config/config.php', + 'composer' => 'composer.json', + 'resources/scripts/module' => 'Resources/scripts/module.js', + 'resources/sass/module' => 'Resources/sass/module.scss', + 'resources/scripts/stores/sample-store' => 'Resources/scripts/stores/sample-store.js', + 'resources/scripts/views/SamplePage' => 'Resources/scripts/views/SamplePage.vue', + 'resources/locales/en' => 'Resources/locales/en.json', + 'resources/locales/locales' => 'Resources/locales/locales.js', + 'package' => 'package.json', + 'postcss.config' => 'postcss.config.js', + 'tailwind.config' => 'tailwind.config.js', + 'vite.config' => 'vite.config.js', + ], + 'replacements' => [ + 'routes/web' => ['LOWER_NAME', 'STUDLY_NAME'], + 'routes/api' => ['LOWER_NAME'], + 'webpack' => ['LOWER_NAME'], + 'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'], + 'views/index' => ['LOWER_NAME'], + 'views/master' => ['LOWER_NAME', 'STUDLY_NAME'], + 'scaffold/config' => ['STUDLY_NAME'], + 'composer' => [ + 'LOWER_NAME', + 'STUDLY_NAME', + 'VENDOR', + 'AUTHOR_NAME', + 'AUTHOR_EMAIL', + 'MODULE_NAMESPACE', + 'PROVIDER_NAMESPACE', + ], + 'assets/scripts/module' => ['LOWER_NAME'], + 'assets/scripts/stores/sample-store' => ['LOWER_NAME'], + 'vite.config' => ['LOWER_NAME'], + ], + 'gitkeep' => true, + ], + 'paths' => [ + /* + |-------------------------------------------------------------------------- + | Modules path + |-------------------------------------------------------------------------- + | + | This path used for save the generated module. This path also will be added + | automatically to list of scanned folders. + | + */ + + 'modules' => base_path('Modules'), + /* + |-------------------------------------------------------------------------- + | Modules assets path + |-------------------------------------------------------------------------- + | + | Here you may update the modules assets path. + | + */ + + 'assets' => public_path('modules'), + /* + |-------------------------------------------------------------------------- + | The migrations path + |-------------------------------------------------------------------------- + | + | Where you run 'module:publish-migration' command, where do you publish the + | the migration files? + | + */ + + 'migration' => base_path('database/migrations'), + /* + |-------------------------------------------------------------------------- + | Generator path + |-------------------------------------------------------------------------- + | Customise the paths where the folders will be generated. + | Set the generate key to false to not generate that folder + */ + 'generator' => [ + 'config' => ['path' => 'Config', 'generate' => true], + 'command' => ['path' => 'Console', 'generate' => true], + 'migration' => ['path' => 'Database/Migrations', 'generate' => true], + 'seeder' => ['path' => 'Database/Seeders', 'generate' => true], + 'factory' => ['path' => 'Database/factories', 'generate' => true], + 'model' => ['path' => 'Entities', 'generate' => true], + 'routes' => ['path' => 'Routes', 'generate' => true], + 'controller' => ['path' => 'Http/Controllers', 'generate' => true], + 'filter' => ['path' => 'Http/Middleware', 'generate' => true], + 'request' => ['path' => 'Http/Requests', 'generate' => true], + 'provider' => ['path' => 'Providers', 'generate' => true], + 'assets' => ['path' => 'Resources/assets', 'generate' => true], + 'lang' => ['path' => 'Resources/lang', 'generate' => true], + 'views' => ['path' => 'Resources/views', 'generate' => true], + 'test' => ['path' => 'Tests/Unit', 'generate' => true], + 'test-feature' => ['path' => 'Tests/Feature', 'generate' => true], + 'repository' => ['path' => 'Repositories', 'generate' => false], + 'event' => ['path' => 'Events', 'generate' => false], + 'listener' => ['path' => 'Listeners', 'generate' => false], + 'policies' => ['path' => 'Policies', 'generate' => false], + 'rules' => ['path' => 'Rules', 'generate' => false], + 'jobs' => ['path' => 'Jobs', 'generate' => false], + 'emails' => ['path' => 'Emails', 'generate' => false], + 'notifications' => ['path' => 'Notifications', 'generate' => false], + 'resource' => ['path' => 'Transformers', 'generate' => false], + 'component-view' => ['path' => 'Resources/views/components', 'generate' => false], + 'component-class' => ['path' => 'View/Components', 'generate' => false], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Package commands + |-------------------------------------------------------------------------- + | + | Here you can define which commands will be visible and used in your + | application. If for example you don't use some of the commands provided + | you can simply comment them out. + | + */ + 'commands' => [ + Commands\CommandMakeCommand::class, + Commands\ComponentClassMakeCommand::class, + Commands\ComponentViewMakeCommand::class, + Commands\ControllerMakeCommand::class, + Commands\DisableCommand::class, + Commands\DumpCommand::class, + Commands\EnableCommand::class, + Commands\EventMakeCommand::class, + Commands\JobMakeCommand::class, + Commands\ListenerMakeCommand::class, + Commands\MailMakeCommand::class, + Commands\MiddlewareMakeCommand::class, + Commands\NotificationMakeCommand::class, + Commands\ProviderMakeCommand::class, + Commands\RouteProviderMakeCommand::class, + Commands\InstallCommand::class, + Commands\ListCommand::class, + Commands\ModuleDeleteCommand::class, + Commands\ModuleMakeCommand::class, + Commands\FactoryMakeCommand::class, + Commands\PolicyMakeCommand::class, + Commands\RequestMakeCommand::class, + Commands\RuleMakeCommand::class, + Commands\MigrateCommand::class, + Commands\MigrateRefreshCommand::class, + Commands\MigrateResetCommand::class, + Commands\MigrateRollbackCommand::class, + Commands\MigrateStatusCommand::class, + Commands\MigrationMakeCommand::class, + Commands\ModelMakeCommand::class, + Commands\PublishCommand::class, + Commands\PublishConfigurationCommand::class, + Commands\PublishMigrationCommand::class, + Commands\PublishTranslationCommand::class, + Commands\SeedCommand::class, + Commands\SeedMakeCommand::class, + Commands\SetupCommand::class, + Commands\UnUseCommand::class, + Commands\UpdateCommand::class, + Commands\UseCommand::class, + Commands\ResourceMakeCommand::class, + Commands\TestMakeCommand::class, + Commands\LaravelModulesV6Migrator::class, + Commands\ComponentClassMakeCommand::class, + Commands\ComponentViewMakeCommand::class, + ], + + /* + |-------------------------------------------------------------------------- + | Scan Path + |-------------------------------------------------------------------------- + | + | Here you define which folder will be scanned. By default will scan vendor + | directory. This is useful if you host the package in packagist website. + | + */ + + 'scan' => [ + 'enabled' => false, + 'paths' => [ + base_path('vendor/*/*'), + ], + ], + /* + |-------------------------------------------------------------------------- + | Composer File Template + |-------------------------------------------------------------------------- + | + | Here is the config for composer.json file, generated by this package + | + */ + + 'composer' => [ + 'vendor' => 'nwidart', + 'author' => [ + 'name' => 'Nicolas Widart', + 'email' => 'n.widart@gmail.com', + ], + 'composer-output' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Caching + |-------------------------------------------------------------------------- + | + | Here is the config for setting up caching feature. + | + */ + 'cache' => [ + 'enabled' => false, + 'key' => 'laravel-modules', + 'lifetime' => 60, + ], + /* + |-------------------------------------------------------------------------- + | Choose what laravel-modules will register as custom namespaces. + | Setting one to false will require you to register that part + | in your own Service Provider class. + |-------------------------------------------------------------------------- + */ + 'register' => [ + 'translations' => true, + /** + * load files on boot or register method + * + * Note: boot not compatible with asgardcms + * + * @example boot|register + */ + 'files' => 'register', + ], + + /* + |-------------------------------------------------------------------------- + | Activators + |-------------------------------------------------------------------------- + | + | You can define new types of activators here, file, database etc. The only + | required parameter is 'class'. + | The file activator will store the activation status in storage/installed_modules + */ + 'activators' => [ + 'file' => [ + 'class' => FileActivator::class, + 'statuses-file' => base_path('storage/app/modules_statuses.json'), + 'cache-key' => 'activator.installed', + 'cache-lifetime' => 604800, + ], + ], + + 'activator' => 'file', +]; diff --git a/crater/config/queue.php b/crater/config/queue.php new file mode 100644 index 0000000..cfc88c8 --- /dev/null +++ b/crater/config/queue.php @@ -0,0 +1,85 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => 'your-public-key', + 'secret' => 'your-secret-key', + 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id', + 'queue' => 'your-queue-name', + 'region' => 'us-east-1', + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => 'default', + 'retry_after' => 90, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/crater/config/sanctum.php b/crater/config/sanctum.php new file mode 100644 index 0000000..51d060f --- /dev/null +++ b/crater/config/sanctum.php @@ -0,0 +1,47 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1,127.0.0.1:8000,::1')), + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. If this value is null, personal access tokens do + | not expire. This won't tweak the lifetime of first-party sessions. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'verify_csrf_token' => Crater\Http\Middleware\VerifyCsrfToken::class, + 'encrypt_cookies' => Crater\Http\Middleware\EncryptCookies::class, + ], + +]; diff --git a/crater/config/services.php b/crater/config/services.php new file mode 100644 index 0000000..c9254a8 --- /dev/null +++ b/crater/config/services.php @@ -0,0 +1,68 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + ], + + 'ses' => [ + 'key' => env('SES_KEY'), + 'secret' => env('SES_SECRET'), + 'region' => 'us-east-1', + ], + + 'sparkpost' => [ + 'secret' => env('SPARKPOST_SECRET'), + ], + + 'sendgrid' => [ + 'api_key' => env('SENDGRID_API_KEY'), + ], + + 'stripe' => [ + 'model' => \Crater\Models\User::class, + 'key' => env('STRIPE_KEY'), + 'secret' => env('STRIPE_SECRET'), + 'webhook' => [ + 'secret' => env('STRIPE_WEBHOOK_SECRET'), + 'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300), + ], + ], + + 'facebook' => [ + 'client_id' => env('FACEBOOK_CLIENT_ID'), + 'client_secret' => env('FACEBOOK_CLIENT_SECRET'), + 'redirect' => env('FACEBOOK_REDIRECT_URL'), + ], + + 'google' => [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URL'), + ], + + 'github' => [ + 'client_id' => env('GITHUB_CLIENT_ID'), + 'client_secret' => env('GITHUB_CLIENT_SECRET'), + 'redirect' => env('GITHUB_REDIRECT_URL'), + ], + + 'cron_job' => [ + 'auth_token' => env('CRON_JOB_AUTH_TOKEN', 0) + ], +]; diff --git a/crater/config/session.php b/crater/config/session.php new file mode 100644 index 0000000..da692f3 --- /dev/null +++ b/crater/config/session.php @@ -0,0 +1,199 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION', null), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using the "apc", "memcached", or "dynamodb" session drivers you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + */ + + 'store' => env('SESSION_STORE', null), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => env('SESSION_DOMAIN', null), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" since this is a secure default value. + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => 'lax', + +]; diff --git a/crater/config/trustedproxy.php b/crater/config/trustedproxy.php new file mode 100644 index 0000000..5d9d0fc --- /dev/null +++ b/crater/config/trustedproxy.php @@ -0,0 +1,50 @@ + null, // [,], '*', ',' + + /* + * To trust one or more specific proxies that connect + * directly to your server, use an array or a string separated by comma of IP addresses: + */ + // 'proxies' => ['192.168.1.1'], + // 'proxies' => '192.168.1.1, 192.168.1.2', + + /* + * Or, to trust all proxies that connect + * directly to your server, use a "*" + */ + 'proxies' => env('TRUSTED_PROXIES', '*'), + + /* + * Which headers to use to detect proxy related data (For, Host, Proto, Port) + * + * Options include: + * + * - Illuminate\Http\Request::HEADER_X_FORWARDED_ALL (use all x-forwarded-* headers to establish trust) + * - Illuminate\Http\Request::HEADER_FORWARDED (use the FORWARDED header to establish trust) + * - Illuminate\Http\Request::HEADER_X_FORWARDED_AWS_ELB (If you are using AWS Elastic Load Balancer) + * + * - 'HEADER_X_FORWARDED_ALL' (use all x-forwarded-* headers to establish trust) + * - 'HEADER_FORWARDED' (use the FORWARDED header to establish trust) + * - 'HEADER_X_FORWARDED_AWS_ELB' (If you are using AWS Elastic Load Balancer) + * + * @link https://symfony.com/doc/current/deployment/proxies.html + */ + 'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_ALL, + +]; diff --git a/crater/config/view.php b/crater/config/view.php new file mode 100644 index 0000000..2acfd9c --- /dev/null +++ b/crater/config/view.php @@ -0,0 +1,33 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => realpath(storage_path('framework/views')), + +]; diff --git a/crater/config/vite.php b/crater/config/vite.php new file mode 100644 index 0000000..18e90ba --- /dev/null +++ b/crater/config/vite.php @@ -0,0 +1,67 @@ + [ + 'resources/scripts/main.js', + ], + 'ignore_patterns' => ["/\.d\.ts$/"], + + /* + |-------------------------------------------------------------------------- + | Aliases + |-------------------------------------------------------------------------- + | These aliases will be added to the Vite configuration and used + | to generate a proper tsconfig.json file. + */ + 'aliases' => [ + '@' => 'resources', + ], + + /* + |-------------------------------------------------------------------------- + | Static assets path + |-------------------------------------------------------------------------- + | This option defines the directory that Vite considers as the + | public directory. Its content will be copied to the build directory + | at build-time. + | https://vitejs.dev/config/#publicdir + */ + 'public_directory' => resource_path('static'), + + /* + |-------------------------------------------------------------------------- + | Ping timeout + |-------------------------------------------------------------------------- + | The maximum duration, in seconds, that the ping to the development + | server should take while trying to determine whether to use the + | manifest or the server in a local environment. + */ + 'ping_timeout' => .1, + + /* + |-------------------------------------------------------------------------- + | Build path + |-------------------------------------------------------------------------- + | The directory, relative to /public, in which Vite will build + | the production files. This should match "build.outDir" in the Vite + | configuration file. + */ + 'build_path' => 'build', + + /* + |-------------------------------------------------------------------------- + | Development URL + |-------------------------------------------------------------------------- + | The URL at which the Vite development server runs. + | This is used to generate the script tags when developing. + */ + 'dev_url' => 'http://localhost:3000', +]; diff --git a/crater/crater.code-workspace b/crater/crater.code-workspace new file mode 100644 index 0000000..c3afed3 --- /dev/null +++ b/crater/crater.code-workspace @@ -0,0 +1,44 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "search.exclude": { + "**/public": true + }, + "editor.formatOnSave": true, + "vetur.validation.template": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "editor.formatOnPaste": true, + "editor.formatOnType": true, + "editor.codeActionsOnSaveTimeout": 2000, + "prettier.semi": false, + "prettier.singleQuote": true, + "files.associations": {}, + "eslint.codeAction.disableRuleComment": {}, + "eslint.codeAction.showDocumentation": { + "enable": true + }, + "eslint.validate": [ + "javascript", + "javascriptreact", + "vue" + ], + "[php]": { + "editor.defaultFormatter": "junstyle.php-cs-fixer" + }, + "debug.allowBreakpointsEverywhere": true, + "files.autoGuessEncoding": true, + "files.exclude": { + "**/.vscode": true, + "compile_commands.json": true, + "*.hrccproj": true, + "*.sln": true, + "*.suo": true + }, + } +} \ No newline at end of file diff --git a/crater/crowdin.yml b/crater/crowdin.yml new file mode 100644 index 0000000..da4a46a --- /dev/null +++ b/crater/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /resources/scripts/locales/en.json + translation: /resources/scripts/locales/%two_letters_code%.json diff --git a/crater/database/.gitignore b/crater/database/.gitignore new file mode 100644 index 0000000..9b1dffd --- /dev/null +++ b/crater/database/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/crater/database/factories/AddressFactory.php b/crater/database/factories/AddressFactory.php new file mode 100644 index 0000000..992674c --- /dev/null +++ b/crater/database/factories/AddressFactory.php @@ -0,0 +1,42 @@ + $this->faker->name, + 'address_street_1' => $this->faker->streetAddress, + 'address_street_2' => $this->faker->streetAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'country_id' => 231, + 'company_id' => User::find(1)->companies()->first()->id, + 'zip' => $this->faker->postcode, + 'phone' => $this->faker->phoneNumber, + 'fax' => $this->faker->phoneNumber, + 'type' => $this->faker->randomElement([Address::BILLING_TYPE, Address::SHIPPING_TYPE]), + 'user_id' => User::factory(), + 'customer_id' => Customer::factory() + ]; + } +} diff --git a/crater/database/factories/CompanyFactory.php b/crater/database/factories/CompanyFactory.php new file mode 100644 index 0000000..763ad4a --- /dev/null +++ b/crater/database/factories/CompanyFactory.php @@ -0,0 +1,32 @@ + str_random(20), + 'name' => $this->faker->name(), + 'owner_id' => User::where('role', 'super admin')->first()->id, + 'slug' => $this->faker->word + ]; + } +} diff --git a/crater/database/factories/CompanySettingFactory.php b/crater/database/factories/CompanySettingFactory.php new file mode 100644 index 0000000..bec7a80 --- /dev/null +++ b/crater/database/factories/CompanySettingFactory.php @@ -0,0 +1,31 @@ + $this->faker->word, + 'value' => $this->faker->word, + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/CustomFieldFactory.php b/crater/database/factories/CustomFieldFactory.php new file mode 100644 index 0000000..8eb906c --- /dev/null +++ b/crater/database/factories/CustomFieldFactory.php @@ -0,0 +1,38 @@ + $this->faker->name, + 'label' => $this->faker->name, + 'order' => $this->faker->randomDigitNotNull, + 'is_required' => $this->faker->randomElement([true, false]), + 'model_type' => $this->faker->randomElement(['Customer', 'Invoice', 'Estimate', 'Expense', 'Payment']), + 'slug' => function (array $item) { + return clean_slug($item['model_type'], $item['label']); + }, + 'type' => $this->faker->randomElement(['Text', 'Textarea', 'Phone', 'URL', 'Number','Dropdown' , 'Switch', 'Date', 'DateTime', 'Time']), + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/CustomFieldValueFactory.php b/crater/database/factories/CustomFieldValueFactory.php new file mode 100644 index 0000000..7891970 --- /dev/null +++ b/crater/database/factories/CustomFieldValueFactory.php @@ -0,0 +1,34 @@ + $this->faker->name , + 'custom_field_valuable_id' => 1, + 'type' => $this->faker->name, + 'custom_field_id' => CustomField::factory(), + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/CustomerFactory.php b/crater/database/factories/CustomerFactory.php new file mode 100644 index 0000000..f8f3d40 --- /dev/null +++ b/crater/database/factories/CustomerFactory.php @@ -0,0 +1,41 @@ + $this->faker->name, + 'company_name' => $this->faker->company, + 'contact_name' => $this->faker->name, + 'prefix' => $this->faker->randomDigitNotNull, + 'website' => $this->faker->url, + 'enable_portal' => true, + 'email' => $this->faker->unique()->safeEmail, + 'phone' => $this->faker->phoneNumber, + 'company_id' => User::find(1)->companies()->first()->id, + 'password' => Hash::make('secret'), + 'currency_id' => Currency::find(1)->id, + ]; + } +} diff --git a/crater/database/factories/EmailLogFactory.php b/crater/database/factories/EmailLogFactory.php new file mode 100644 index 0000000..d4e86a4 --- /dev/null +++ b/crater/database/factories/EmailLogFactory.php @@ -0,0 +1,38 @@ + $this->faker->unique()->safeEmail, + 'to' => $this->faker->unique()->safeEmail, + 'subject' => $this->faker->sentence, + 'body' => $this->faker->text, + 'mailable_type' => $this->faker->randomElement([Invoice::class, Estimate::class, Payment::class]), + 'mailable_id' => function (array $log) { + return $log['mailable_type']::factory(); + }, + ]; + } +} diff --git a/crater/database/factories/EstimateFactory.php b/crater/database/factories/EstimateFactory.php new file mode 100644 index 0000000..e7e7111 --- /dev/null +++ b/crater/database/factories/EstimateFactory.php @@ -0,0 +1,111 @@ +state(function (array $attributes) { + return [ + 'status' => Estimate::STATUS_SENT, + ]; + }); + } + + public function viewed() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Estimate::STATUS_VIEWED, + ]; + }); + } + + public function expired() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Estimate::STATUS_EXPIRED, + ]; + }); + } + + public function accepted() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Estimate::STATUS_ACCEPTED, + ]; + }); + } + + public function rejected() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Estimate::STATUS_REJECTED, + ]; + }); + } + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + $sequenceNumber = (new SerialNumberFormatter()) + ->setModel(new Estimate()) + ->setCompany(User::find(1)->companies()->first()->id) + ->setNextNumbers(); + + return [ + 'estimate_date' => $this->faker->date('Y-m-d', 'now'), + 'expiry_date' => $this->faker->date('Y-m-d', 'now'), + 'estimate_number' => $sequenceNumber->getNextNumber(), + 'sequence_number' => $sequenceNumber->nextSequenceNumber, + 'customer_sequence_number' => $sequenceNumber->nextCustomerSequenceNumber, + 'reference_number' => $sequenceNumber->getNextNumber(), + 'company_id' => User::find(1)->companies()->first()->id, + 'status' => Estimate::STATUS_DRAFT, + 'template_name' => 'estimate1', + 'sub_total' => $this->faker->randomDigitNotNull, + 'total' => $this->faker->randomDigitNotNull, + 'discount_type' => $this->faker->randomElement(['percentage', 'fixed']), + 'discount_val' => function (array $estimate) { + return $estimate['discount_type'] == 'percentage' ? $this->faker->numberBetween($min = 0, $max = 100) : $this->faker->randomDigitNotNull; + }, + 'discount' => function (array $estimate) { + return $estimate['discount_type'] == 'percentage' ? (($estimate['discount_val'] * $estimate['total']) / 100) : $estimate['discount_val']; + }, + 'tax_per_item' => 'YES', + 'discount_per_item' => 'No', + 'tax' => $this->faker->randomDigitNotNull, + 'notes' => $this->faker->text(80), + 'unique_hash' => str_random(60), + 'customer_id' => Customer::factory(), + 'exchange_rate' => $this->faker->randomDigitNotNull, + 'base_discount_val' => $this->faker->randomDigitNotNull, + 'base_sub_total' => $this->faker->randomDigitNotNull, + 'base_total' => $this->faker->randomDigitNotNull, + 'base_tax' => $this->faker->randomDigitNotNull, + 'currency_id' => Currency::find(1)->id, + ]; + } +} diff --git a/crater/database/factories/EstimateItemFactory.php b/crater/database/factories/EstimateItemFactory.php new file mode 100644 index 0000000..2b78a49 --- /dev/null +++ b/crater/database/factories/EstimateItemFactory.php @@ -0,0 +1,59 @@ + Item::factory(), + 'name' => function (array $item) { + return Item::find($item['item_id'])->name; + }, + 'description' => function (array $item) { + return Item::find($item['item_id'])->description; + }, + 'price' => function (array $item) { + return Item::find($item['item_id'])->price; + }, + 'estimate_id' => Estimate::factory(), + 'quantity' => $this->faker->randomDigitNotNull, + 'company_id' => User::find(1)->companies()->first()->id, + 'tax' => $this->faker->randomDigitNotNull, + 'total' => function (array $item) { + return ($item['price'] * $item['quantity']); + }, + 'discount_type' => $this->faker->randomElement(['percentage', 'fixed']), + 'discount_val' => function (array $estimate) { + return $estimate['discount_type'] == 'percentage' ? $this->faker->numberBetween($min = 0, $max = 100) : $this->faker->randomDigitNotNull; + }, + 'discount' => function (array $estimate) { + return $estimate['discount_type'] == 'percentage' ? (($estimate['discount_val'] * $estimate['total']) / 100) : $estimate['discount_val']; + }, + 'exchange_rate' => $this->faker->randomDigitNotNull, + 'base_discount_val' => $this->faker->randomDigitNotNull, + 'base_price' => $this->faker->randomDigitNotNull, + 'base_total' => $this->faker->randomDigitNotNull, + 'base_tax' => $this->faker->randomDigitNotNull, + ]; + } +} diff --git a/crater/database/factories/ExchangeRateLogFactory.php b/crater/database/factories/ExchangeRateLogFactory.php new file mode 100644 index 0000000..14bf200 --- /dev/null +++ b/crater/database/factories/ExchangeRateLogFactory.php @@ -0,0 +1,33 @@ + Currency::find(1)->id, + 'base_currency_id' => User::find(1)->companies()->first()->id, + 'currency_id' => Currency::find(4)->id, + 'exchange_rate' => $this->faker->randomDigitNotNull + ]; + } +} diff --git a/crater/database/factories/ExchangeRateProviderFactory.php b/crater/database/factories/ExchangeRateProviderFactory.php new file mode 100644 index 0000000..c71bf57 --- /dev/null +++ b/crater/database/factories/ExchangeRateProviderFactory.php @@ -0,0 +1,30 @@ + $this->faker->word, + 'key' => str_random(10), + 'active' => $this->faker->randomElement([true, false]), + ]; + } +} diff --git a/crater/database/factories/ExpenseCategoryFactory.php b/crater/database/factories/ExpenseCategoryFactory.php new file mode 100644 index 0000000..93800fc --- /dev/null +++ b/crater/database/factories/ExpenseCategoryFactory.php @@ -0,0 +1,31 @@ + $this->faker->word, + 'company_id' => User::find(1)->companies()->first()->id, + 'description' => $this->faker->text, + ]; + } +} diff --git a/crater/database/factories/ExpenseFactory.php b/crater/database/factories/ExpenseFactory.php new file mode 100644 index 0000000..4ad2542 --- /dev/null +++ b/crater/database/factories/ExpenseFactory.php @@ -0,0 +1,41 @@ + $this->faker->date('Y-m-d', 'now'), + 'expense_category_id' => ExpenseCategory::factory(), + 'company_id' => User::find(1)->companies()->first()->id, + 'amount' => $this->faker->randomDigitNotNull, + 'notes' => $this->faker->text, + 'attachment_receipt' => null, + 'customer_id' => Customer::factory(), + 'exchange_rate' => $this->faker->randomDigitNotNull, + 'base_amount' => $this->faker->randomDigitNotNull, + 'currency_id' => Currency::find(1)->id, + ]; + } +} diff --git a/crater/database/factories/FileDiskFactory.php b/crater/database/factories/FileDiskFactory.php new file mode 100644 index 0000000..d522781 --- /dev/null +++ b/crater/database/factories/FileDiskFactory.php @@ -0,0 +1,35 @@ + $this->faker->word, + 'driver' => 'local', + 'set_as_default' => false, + 'credentials' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + ], + + ]; + } +} diff --git a/crater/database/factories/InvoiceFactory.php b/crater/database/factories/InvoiceFactory.php new file mode 100644 index 0000000..04c4af1 --- /dev/null +++ b/crater/database/factories/InvoiceFactory.php @@ -0,0 +1,127 @@ +state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_SENT, + ]; + }); + } + + public function viewed() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_VIEWED, + ]; + }); + } + + public function completed() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_COMPLETED, + ]; + }); + } + + public function unpaid() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_UNPAID, + ]; + }); + } + + public function partiallyPaid() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_PARTIALLY_PAID, + ]; + }); + } + + public function paid() + { + return $this->state(function (array $attributes) { + return [ + 'status' => Invoice::STATUS_PAID, + ]; + }); + } + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + $sequenceNumber = (new SerialNumberFormatter()) + ->setModel(new Invoice()) + ->setCompany(User::find(1)->companies()->first()->id) + ->setNextNumbers(); + + return [ + 'invoice_date' => $this->faker->date('Y-m-d', 'now'), + 'due_date' => $this->faker->date('Y-m-d', 'now'), + 'invoice_number' => $sequenceNumber->getNextNumber(), + 'sequence_number' => $sequenceNumber->nextSequenceNumber, + 'customer_sequence_number' => $sequenceNumber->nextCustomerSequenceNumber, + 'reference_number' => $sequenceNumber->getNextNumber(), + 'template_name' => 'invoice1', + 'status' => Invoice::STATUS_DRAFT, + 'tax_per_item' => 'NO', + 'discount_per_item' => 'NO', + 'paid_status' => Invoice::STATUS_UNPAID, + 'company_id' => User::find(1)->companies()->first()->id, + 'sub_total' => $this->faker->randomDigitNotNull, + 'total' => $this->faker->randomDigitNotNull, + 'discount_type' => $this->faker->randomElement(['percentage', 'fixed']), + 'discount_val' => function (array $invoice) { + return $invoice['discount_type'] == 'percentage' ? $this->faker->numberBetween($min = 0, $max = 100) : $this->faker->randomDigitNotNull; + }, + 'discount' => function (array $invoice) { + return $invoice['discount_type'] == 'percentage' ? (($invoice['discount_val'] * $invoice['total']) / 100) : $invoice['discount_val']; + }, + 'tax' => $this->faker->randomDigitNotNull, + 'due_amount' => function (array $invoice) { + return $invoice['total']; + }, + 'notes' => $this->faker->text(80), + 'unique_hash' => str_random(60), + 'customer_id' => Customer::factory(), + 'recurring_invoice_id' => RecurringInvoice::factory(), + 'exchange_rate' => $this->faker->randomDigitNotNull, + 'base_discount_val' => $this->faker->randomDigitNotNull, + 'base_sub_total' => $this->faker->randomDigitNotNull, + 'base_total' => $this->faker->randomDigitNotNull, + 'base_tax' => $this->faker->randomDigitNotNull, + 'base_due_amount' => $this->faker->randomDigitNotNull, + 'currency_id' => Currency::find(1)->id, + ]; + } +} diff --git a/crater/database/factories/InvoiceItemFactory.php b/crater/database/factories/InvoiceItemFactory.php new file mode 100644 index 0000000..7d3ce52 --- /dev/null +++ b/crater/database/factories/InvoiceItemFactory.php @@ -0,0 +1,59 @@ + Item::factory(), + 'name' => function (array $item) { + return Item::find($item['item_id'])->name; + }, + 'description' => function (array $item) { + return Item::find($item['item_id'])->description; + }, + 'price' => function (array $item) { + return Item::find($item['item_id'])->price; + }, + 'company_id' => User::find(1)->companies()->first()->id, + 'quantity' => $this->faker->randomDigitNotNull, + 'total' => function (array $item) { + return ($item['price'] * $item['quantity']); + }, + 'discount_type' => $this->faker->randomElement(['percentage', 'fixed']), + 'discount_val' => function (array $invoice) { + return $invoice['discount_type'] == 'percentage' ? $this->faker->numberBetween($min = 0, $max = 100) : $this->faker->randomDigitNotNull; + }, + 'discount' => function (array $invoice) { + return $invoice['discount_type'] == 'percentage' ? (($invoice['discount_val'] * $invoice['total']) / 100) : $invoice['discount_val']; + }, + 'tax' => $this->faker->randomDigitNotNull, + 'recurring_invoice_id' => RecurringInvoice::factory(), + 'exchange_rate' => $this->faker->randomDigitNotNull, + 'base_discount_val' => $this->faker->randomDigitNotNull, + 'base_price' => $this->faker->randomDigitNotNull, + 'base_total' => $this->faker->randomDigitNotNull, + 'base_tax' => $this->faker->randomDigitNotNull, + ]; + } +} diff --git a/crater/database/factories/ItemFactory.php b/crater/database/factories/ItemFactory.php new file mode 100644 index 0000000..bc476e9 --- /dev/null +++ b/crater/database/factories/ItemFactory.php @@ -0,0 +1,38 @@ + $this->faker->name, + 'description' => $this->faker->text, + 'company_id' => User::find(1)->companies()->first()->id, + 'price' => $this->faker->randomDigitNotNull, + 'unit_id' => Unit::factory(), + 'creator_id' => User::where('role', 'super admin')->first()->company_id, + 'currency_id' => Currency::find(1)->id, + 'tax_per_item' => $this->faker->randomElement([true, false]) + ]; + } +} diff --git a/crater/database/factories/NoteFactory.php b/crater/database/factories/NoteFactory.php new file mode 100644 index 0000000..7e436ef --- /dev/null +++ b/crater/database/factories/NoteFactory.php @@ -0,0 +1,32 @@ + $this->faker->randomElement(['Invoice', 'Estimate', 'Payment']), + 'name' => $this->faker->word, + 'notes' => $this->faker->text, + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/PaymentFactory.php b/crater/database/factories/PaymentFactory.php new file mode 100644 index 0000000..9fb91c6 --- /dev/null +++ b/crater/database/factories/PaymentFactory.php @@ -0,0 +1,49 @@ +setModel(new Payment()) + ->setCompany(User::find(1)->companies()->first()->id) + ->setNextNumbers(); + + return [ + 'company_id' => User::find(1)->companies()->first()->id, + 'payment_date' => $this->faker->date('Y-m-d', 'now'), + 'notes' => $this->faker->text(80), + 'amount' => $this->faker->randomDigitNotNull, + 'sequence_number' => $sequenceNumber->nextSequenceNumber, + 'customer_sequence_number' => $sequenceNumber->nextCustomerSequenceNumber, + 'payment_number' => $sequenceNumber->getNextNumber(), + 'unique_hash' => str_random(60), + 'payment_method_id' => PaymentMethod::find(1)->id, + 'customer_id' => Customer::factory(), + 'base_amount' => $this->faker->randomDigitNotNull, + 'currency_id' => Currency::find(1)->id, + ]; + } +} diff --git a/crater/database/factories/PaymentMethodFactory.php b/crater/database/factories/PaymentMethodFactory.php new file mode 100644 index 0000000..c2e3576 --- /dev/null +++ b/crater/database/factories/PaymentMethodFactory.php @@ -0,0 +1,30 @@ + $this->faker->name, + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/RecurringInvoiceFactory.php b/crater/database/factories/RecurringInvoiceFactory.php new file mode 100644 index 0000000..90756ba --- /dev/null +++ b/crater/database/factories/RecurringInvoiceFactory.php @@ -0,0 +1,47 @@ + $this->faker->iso8601(), + 'send_automatically' => false, + 'status' => $this->faker->randomElement(['COMPLETED', 'ON_HOLD', 'ACTIVE']), + 'tax_per_item' => 'NO', + 'discount_per_item' => 'NO', + 'sub_total' => $this->faker->randomDigitNotNull, + 'total' => $this->faker->randomDigitNotNull, + 'tax' => $this->faker->randomDigitNotNull, + 'due_amount' => $this->faker->randomDigitNotNull, + 'discount' => $this->faker->randomDigitNotNull, + 'discount_val' => $this->faker->randomDigitNotNull, + 'customer_id' => Customer::factory(), + 'company_id' => User::find(1)->companies()->first()->id, + 'frequency' => '* * 18 * *', + 'limit_by' => $this->faker->randomElement(['NONE', 'COUNT', 'DATE']), + 'limit_count' => $this->faker->randomDigit, + 'limit_date' => $this->faker->date(), + 'exchange_rate' => $this->faker->randomDigitNotNull + ]; + } +} diff --git a/crater/database/factories/TaxFactory.php b/crater/database/factories/TaxFactory.php new file mode 100644 index 0000000..0ca3f7f --- /dev/null +++ b/crater/database/factories/TaxFactory.php @@ -0,0 +1,42 @@ + TaxType::factory(), + 'percent' => function (array $item) { + return TaxType::find($item['tax_type_id'])->percent; + }, + 'name' => function (array $item) { + return TaxType::find($item['tax_type_id'])->name; + }, + 'company_id' => User::find(1)->companies()->first()->id, + 'amount' => $this->faker->randomDigitNotNull, + 'compound_tax' => $this->faker->randomDigitNotNull, + 'base_amount' => $this->faker->randomDigitNotNull, + 'currency_id' => Currency::where('name', 'US Dollar')->first()->id, + ]; + } +} diff --git a/crater/database/factories/TaxTypeFactory.php b/crater/database/factories/TaxTypeFactory.php new file mode 100644 index 0000000..2306d05 --- /dev/null +++ b/crater/database/factories/TaxTypeFactory.php @@ -0,0 +1,34 @@ + $this->faker->word, + 'company_id' => User::find(1)->companies()->first()->id, + 'percent' => $this->faker->numberBetween($min = 0, $max = 100), + 'description' => $this->faker->text, + 'compound_tax' => 0, + 'collective_tax' => 0, + ]; + } +} diff --git a/crater/database/factories/UnitFactory.php b/crater/database/factories/UnitFactory.php new file mode 100644 index 0000000..8b24445 --- /dev/null +++ b/crater/database/factories/UnitFactory.php @@ -0,0 +1,30 @@ + $this->faker->name, + 'company_id' => User::find(1)->companies()->first()->id, + ]; + } +} diff --git a/crater/database/factories/UserFactory.php b/crater/database/factories/UserFactory.php new file mode 100644 index 0000000..2f67269 --- /dev/null +++ b/crater/database/factories/UserFactory.php @@ -0,0 +1,39 @@ + $this->faker->name, + 'company_name' => $this->faker->company, + 'contact_name' => $this->faker->name, + 'website' => $this->faker->url, + 'enable_portal' => true, + 'email' => $this->faker->unique()->safeEmail, + 'phone' => $this->faker->phoneNumber, + 'role' => 'super admin', + 'password' => Hash::make('secret'), + 'currency_id' => Currency::first()->id, + ]; + } +} diff --git a/crater/database/migrations/2014_10_11_071840_create_companies_table.php b/crater/database/migrations/2014_10_11_071840_create_companies_table.php new file mode 100644 index 0000000..8e94831 --- /dev/null +++ b/crater/database/migrations/2014_10_11_071840_create_companies_table.php @@ -0,0 +1,34 @@ +increments('id'); + $table->string('name'); + $table->string('logo')->nullable(); + $table->string('unique_hash')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('companies'); + } +} diff --git a/crater/database/migrations/2014_10_11_125754_create_currencies_table.php b/crater/database/migrations/2014_10_11_125754_create_currencies_table.php new file mode 100644 index 0000000..200b4ee --- /dev/null +++ b/crater/database/migrations/2014_10_11_125754_create_currencies_table.php @@ -0,0 +1,38 @@ +increments('id'); + $table->string('name'); + $table->string('code'); + $table->string('symbol')->nullable(); + $table->integer('precision'); + $table->string('thousand_separator'); + $table->string('decimal_separator'); + $table->boolean('swap_currency_symbol')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('currencies'); + } +} diff --git a/crater/database/migrations/2014_10_12_000000_create_users_table.php b/crater/database/migrations/2014_10_12_000000_create_users_table.php new file mode 100644 index 0000000..13adfbf --- /dev/null +++ b/crater/database/migrations/2014_10_12_000000_create_users_table.php @@ -0,0 +1,48 @@ +increments('id'); + $table->string('name'); + $table->string('email')->unique()->nullable(); + $table->string('phone')->nullable(); + $table->string('password')->nullable(); + $table->string('role')->default('user'); + $table->rememberToken(); + $table->string('facebook_id')->nullable(); + $table->string('google_id')->nullable(); + $table->string('github_id')->nullable(); + $table->string('contact_name')->nullable(); + $table->string('company_name')->nullable(); + $table->string('website')->nullable(); + $table->boolean('enable_portal')->nullable(); + $table->integer('currency_id')->unsigned()->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/crater/database/migrations/2014_10_12_100000_create_password_resets_table.php b/crater/database/migrations/2014_10_12_100000_create_password_resets_table.php new file mode 100644 index 0000000..0ee0a36 --- /dev/null +++ b/crater/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -0,0 +1,32 @@ +string('email')->index(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('password_resets'); + } +} diff --git a/crater/database/migrations/2016_05_13_060834_create_settings_table.php b/crater/database/migrations/2016_05_13_060834_create_settings_table.php new file mode 100644 index 0000000..79a6fe4 --- /dev/null +++ b/crater/database/migrations/2016_05_13_060834_create_settings_table.php @@ -0,0 +1,32 @@ +increments('id'); + $table->string('option'); + $table->string('value'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('settings'); + } +} diff --git a/crater/database/migrations/2017_04_11_064308_create_units_table.php b/crater/database/migrations/2017_04_11_064308_create_units_table.php new file mode 100644 index 0000000..2ce4ef3 --- /dev/null +++ b/crater/database/migrations/2017_04_11_064308_create_units_table.php @@ -0,0 +1,36 @@ +increments('id'); + $table->string('name'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('units'); + } +} diff --git a/crater/database/migrations/2017_04_11_081227_create_items_table.php b/crater/database/migrations/2017_04_11_081227_create_items_table.php new file mode 100644 index 0000000..ba912e2 --- /dev/null +++ b/crater/database/migrations/2017_04_11_081227_create_items_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->string('name'); + $table->string('description')->nullable(); + $table->string('unit')->nullable(); + $table->unsignedBigInteger('price'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->integer('unit_id')->unsigned()->nullable(); + $table->foreign('unit_id')->references('id')->on('units')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('items'); + } +} diff --git a/crater/database/migrations/2017_04_12_090759_create_invoices_table.php b/crater/database/migrations/2017_04_12_090759_create_invoices_table.php new file mode 100644 index 0000000..444313e --- /dev/null +++ b/crater/database/migrations/2017_04_12_090759_create_invoices_table.php @@ -0,0 +1,54 @@ +increments('id'); + $table->date('invoice_date'); + $table->date('due_date'); + $table->string('invoice_number'); + $table->string('reference_number')->nullable(); + $table->string('status'); + $table->string('paid_status'); + $table->string('tax_per_item'); + $table->string('discount_per_item'); + $table->text('notes')->nullable(); + $table->string('discount_type')->nullable(); + $table->decimal('discount', 15, 2)->nullable(); + $table->unsignedBigInteger('discount_val')->nullable(); + $table->unsignedBigInteger('sub_total'); + $table->unsignedBigInteger('total'); + $table->unsignedBigInteger('tax'); + $table->unsignedBigInteger('due_amount'); + $table->boolean('sent')->default(false); + $table->boolean('viewed')->default(false); + $table->string('unique_hash')->nullable(); + $table->integer('user_id')->unsigned()->nullable(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoices'); + } +} diff --git a/crater/database/migrations/2017_04_12_091015_create_invoice_items_table.php b/crater/database/migrations/2017_04_12_091015_create_invoice_items_table.php new file mode 100644 index 0000000..4b5bcf2 --- /dev/null +++ b/crater/database/migrations/2017_04_12_091015_create_invoice_items_table.php @@ -0,0 +1,46 @@ +increments('id'); + $table->string('name'); + $table->string('description')->nullable(); + $table->string('discount_type'); + $table->unsignedBigInteger('price'); + $table->decimal('quantity', 15, 2); + $table->decimal('discount', 15, 2)->nullable(); + $table->unsignedBigInteger('discount_val'); + $table->unsignedBigInteger('tax'); + $table->unsignedBigInteger('total'); + $table->integer('invoice_id')->unsigned(); + $table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade'); + $table->integer('item_id')->unsigned()->nullable(); + $table->foreign('item_id')->references('id')->on('items')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoice_items'); + } +} diff --git a/crater/database/migrations/2017_05_05_055609_create_estimates_table.php b/crater/database/migrations/2017_05_05_055609_create_estimates_table.php new file mode 100644 index 0000000..c3259e2 --- /dev/null +++ b/crater/database/migrations/2017_05_05_055609_create_estimates_table.php @@ -0,0 +1,50 @@ +increments('id'); + $table->date('estimate_date'); + $table->date('expiry_date'); + $table->string('estimate_number'); + $table->string('status'); + $table->string('reference_number')->nullable(); + $table->string('tax_per_item'); + $table->string('discount_per_item'); + $table->string('notes')->nullable(); + $table->decimal('discount', 15, 2)->nullable(); + $table->string('discount_type')->nullable(); + $table->unsignedBigInteger('discount_val')->nullable(); + $table->unsignedBigInteger('sub_total'); + $table->unsignedBigInteger('total'); + $table->unsignedBigInteger('tax'); + $table->string('unique_hash')->nullable(); + $table->integer('user_id')->unsigned()->nullable(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('estimates'); + } +} diff --git a/crater/database/migrations/2017_05_05_073927_create_notifications_table.php b/crater/database/migrations/2017_05_05_073927_create_notifications_table.php new file mode 100644 index 0000000..9797596 --- /dev/null +++ b/crater/database/migrations/2017_05_05_073927_create_notifications_table.php @@ -0,0 +1,35 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notifications'); + } +} diff --git a/crater/database/migrations/2017_05_06_173745_create_countries_table.php b/crater/database/migrations/2017_05_06_173745_create_countries_table.php new file mode 100755 index 0000000..fd6e7a5 --- /dev/null +++ b/crater/database/migrations/2017_05_06_173745_create_countries_table.php @@ -0,0 +1,34 @@ +engine = 'InnoDB'; + $table->increments('id')->index(); + $table->string('code'); + $table->string('name'); + $table->integer('phonecode'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('countries'); + } +} diff --git a/crater/database/migrations/2017_10_02_123501_create_estimate_items_table.php b/crater/database/migrations/2017_10_02_123501_create_estimate_items_table.php new file mode 100644 index 0000000..bddd5e3 --- /dev/null +++ b/crater/database/migrations/2017_10_02_123501_create_estimate_items_table.php @@ -0,0 +1,46 @@ +increments('id'); + $table->string('name'); + $table->string('description')->nullable(); + $table->string('discount_type'); + $table->decimal('quantity', 15, 2); + $table->decimal('discount', 15, 2)->nullable(); + $table->unsignedBigInteger('discount_val')->nullable(); + $table->unsignedBigInteger('price'); + $table->unsignedBigInteger('tax'); + $table->unsignedBigInteger('total'); + $table->integer('item_id')->unsigned()->nullable(); + $table->foreign('item_id')->references('id')->on('items')->onDelete('cascade'); + $table->integer('estimate_id')->unsigned(); + $table->foreign('estimate_id')->references('id')->on('estimates')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('estimate_items'); + } +} diff --git a/crater/database/migrations/2018_11_02_133825_create_ expense_categories_table.php b/crater/database/migrations/2018_11_02_133825_create_ expense_categories_table.php new file mode 100644 index 0000000..9017522 --- /dev/null +++ b/crater/database/migrations/2018_11_02_133825_create_ expense_categories_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->string('name'); + $table->string('description')->nullable(); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('expense_categories'); + } +} diff --git a/crater/database/migrations/2018_11_02_133956_create_expenses_table.php b/crater/database/migrations/2018_11_02_133956_create_expenses_table.php new file mode 100644 index 0000000..1e738ce --- /dev/null +++ b/crater/database/migrations/2018_11_02_133956_create_expenses_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->date('expense_date'); + $table->string('attachment_receipt')->nullable(); + $table->unsignedBigInteger('amount'); + $table->string('notes')->nullable(); + $table->integer('expense_category_id')->unsigned(); + $table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('expenses'); + } +} diff --git a/crater/database/migrations/2019_08_30_072639_create_addresses_table.php b/crater/database/migrations/2019_08_30_072639_create_addresses_table.php new file mode 100644 index 0000000..c9fb5b2 --- /dev/null +++ b/crater/database/migrations/2019_08_30_072639_create_addresses_table.php @@ -0,0 +1,44 @@ +bigIncrements('id'); + $table->string('name')->nullable(); + $table->string('address_street_1')->nullable(); + $table->string('address_street_2')->nullable(); + $table->string('city')->nullable(); + $table->string('state')->nullable(); + $table->integer('country_id')->unsigned()->nullable(); + $table->foreign('country_id')->references('id')->on('countries'); + $table->string('zip')->nullable(); + $table->string('phone')->nullable(); + $table->string('fax')->nullable(); + $table->string('type')->nullable(); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('addresses'); + } +} diff --git a/crater/database/migrations/2019_09_02_053155_create_payment_methods_table.php b/crater/database/migrations/2019_09_02_053155_create_payment_methods_table.php new file mode 100644 index 0000000..43961fb --- /dev/null +++ b/crater/database/migrations/2019_09_02_053155_create_payment_methods_table.php @@ -0,0 +1,36 @@ +increments('id'); + $table->string('name'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('payment_methods'); + } +} diff --git a/crater/database/migrations/2019_09_03_135234_create_payments_table.php b/crater/database/migrations/2019_09_03_135234_create_payments_table.php new file mode 100644 index 0000000..2ac1893 --- /dev/null +++ b/crater/database/migrations/2019_09_03_135234_create_payments_table.php @@ -0,0 +1,44 @@ +bigIncrements('id'); + $table->string('payment_number'); + $table->date('payment_date'); + $table->text('notes')->nullable(); + $table->unsignedBigInteger('amount'); + $table->string('unique_hash')->nullable(); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->integer('invoice_id')->unsigned()->nullable(); + $table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->integer('payment_method_id')->unsigned()->nullable(); + $table->foreign('payment_method_id')->references('id')->on('payment_methods')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('payments'); + } +} diff --git a/crater/database/migrations/2019_09_14_120124_create_media_table.php b/crater/database/migrations/2019_09_14_120124_create_media_table.php new file mode 100644 index 0000000..d35782b --- /dev/null +++ b/crater/database/migrations/2019_09_14_120124_create_media_table.php @@ -0,0 +1,38 @@ +bigIncrements('id'); + $table->morphs('model'); + $table->string('collection_name'); + $table->string('name'); + $table->string('file_name'); + $table->string('mime_type')->nullable(); + $table->string('disk'); + $table->unsignedInteger('size'); + $table->text('manipulations'); + $table->text('custom_properties'); + $table->text('responsive_images'); + $table->unsignedInteger('order_column')->nullable(); + $table->nullableTimestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + { + Schema::dropIfExists('media'); + } +} diff --git a/crater/database/migrations/2019_09_21_052540_create_tax_types_table.php b/crater/database/migrations/2019_09_21_052540_create_tax_types_table.php new file mode 100644 index 0000000..0e2eb3c --- /dev/null +++ b/crater/database/migrations/2019_09_21_052540_create_tax_types_table.php @@ -0,0 +1,38 @@ +increments('id'); + $table->string('name'); + $table->decimal('percent', 5, 2); + $table->tinyInteger('compound_tax')->default(0); + $table->tinyInteger('collective_tax')->default(0); + $table->text('description')->nullable(); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('tax_types'); + } +} diff --git a/crater/database/migrations/2019_09_21_052548_create_taxes_table.php b/crater/database/migrations/2019_09_21_052548_create_taxes_table.php new file mode 100644 index 0000000..dae0bfa --- /dev/null +++ b/crater/database/migrations/2019_09_21_052548_create_taxes_table.php @@ -0,0 +1,49 @@ +increments('id'); + $table->integer('tax_type_id')->unsigned(); + $table->foreign('tax_type_id')->references('id')->on('tax_types'); + $table->integer('invoice_id')->unsigned()->nullable(); + $table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade'); + $table->integer('estimate_id')->unsigned()->nullable(); + $table->foreign('estimate_id')->references('id')->on('estimates')->onDelete('cascade'); + $table->integer('invoice_item_id')->unsigned()->nullable(); + $table->foreign('invoice_item_id')->references('id')->on('invoice_items')->onDelete('cascade'); + $table->integer('estimate_item_id')->unsigned()->nullable(); + $table->foreign('estimate_item_id')->references('id')->on('estimate_items')->onDelete('cascade'); + $table->integer('item_id')->unsigned()->nullable(); + $table->foreign('item_id')->references('id')->on('items')->onDelete('cascade'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->string('name'); + $table->unsignedBigInteger('amount'); + $table->decimal('percent', 5, 2); + $table->tinyInteger('compound_tax')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('taxes'); + } +} diff --git a/crater/database/migrations/2019_09_26_145012_create_company_settings_table.php b/crater/database/migrations/2019_09_26_145012_create_company_settings_table.php new file mode 100644 index 0000000..ca030e3 --- /dev/null +++ b/crater/database/migrations/2019_09_26_145012_create_company_settings_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->string('option'); + $table->string('value'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('company_settings'); + } +} diff --git a/crater/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/crater/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..3ce0002 --- /dev/null +++ b/crater/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,36 @@ +bigIncrements('id'); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('personal_access_tokens'); + } +} diff --git a/crater/database/migrations/2020_02_01_063235_create_custom_fields_table.php b/crater/database/migrations/2020_02_01_063235_create_custom_fields_table.php new file mode 100644 index 0000000..2d695a2 --- /dev/null +++ b/crater/database/migrations/2020_02_01_063235_create_custom_fields_table.php @@ -0,0 +1,48 @@ +bigIncrements('id'); + $table->string('name'); + $table->string('slug'); + $table->string('label'); + $table->string('model_type'); + $table->string('type'); + $table->string('placeholder')->nullable(); + $table->json('options')->nullable(); + $table->boolean('boolean_answer')->nullable(); + $table->date('date_answer')->nullable(); + $table->time('time_answer')->nullable(); + $table->text('string_answer')->nullable(); + $table->unsignedBigInteger('number_answer')->nullable(); + $table->dateTime('date_time_answer')->nullable(); + $table->boolean('is_required')->default(false); + $table->unsignedBigInteger('order')->default(1); + $table->integer('company_id')->unsigned(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('custom_fields'); + } +} diff --git a/crater/database/migrations/2020_02_01_063509_create_custom_field_values_table.php b/crater/database/migrations/2020_02_01_063509_create_custom_field_values_table.php new file mode 100644 index 0000000..7701013 --- /dev/null +++ b/crater/database/migrations/2020_02_01_063509_create_custom_field_values_table.php @@ -0,0 +1,44 @@ +bigIncrements('id'); + $table->string('custom_field_valuable_type'); + $table->unsignedInteger('custom_field_valuable_id'); + $table->string('type'); + $table->boolean('boolean_answer')->nullable(); + $table->date('date_answer')->nullable(); + $table->time('time_answer')->nullable(); + $table->text('string_answer')->nullable(); + $table->unsignedBigInteger('number_answer')->nullable(); + $table->dateTime('date_time_answer')->nullable(); + $table->unsignedBigInteger('custom_field_id'); + $table->foreign('custom_field_id')->references('id')->on('custom_fields'); + $table->integer('company_id')->unsigned(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('answers'); + } +} diff --git a/crater/database/migrations/2020_05_12_154129_add_user_id_to_expenses_table.php b/crater/database/migrations/2020_05_12_154129_add_user_id_to_expenses_table.php new file mode 100644 index 0000000..130994c --- /dev/null +++ b/crater/database/migrations/2020_05_12_154129_add_user_id_to_expenses_table.php @@ -0,0 +1,33 @@ +integer('user_id')->unsigned()->nullable(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropColumn('paid'); + }); + } +} diff --git a/crater/database/migrations/2020_09_07_103054_create_file_disks_table.php b/crater/database/migrations/2020_09_07_103054_create_file_disks_table.php new file mode 100644 index 0000000..b137fe6 --- /dev/null +++ b/crater/database/migrations/2020_09_07_103054_create_file_disks_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('name'); + $table->string('type')->default('REMOTE'); + $table->string('driver'); + $table->boolean('set_as_default')->default(false); + $table->json('credentials'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('file_disks'); + } +} diff --git a/crater/database/migrations/2020_09_22_153617_add_columns_to_media_table.php b/crater/database/migrations/2020_09_22_153617_add_columns_to_media_table.php new file mode 100644 index 0000000..2410fd9 --- /dev/null +++ b/crater/database/migrations/2020_09_22_153617_add_columns_to_media_table.php @@ -0,0 +1,34 @@ +uuid('uuid')->nullable(); + $table->string('conversions_disk')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('media', function (Blueprint $table) { + $table->dropColumn('uuid'); + $table->dropColumn('conversions_disk'); + }); + } +} diff --git a/crater/database/migrations/2020_09_26_100951_create_user_settings_table.php b/crater/database/migrations/2020_09_26_100951_create_user_settings_table.php new file mode 100644 index 0000000..d59beff --- /dev/null +++ b/crater/database/migrations/2020_09_26_100951_create_user_settings_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('key'); + $table->text('value'); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_settings'); + } +} diff --git a/crater/database/migrations/2020_10_01_102913_add_company_to_addresses_table.php b/crater/database/migrations/2020_10_01_102913_add_company_to_addresses_table.php new file mode 100644 index 0000000..570fe91 --- /dev/null +++ b/crater/database/migrations/2020_10_01_102913_add_company_to_addresses_table.php @@ -0,0 +1,34 @@ +integer('user_id')->unsigned()->nullable()->change(); + $table->unsignedInteger('company_id')->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('addresses', function (Blueprint $table) { + $table->dropForeign(['company_id']); + }); + } +} diff --git a/crater/database/migrations/2020_10_17_074745_create_notes_table.php b/crater/database/migrations/2020_10_17_074745_create_notes_table.php new file mode 100644 index 0000000..84888bb --- /dev/null +++ b/crater/database/migrations/2020_10_17_074745_create_notes_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('type'); + $table->string('name'); + $table->text('notes'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notes'); + } +} diff --git a/crater/database/migrations/2020_10_24_091934_change_value_column_to_text_on_company_settings_table.php b/crater/database/migrations/2020_10_24_091934_change_value_column_to_text_on_company_settings_table.php new file mode 100644 index 0000000..9b0f1c1 --- /dev/null +++ b/crater/database/migrations/2020_10_24_091934_change_value_column_to_text_on_company_settings_table.php @@ -0,0 +1,32 @@ +text('value')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('company_settings', function (Blueprint $table) { + $table->string('value')->change(); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_050206_add_creator_in_invoices_table.php b/crater/database/migrations/2020_11_23_050206_add_creator_in_invoices_table.php new file mode 100644 index 0000000..81fd590 --- /dev/null +++ b/crater/database/migrations/2020_11_23_050206_add_creator_in_invoices_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_050252_add_creator_in_estimates_table.php b/crater/database/migrations/2020_11_23_050252_add_creator_in_estimates_table.php new file mode 100644 index 0000000..59263bb --- /dev/null +++ b/crater/database/migrations/2020_11_23_050252_add_creator_in_estimates_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_050316_add_creator_in_payments_table.php b/crater/database/migrations/2020_11_23_050316_add_creator_in_payments_table.php new file mode 100644 index 0000000..e16cc46 --- /dev/null +++ b/crater/database/migrations/2020_11_23_050316_add_creator_in_payments_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_050333_add_creator_in_expenses_table.php b/crater/database/migrations/2020_11_23_050333_add_creator_in_expenses_table.php new file mode 100644 index 0000000..02e0fd5 --- /dev/null +++ b/crater/database/migrations/2020_11_23_050333_add_creator_in_expenses_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_050406_add_creator_in_items_table.php b/crater/database/migrations/2020_11_23_050406_add_creator_in_items_table.php new file mode 100644 index 0000000..5d1a8b8 --- /dev/null +++ b/crater/database/migrations/2020_11_23_050406_add_creator_in_items_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('items', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_065815_add_creator_in_users_table.php b/crater/database/migrations/2020_11_23_065815_add_creator_in_users_table.php new file mode 100644 index 0000000..15797a1 --- /dev/null +++ b/crater/database/migrations/2020_11_23_065815_add_creator_in_users_table.php @@ -0,0 +1,33 @@ +unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['creator_id']); + }); + } +} diff --git a/crater/database/migrations/2020_11_23_074154_create_email_logs_table.php b/crater/database/migrations/2020_11_23_074154_create_email_logs_table.php new file mode 100644 index 0000000..89923cf --- /dev/null +++ b/crater/database/migrations/2020_11_23_074154_create_email_logs_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('from'); + $table->string('to'); + $table->string('subject'); + $table->text('body'); + $table->string('mailable_type'); + $table->string('mailable_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('email_logs'); + } +} diff --git a/crater/database/migrations/2020_12_02_064933_update_crater_version_320.php b/crater/database/migrations/2020_12_02_064933_update_crater_version_320.php new file mode 100644 index 0000000..54a901f --- /dev/null +++ b/crater/database/migrations/2020_12_02_064933_update_crater_version_320.php @@ -0,0 +1,29 @@ +fileDiskSeed(); + + Setting::setSetting('version', '4.0.0'); + + $user = User::where('role', 'admin')->first(); + + if ($user && $user->role == 'admin') { + $user->update([ + 'role' => 'super admin', + ]); + + // Update language + $user->setSettings(['language' => CompanySetting::getSetting('language', $user->company_id)]); + + Address::where('user_id', $user->id)->update([ + 'company_id' => $user->company_id, + 'user_id' => null + ]); + + // Update company settings + $this->updateCompanySettings($user); + + // Update Creator + $this->updateCreatorId($user); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } + + private function fileDiskSeed() + { + $privateDisk = [ + 'root' => config('filesystems.disks.local.root'), + 'driver' => 'local', + ]; + + $publicDisk = [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + ]; + + FileDisk::create([ + 'credentials' => json_encode($publicDisk), + 'name' => 'local_public', + 'type' => 'SYSTEM', + 'driver' => 'local', + 'set_as_default' => false, + ]); + + FileDisk::create([ + 'credentials' => json_encode($privateDisk), + 'name' => 'local_private', + 'type' => 'SYSTEM', + 'driver' => 'local', + 'set_as_default' => true, + ]); + } + + private function updateCreatorId($user) + { + Invoice::where('company_id', '<>', null)->update(['creator_id' => $user->id]); + Estimate::where('company_id', '<>', null)->update(['creator_id' => $user->id]); + Expense::where('company_id', '<>', null)->update(['creator_id' => $user->id]); + Payment::where('company_id', '<>', null)->update(['creator_id' => $user->id]); + Item::where('company_id', '<>', null)->update(['creator_id' => $user->id]); + User::where('role', 'customer')->update(['creator_id' => $user->id]); + } + + private function updateCompanySettings($user) + { + $defaultInvoiceEmailBody = 'You have received a new invoice from {COMPANY_NAME}.
Please download using the button below:'; + $defaultEstimateEmailBody = 'You have received a new estimate from {COMPANY_NAME}.
Please download using the button below:'; + $defaultPaymentEmailBody = 'Thank you for the payment.
Please download your payment receipt using the button below:'; + $billingAddressFormat = '

{BILLING_ADDRESS_NAME}

{BILLING_ADDRESS_STREET_1}

{BILLING_ADDRESS_STREET_2}

{BILLING_CITY} {BILLING_STATE}

{BILLING_COUNTRY} {BILLING_ZIP_CODE}

{BILLING_PHONE}

'; + $shippingAddressFormat = '

{SHIPPING_ADDRESS_NAME}

{SHIPPING_ADDRESS_STREET_1}

{SHIPPING_ADDRESS_STREET_2}

{SHIPPING_CITY} {SHIPPING_STATE}

{SHIPPING_COUNTRY} {SHIPPING_ZIP_CODE}

{SHIPPING_PHONE}

'; + $companyAddressFormat = '

{COMPANY_NAME}

{COMPANY_ADDRESS_STREET_1}

{COMPANY_ADDRESS_STREET_2}

{COMPANY_CITY} {COMPANY_STATE}

{COMPANY_COUNTRY} {COMPANY_ZIP_CODE}

{COMPANY_PHONE}

'; + $paymentFromCustomerAddress = '

{BILLING_ADDRESS_NAME}

{BILLING_ADDRESS_STREET_1}

{BILLING_ADDRESS_STREET_2}

{BILLING_CITY} {BILLING_STATE} {BILLING_ZIP_CODE}

{BILLING_COUNTRY}

{BILLING_PHONE}

'; + + $settings = [ + 'invoice_auto_generate' => 'YES', + 'payment_auto_generate' => 'YES', + 'estimate_auto_generate' => 'YES', + 'save_pdf_to_disk' => 'NO', + 'invoice_mail_body' => $defaultInvoiceEmailBody, + 'estimate_mail_body' => $defaultEstimateEmailBody, + 'payment_mail_body' => $defaultPaymentEmailBody, + 'invoice_company_address_format' => $companyAddressFormat, + 'invoice_shipping_address_format' => $shippingAddressFormat, + 'invoice_billing_address_format' => $billingAddressFormat, + 'estimate_company_address_format' => $companyAddressFormat, + 'estimate_shipping_address_format' => $shippingAddressFormat, + 'estimate_billing_address_format' => $billingAddressFormat, + 'payment_company_address_format' => $companyAddressFormat, + 'payment_from_customer_address_format' => $paymentFromCustomerAddress, + ]; + + CompanySetting::setSettings($settings, $user->company_id); + } +} diff --git a/crater/database/migrations/2020_12_08_065715_change_description_and_notes_column_type.php b/crater/database/migrations/2020_12_08_065715_change_description_and_notes_column_type.php new file mode 100644 index 0000000..a42967e --- /dev/null +++ b/crater/database/migrations/2020_12_08_065715_change_description_and_notes_column_type.php @@ -0,0 +1,46 @@ +text('notes')->nullable()->change(); + }); + + Schema::table('expenses', function (Blueprint $table) { + $table->text('notes')->nullable()->change(); + }); + + Schema::table('estimate_items', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('invoice_items', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('items', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2020_12_08_133131_update_crater_version_401.php b/crater/database/migrations/2020_12_08_133131_update_crater_version_401.php new file mode 100644 index 0000000..d39a91f --- /dev/null +++ b/crater/database/migrations/2020_12_08_133131_update_crater_version_401.php @@ -0,0 +1,27 @@ +string('template_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('template_name'); + }); + } +} diff --git a/crater/database/migrations/2020_12_14_045310_add_template_name_to_estimates_table.php b/crater/database/migrations/2020_12_14_045310_add_template_name_to_estimates_table.php new file mode 100644 index 0000000..0028d9c --- /dev/null +++ b/crater/database/migrations/2020_12_14_045310_add_template_name_to_estimates_table.php @@ -0,0 +1,32 @@ +string('template_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + $table->dropColumn('template_name'); + }); + } +} diff --git a/crater/database/migrations/2020_12_14_051450_remove_template_id_from_invoices_and_estimates_table.php b/crater/database/migrations/2020_12_14_051450_remove_template_id_from_invoices_and_estimates_table.php new file mode 100644 index 0000000..d0b0cc5 --- /dev/null +++ b/crater/database/migrations/2020_12_14_051450_remove_template_id_from_invoices_and_estimates_table.php @@ -0,0 +1,63 @@ +map(function ($invoice) { + $invoice->template_name = 'invoice'.$invoice->invoice_template_id; + $invoice->save(); + }); + + Schema::table('invoices', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['invoice_template_id']); + } + $table->dropColumn('invoice_template_id'); + }); + } + + if (Schema::hasColumn('estimates', 'estimate_template_id')) { + $estimates = Estimate::all(); + + $estimates->map(function ($estimate) { + $estimate->template_name = 'estimate'.$estimate->estimate_template_id; + $estimate->save(); + }); + + Schema::table('estimates', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['estimate_template_id']); + } + $table->dropColumn('estimate_template_id'); + }); + } + + Schema::dropIfExists('invoice_templates'); + Schema::dropIfExists('estimate_templates'); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2020_12_23_061302_update_crater_version_402.php b/crater/database/migrations/2020_12_23_061302_update_crater_version_402.php new file mode 100644 index 0000000..d6a5854 --- /dev/null +++ b/crater/database/migrations/2020_12_23_061302_update_crater_version_402.php @@ -0,0 +1,27 @@ +string('unit_name')->nullable()->after('quantity'); + }); + Schema::table('estimate_items', function (Blueprint $table) { + $table->string('unit_name')->nullable()->after('quantity'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_items', function (Blueprint $table) { + $table->dropColumn('unit_name'); + }); + Schema::table('estimate_items', function (Blueprint $table) { + $table->dropColumn('unit_name'); + }); + } +} diff --git a/crater/database/migrations/2021_03_23_145012_add_number_length_setting.php b/crater/database/migrations/2021_03_23_145012_add_number_length_setting.php new file mode 100644 index 0000000..0a0a8a2 --- /dev/null +++ b/crater/database/migrations/2021_03_23_145012_add_number_length_setting.php @@ -0,0 +1,45 @@ +first(); + + if ($user) { + $invoice_number_length = CompanySetting::getSetting('invoice_number_length', $user->company_id); + if (empty($invoice_number_length)) { + CompanySetting::setSettings(['invoice_number_length' => '6'], $user->company_id); + } + + $estimate_number_length = CompanySetting::getSetting('estimate_number_length', $user->company_id); + if (empty($estimate_number_length)) { + CompanySetting::setSettings(['estimate_number_length' => '6'], $user->company_id); + } + + $payment_number_length = CompanySetting::getSetting('payment_number_length', $user->company_id); + if (empty($payment_number_length)) { + CompanySetting::setSettings(['payment_number_length' => '6'], $user->company_id); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_05_05_063533_update_crater_version_410.php b/crater/database/migrations/2021_05_05_063533_update_crater_version_410.php new file mode 100644 index 0000000..a8d3413 --- /dev/null +++ b/crater/database/migrations/2021_05_05_063533_update_crater_version_410.php @@ -0,0 +1,27 @@ +bigIncrements('id'); + $table->string('name'); + $table->string('title')->nullable(); + $table->bigInteger('entity_id')->unsigned()->nullable(); + $table->string('entity_type')->nullable(); + $table->boolean('only_owned')->default(false); + $table->json('options')->nullable(); + $table->integer('scope')->nullable()->index(); + $table->timestamps(); + }); + + Schema::create(Models::table('roles'), function (Blueprint $table) { + $table->bigIncrements('id'); + $table->string('name'); + $table->string('title')->nullable(); + $table->integer('level')->unsigned()->nullable(); + $table->integer('scope')->nullable()->index(); + $table->timestamps(); + + $table->unique( + ['name', 'scope'], + 'roles_name_unique' + ); + }); + + Schema::create(Models::table('assigned_roles'), function (Blueprint $table) { + $table->bigIncrements('id'); + $table->bigInteger('role_id')->unsigned()->index(); + $table->bigInteger('entity_id')->unsigned(); + $table->string('entity_type'); + $table->bigInteger('restricted_to_id')->unsigned()->nullable(); + $table->string('restricted_to_type')->nullable(); + $table->integer('scope')->nullable()->index(); + + $table->index( + ['entity_id', 'entity_type', 'scope'], + 'assigned_roles_entity_index' + ); + + $table->foreign('role_id') + ->references('id')->on(Models::table('roles')) + ->onUpdate('cascade')->onDelete('cascade'); + }); + + Schema::create(Models::table('permissions'), function (Blueprint $table) { + $table->bigIncrements('id'); + $table->bigInteger('ability_id')->unsigned()->index(); + $table->bigInteger('entity_id')->unsigned()->nullable(); + $table->string('entity_type')->nullable(); + $table->boolean('forbidden')->default(false); + $table->integer('scope')->nullable()->index(); + + $table->index( + ['entity_id', 'entity_type', 'scope'], + 'permissions_entity_index' + ); + + $table->foreign('ability_id') + ->references('id')->on(Models::table('abilities')) + ->onUpdate('cascade')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(Models::table('permissions')); + Schema::drop(Models::table('assigned_roles')); + Schema::drop(Models::table('roles')); + Schema::drop(Models::table('abilities')); + } +} diff --git a/crater/database/migrations/2021_06_28_111647_create_customers_table.php b/crater/database/migrations/2021_06_28_111647_create_customers_table.php new file mode 100644 index 0000000..1e073c4 --- /dev/null +++ b/crater/database/migrations/2021_06_28_111647_create_customers_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique()->nullable(); + $table->string('phone')->nullable(); + $table->string('password')->nullable(); + $table->rememberToken(); + $table->string('facebook_id')->nullable(); + $table->string('google_id')->nullable(); + $table->string('github_id')->nullable(); + $table->string('contact_name')->nullable(); + $table->string('company_name')->nullable(); + $table->string('website')->nullable(); + $table->boolean('enable_portal')->nullable(); + $table->integer('currency_id')->unsigned()->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('customers'); + } +} diff --git a/crater/database/migrations/2021_06_28_120010_add_customer_id_to_estimates_table.php b/crater/database/migrations/2021_06_28_120010_add_customer_id_to_estimates_table.php new file mode 100644 index 0000000..c276201 --- /dev/null +++ b/crater/database/migrations/2021_06_28_120010_add_customer_id_to_estimates_table.php @@ -0,0 +1,36 @@ +unsignedBigInteger('customer_id')->nullable(); + $table->foreign('customer_id')->references('id')->on('customers'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['customer_id']); + } + $table->dropColumn('customer_id'); + }); + } +} diff --git a/crater/database/migrations/2021_06_28_120133_add_customer_id_to_expenses_table.php b/crater/database/migrations/2021_06_28_120133_add_customer_id_to_expenses_table.php new file mode 100644 index 0000000..ebe905c --- /dev/null +++ b/crater/database/migrations/2021_06_28_120133_add_customer_id_to_expenses_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('customer_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropColumn('customer_id'); + }); + } +} diff --git a/crater/database/migrations/2021_06_28_120208_add_customer_id_to_invoices_table.php b/crater/database/migrations/2021_06_28_120208_add_customer_id_to_invoices_table.php new file mode 100644 index 0000000..5df3fb3 --- /dev/null +++ b/crater/database/migrations/2021_06_28_120208_add_customer_id_to_invoices_table.php @@ -0,0 +1,36 @@ +unsignedBigInteger('customer_id')->nullable(); + $table->foreign('customer_id')->references('id')->on('customers'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['customer_id']); + } + $table->dropColumn('customer_id'); + }); + } +} diff --git a/crater/database/migrations/2021_06_28_120231_add_customer_id_to_payments_table.php b/crater/database/migrations/2021_06_28_120231_add_customer_id_to_payments_table.php new file mode 100644 index 0000000..31e2240 --- /dev/null +++ b/crater/database/migrations/2021_06_28_120231_add_customer_id_to_payments_table.php @@ -0,0 +1,39 @@ +unsignedInteger('user_id')->nullable()->change(); + $table->unsignedBigInteger('customer_id')->nullable(); + $table->foreign('customer_id')->references('id')->on('customers'); + }); + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['customer_id']); + } + $table->dropColumn('customer_id'); + }); + } +} diff --git a/crater/database/migrations/2021_06_29_052745_add_customer_id_to_addresses_table.php b/crater/database/migrations/2021_06_29_052745_add_customer_id_to_addresses_table.php new file mode 100644 index 0000000..ca435c4 --- /dev/null +++ b/crater/database/migrations/2021_06_29_052745_add_customer_id_to_addresses_table.php @@ -0,0 +1,36 @@ +unsignedBigInteger('customer_id')->nullable(); + $table->foreign('customer_id')->references('id')->on('customers'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('addresses', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['customer_id']); + } + $table->dropColumn('customer_id'); + }); + } +} diff --git a/crater/database/migrations/2021_06_30_062411_update_customer_id_in_all_tables.php b/crater/database/migrations/2021_06_30_062411_update_customer_id_in_all_tables.php new file mode 100644 index 0000000..eabbe75 --- /dev/null +++ b/crater/database/migrations/2021_06_30_062411_update_customer_id_in_all_tables.php @@ -0,0 +1,124 @@ +get(); + + $users->makeVisible('password', 'remember_token'); + + if ($users) { + foreach ($users as $user) { + $newCustomer = Customer::create($user->toArray()); + + Address::where('user_id', $user->id)->update([ + 'customer_id' => $newCustomer->id, + 'user_id' => null + ]); + + Expense::where('user_id', $user->id)->update([ + 'customer_id' => $newCustomer->id, + 'user_id' => null + ]); + + Estimate::where('user_id', $user->id)->update([ + 'customer_id' => $newCustomer->id, + 'user_id' => null + ]); + + Invoice::where('user_id', $user->id)->update([ + 'customer_id' => $newCustomer->id, + 'user_id' => null + ]); + + Payment::where('user_id', $user->id)->update([ + 'customer_id' => $newCustomer->id, + 'user_id' => null + ]); + + CustomFieldValue::where('custom_field_valuable_id', $user->id) + ->where('custom_field_valuable_type', 'Crater\Models\User') + ->update([ + 'custom_field_valuable_type' => 'Crater\Models\Customer', + 'custom_field_valuable_id' => $newCustomer->id + ]); + } + + $customFields = CustomField::where('model_type', 'User')->get(); + + if ($customFields) { + foreach ($customFields as $customField) { + $customField->model_type = "Customer"; + $customField->slug = Str::upper('CUSTOM_'.$customField->model_type.'_'.Str::slug($customField->label, '_')); + $customField->save(); + } + } + } + + Schema::table('estimates', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['user_id']); + } + $table->dropColumn('user_id'); + }); + + Schema::table('expenses', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['user_id']); + } + $table->dropColumn('user_id'); + }); + + Schema::table('invoices', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['user_id']); + } + $table->dropColumn('user_id'); + }); + + Schema::table('payments', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['user_id']); + } + $table->dropColumn('user_id'); + }); + + Schema::table('items', function (Blueprint $table) { + $table->dropColumn('unit'); + }); + + $users = User::where('role', 'customer') + ->delete(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_07_01_060700_create_user_company_table.php b/crater/database/migrations/2021_07_01_060700_create_user_company_table.php new file mode 100644 index 0000000..48013b6 --- /dev/null +++ b/crater/database/migrations/2021_07_01_060700_create_user_company_table.php @@ -0,0 +1,35 @@ +id(); + $table->unsignedInteger('user_id')->nullable(); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->unsignedInteger('company_id')->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_company'); + } +} diff --git a/crater/database/migrations/2021_07_05_100256_change_relationship_of_company.php b/crater/database/migrations/2021_07_05_100256_change_relationship_of_company.php new file mode 100644 index 0000000..e98ca27 --- /dev/null +++ b/crater/database/migrations/2021_07_05_100256_change_relationship_of_company.php @@ -0,0 +1,44 @@ +companies()->attach($user->company_id); + $user->company_id = null; + $user->save(); + } + } + + Schema::table('users', function (Blueprint $table) { + if (config('database.default') !== 'sqlite') { + $table->dropForeign(['company_id']); + } + $table->dropColumn('company_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_07_06_070204_add_owner_id_to_companies_table.php b/crater/database/migrations/2021_07_06_070204_add_owner_id_to_companies_table.php new file mode 100644 index 0000000..2aba374 --- /dev/null +++ b/crater/database/migrations/2021_07_06_070204_add_owner_id_to_companies_table.php @@ -0,0 +1,58 @@ +string('slug')->nullable(); + $table->unsignedInteger('owner_id')->nullable(); + $table->foreign('owner_id')->references('id')->on('users'); + }); + + $user = User::where('role', 'super admin')->first(); + + $companies = Company::all(); + + if ($companies && $user) { + foreach ($companies as $company) { + $company->owner_id = $user->id; + $company->slug = Str::slug($company->name); + $company->save(); + + $company->setupRoles(); + $user->assign('super admin'); + + $users = User::where('role', 'admin')->get(); + $users->map(function ($user) { + $user->assign('super admin'); + }); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('companies', function (Blueprint $table) { + $table->dropColumn('slug'); + $table->dropForeign(['owner_id']); + }); + } +} diff --git a/crater/database/migrations/2021_07_08_110940_add_company_to_notes_table.php b/crater/database/migrations/2021_07_08_110940_add_company_to_notes_table.php new file mode 100644 index 0000000..939d883 --- /dev/null +++ b/crater/database/migrations/2021_07_08_110940_add_company_to_notes_table.php @@ -0,0 +1,45 @@ +unsignedInteger('company_id')->nullable(); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + }); + + $user = User::where('role', 'super admin')->first(); + + if ($user) { + $notes = Note::where('company_id', null)->get(); + $notes->map(function ($note) use ($user) { + $note->company_id = $user->companies()->first()->id; + $note->save(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('notes', function (Blueprint $table) { + $table->dropForeign(['company_id']); + }); + } +} diff --git a/crater/database/migrations/2021_07_09_063502_create_recurring_invoices_table.php b/crater/database/migrations/2021_07_09_063502_create_recurring_invoices_table.php new file mode 100644 index 0000000..76baab0 --- /dev/null +++ b/crater/database/migrations/2021_07_09_063502_create_recurring_invoices_table.php @@ -0,0 +1,59 @@ +id(); + $table->dateTime('starts_at', $precision = 0); + $table->boolean('send_automatically')->default(false); + $table->unsignedBigInteger('customer_id')->nullable(); + $table->foreign('customer_id')->references('id')->on('customers'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->enum('status', ['COMPLETED', 'ON_HOLD', 'ACTIVE'])->default('ACTIVE'); + $table->dateTime('next_invoice_at', $precision = 0)->nullable(); + $table->unsignedInteger('creator_id')->nullable(); + $table->foreign('creator_id')->references('id')->on('users'); + $table->string('frequency'); + $table->enum('limit_by', ['NONE', 'COUNT', 'DATE'])->default('NONE'); + $table->integer('limit_count')->nullable(); + $table->date('limit_date')->nullable(); + $table->unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + $table->decimal('exchange_rate', 19, 6)->nullable(); + $table->string('tax_per_item'); + $table->string('discount_per_item'); + $table->text('notes')->nullable(); + $table->string('discount_type')->nullable(); + $table->decimal('discount', 15, 2)->nullable(); + $table->unsignedBigInteger('discount_val')->nullable(); + $table->unsignedBigInteger('sub_total'); + $table->unsignedBigInteger('total'); + $table->unsignedBigInteger('tax'); + $table->string('template_name')->nullable(); + $table->unsignedBigInteger('due_amount'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('recurring_invoices'); + } +} diff --git a/crater/database/migrations/2021_07_09_063712_add_recurring_invoice_id_to_invoices_table.php b/crater/database/migrations/2021_07_09_063712_add_recurring_invoice_id_to_invoices_table.php new file mode 100644 index 0000000..bacec8d --- /dev/null +++ b/crater/database/migrations/2021_07_09_063712_add_recurring_invoice_id_to_invoices_table.php @@ -0,0 +1,33 @@ +unsignedBigInteger('recurring_invoice_id')->nullable(); + $table->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('recurring_invoice_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_09_063755_add_recurring_invoice_id_to_invoice_items_table.php b/crater/database/migrations/2021_07_09_063755_add_recurring_invoice_id_to_invoice_items_table.php new file mode 100644 index 0000000..063c6d3 --- /dev/null +++ b/crater/database/migrations/2021_07_09_063755_add_recurring_invoice_id_to_invoice_items_table.php @@ -0,0 +1,34 @@ +integer('invoice_id')->unsigned()->nullable()->change(); + $table->unsignedBigInteger('recurring_invoice_id')->nullable(); + $table->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_items', function (Blueprint $table) { + $table->dropColumn('recurring_invoice_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_15_054753_make_due_date_optional_in_invoices_table.php b/crater/database/migrations/2021_07_15_054753_make_due_date_optional_in_invoices_table.php new file mode 100644 index 0000000..0caf39e --- /dev/null +++ b/crater/database/migrations/2021_07_15_054753_make_due_date_optional_in_invoices_table.php @@ -0,0 +1,32 @@ +date('due_date')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/crater/database/migrations/2021_07_15_054929_make_expiry_date_optional_estimates_table.php b/crater/database/migrations/2021_07_15_054929_make_expiry_date_optional_estimates_table.php new file mode 100644 index 0000000..455e3c4 --- /dev/null +++ b/crater/database/migrations/2021_07_15_054929_make_expiry_date_optional_estimates_table.php @@ -0,0 +1,30 @@ +date('expiry_date')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_07_16_072458_add_base_columns_into_invoices_table.php b/crater/database/migrations/2021_07_16_072458_add_base_columns_into_invoices_table.php new file mode 100644 index 0000000..7d45570 --- /dev/null +++ b/crater/database/migrations/2021_07_16_072458_add_base_columns_into_invoices_table.php @@ -0,0 +1,44 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_discount_val')->nullable(); + $table->unsignedBigInteger('base_sub_total')->nullable(); + $table->unsignedBigInteger('base_total')->nullable(); + $table->unsignedBigInteger('base_tax')->nullable(); + $table->unsignedBigInteger('base_due_amount')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn([ + 'base_discount_val', + 'exchange_rate', + 'base_sub_total', + 'base_total', + 'base_tax', + 'base_due_amount' + ]); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_072925_add_base_columns_into_invoice_items_table.php b/crater/database/migrations/2021_07_16_072925_add_base_columns_into_invoice_items_table.php new file mode 100644 index 0000000..dff9811 --- /dev/null +++ b/crater/database/migrations/2021_07_16_072925_add_base_columns_into_invoice_items_table.php @@ -0,0 +1,42 @@ +unsignedBigInteger('base_price')->nullable(); + $table->decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_discount_val')->nullable(); + $table->unsignedBigInteger('base_tax')->nullable(); + $table->unsignedBigInteger('base_total')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_items', function (Blueprint $table) { + $table->dropColumn([ + 'base_price', + 'exchange_rate', + 'base_discount_val', + 'base_tax', + 'base_total' + ]); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_073040_add_base_columns_into_estimates_table.php b/crater/database/migrations/2021_07_16_073040_add_base_columns_into_estimates_table.php new file mode 100644 index 0000000..d08cb8e --- /dev/null +++ b/crater/database/migrations/2021_07_16_073040_add_base_columns_into_estimates_table.php @@ -0,0 +1,42 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_discount_val')->nullable(); + $table->unsignedBigInteger('base_sub_total')->nullable(); + $table->unsignedBigInteger('base_total')->nullable(); + $table->unsignedBigInteger('base_tax')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + $table->dropColumn([ + 'exchange_rate', + 'base_discount_val', + 'base_sub_total', + 'base_total', + 'base_tax', + ]); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_073441_add_base_columns_into_estimate_items_table.php b/crater/database/migrations/2021_07_16_073441_add_base_columns_into_estimate_items_table.php new file mode 100644 index 0000000..4be5f60 --- /dev/null +++ b/crater/database/migrations/2021_07_16_073441_add_base_columns_into_estimate_items_table.php @@ -0,0 +1,42 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_discount_val')->nullable(); + $table->unsignedBigInteger('base_price')->nullable(); + $table->unsignedBigInteger('base_tax')->nullable(); + $table->unsignedBigInteger('base_total')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimate_items', function (Blueprint $table) { + $table->dropColumn([ + 'exchange_rate', + 'base_discount_val', + 'base_price', + 'base_tax', + 'base_total' + ]); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_074810_add_base_column_into_payments_table.php b/crater/database/migrations/2021_07_16_074810_add_base_column_into_payments_table.php new file mode 100644 index 0000000..18aa0f2 --- /dev/null +++ b/crater/database/migrations/2021_07_16_074810_add_base_column_into_payments_table.php @@ -0,0 +1,33 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_amount')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function (Blueprint $table) { + $table->dropColumn('base_amount'); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_075100_add_base_values_into_taxes_table.php b/crater/database/migrations/2021_07_16_075100_add_base_values_into_taxes_table.php new file mode 100644 index 0000000..15ca892 --- /dev/null +++ b/crater/database/migrations/2021_07_16_075100_add_base_values_into_taxes_table.php @@ -0,0 +1,36 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_amount')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('taxes', function (Blueprint $table) { + $table->dropColumn([ + 'exchange_rate', + 'base_amount', + ]); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_080253_add_currency_id_into_invoices_table.php b/crater/database/migrations/2021_07_16_080253_add_currency_id_into_invoices_table.php new file mode 100644 index 0000000..ad754f0 --- /dev/null +++ b/crater/database/migrations/2021_07_16_080253_add_currency_id_into_invoices_table.php @@ -0,0 +1,33 @@ +unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('currency_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_080508_add_currency_id_into_payments_table.php b/crater/database/migrations/2021_07_16_080508_add_currency_id_into_payments_table.php new file mode 100644 index 0000000..ae49674 --- /dev/null +++ b/crater/database/migrations/2021_07_16_080508_add_currency_id_into_payments_table.php @@ -0,0 +1,33 @@ +unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function (Blueprint $table) { + $table->dropColumn('currency_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_080611_add_currency_id_into_items_table.php b/crater/database/migrations/2021_07_16_080611_add_currency_id_into_items_table.php new file mode 100644 index 0000000..370fcf9 --- /dev/null +++ b/crater/database/migrations/2021_07_16_080611_add_currency_id_into_items_table.php @@ -0,0 +1,33 @@ +unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('items', function (Blueprint $table) { + $table->dropColumn('currency_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_080702_add_currency_id_into_taxes_table.php b/crater/database/migrations/2021_07_16_080702_add_currency_id_into_taxes_table.php new file mode 100644 index 0000000..d1745bb --- /dev/null +++ b/crater/database/migrations/2021_07_16_080702_add_currency_id_into_taxes_table.php @@ -0,0 +1,33 @@ +unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('taxes', function (Blueprint $table) { + $table->dropColumn('currency_id'); + }); + } +} diff --git a/crater/database/migrations/2021_07_16_112429_add_currency_id_into_estimates_table.php b/crater/database/migrations/2021_07_16_112429_add_currency_id_into_estimates_table.php new file mode 100644 index 0000000..724ff37 --- /dev/null +++ b/crater/database/migrations/2021_07_16_112429_add_currency_id_into_estimates_table.php @@ -0,0 +1,33 @@ +unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + $table->dropColumn('currency_id'); + }); + } +} diff --git a/crater/database/migrations/2021_08_05_103535_create_exchange_rate_logs_table.php b/crater/database/migrations/2021_08_05_103535_create_exchange_rate_logs_table.php new file mode 100644 index 0000000..11dda3d --- /dev/null +++ b/crater/database/migrations/2021_08_05_103535_create_exchange_rate_logs_table.php @@ -0,0 +1,38 @@ +id(); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->unsignedInteger('base_currency_id')->nullable(); + $table->foreign('base_currency_id')->references('id')->on('currencies'); + $table->unsignedInteger('currency_id')->nullable(); + $table->foreign('currency_id')->references('id')->on('currencies'); + $table->decimal('exchange_rate', 19, 6)->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('exchange_rates'); + } +} diff --git a/crater/database/migrations/2021_08_16_091413_add_tax_per_item_into_items_table.php b/crater/database/migrations/2021_08_16_091413_add_tax_per_item_into_items_table.php new file mode 100644 index 0000000..f8889a2 --- /dev/null +++ b/crater/database/migrations/2021_08_16_091413_add_tax_per_item_into_items_table.php @@ -0,0 +1,44 @@ +boolean('tax_per_item')->default(false); + }); + + $items = Item::with('taxes')->get(); + + if ($items) { + foreach ($items as $item) { + if (! $item->taxes()->get()->isEmpty()) { + $item->tax_per_item = true; + $item->save(); + } + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('items', function (Blueprint $table) { + $table->dropColumn('tax_per_item'); + }); + } +} diff --git a/crater/database/migrations/2021_08_19_063244_add_base_columns_to_expense_table.php b/crater/database/migrations/2021_08_19_063244_add_base_columns_to_expense_table.php new file mode 100644 index 0000000..63c327d --- /dev/null +++ b/crater/database/migrations/2021_08_19_063244_add_base_columns_to_expense_table.php @@ -0,0 +1,38 @@ +decimal('exchange_rate', 19, 6)->nullable(); + $table->unsignedBigInteger('base_amount')->nullable(); + $table->unsignedInteger('currency_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropColumn([ + 'exchange_rate', + 'base_amount', + 'currency_id', + ]); + }); + } +} diff --git a/crater/database/migrations/2021_09_28_081543_create_exchange_rate_providers_table.php b/crater/database/migrations/2021_09_28_081543_create_exchange_rate_providers_table.php new file mode 100644 index 0000000..7a303d0 --- /dev/null +++ b/crater/database/migrations/2021_09_28_081543_create_exchange_rate_providers_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('driver'); + $table->string('key'); + $table->json('currencies')->nullable(); + $table->json('driver_config')->nullable(); + $table->boolean('active')->default(true); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('exchange_rate_providers'); + } +} diff --git a/crater/database/migrations/2021_09_28_130822_add_sequence_column.php b/crater/database/migrations/2021_09_28_130822_add_sequence_column.php new file mode 100644 index 0000000..992b9bf --- /dev/null +++ b/crater/database/migrations/2021_09_28_130822_add_sequence_column.php @@ -0,0 +1,101 @@ +string('prefix')->nullable()->after('id'); + }); + + Schema::table('invoices', function (Blueprint $table) { + $table->mediumInteger('sequence_number')->unsigned()->nullable()->after('id'); + $table->mediumInteger('customer_sequence_number')->unsigned()->nullable()->after('sequence_number'); + }); + + Schema::table('estimates', function (Blueprint $table) { + $table->mediumInteger('sequence_number')->unsigned()->nullable()->after('id'); + $table->mediumInteger('customer_sequence_number')->unsigned()->nullable()->after('sequence_number'); + }); + + Schema::table('payments', function (Blueprint $table) { + $table->mediumInteger('sequence_number')->unsigned()->nullable()->after('id'); + $table->mediumInteger('customer_sequence_number')->unsigned()->nullable()->after('sequence_number'); + }); + + $user = User::where('role', 'super admin')->first(); + + if ($user && $user->role == 'super admin') { + $customers = Customer::all(); + foreach ($customers as $customer) { + $invoices = $customer->invoices; + if ($invoices) { + $customerSequence = 1; + $invoices->map(function ($invoice) use ($customerSequence) { + $invoiceNumber = explode("-", $invoice->invoice_number); + $invoice->sequence_number = intval(end($invoiceNumber)); + $invoice->customer_sequence_number = $customerSequence; + $invoice->save(); + $customerSequence += 1; + }); + } + + $estimates = $customer->estimates; + if ($estimates) { + $customerSequence = 1; + $estimates->map(function ($estimate) use ($customerSequence) { + $estimateNumber = explode("-", $estimate->estimate_number); + $estimate->sequence_number = intval(end($estimateNumber)); + $estimate->customer_sequence_number = $customerSequence; + $estimate->save(); + $customerSequence += 1; + }); + } + + $payments = $customer->payments; + if ($estimates) { + $customerSequence = 1; + $payments->map(function ($payment) use ($customerSequence) { + $paymentNumber = explode("-", $payment->payment_number); + $payment->sequence_number = intval(end($paymentNumber)); + $payment->customer_sequence_number = $customerSequence; + $payment->save(); + $customerSequence += 1; + }); + } + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('sequence_number'); + $table->dropColumn('customer_sequence_number'); + }); + Schema::table('estimates', function (Blueprint $table) { + $table->dropColumn('sequence_number'); + $table->dropColumn('customer_sequence_number'); + }); + Schema::table('payments', function (Blueprint $table) { + $table->dropColumn('sequence_number'); + $table->dropColumn('customer_sequence_number'); + }); + } +} diff --git a/crater/database/migrations/2021_10_06_100539_add_recurring_invoice_id_to_taxes_table.php b/crater/database/migrations/2021_10_06_100539_add_recurring_invoice_id_to_taxes_table.php new file mode 100644 index 0000000..90da41a --- /dev/null +++ b/crater/database/migrations/2021_10_06_100539_add_recurring_invoice_id_to_taxes_table.php @@ -0,0 +1,33 @@ +unsignedBigInteger('recurring_invoice_id')->nullable(); + $table->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('taxes', function (Blueprint $table) { + $table->dropColumn('recurring_invoice_id'); + }); + } +} diff --git a/crater/database/migrations/2021_11_13_051127_add_payment_method_to_expense_table.php b/crater/database/migrations/2021_11_13_051127_add_payment_method_to_expense_table.php new file mode 100644 index 0000000..c1fbabd --- /dev/null +++ b/crater/database/migrations/2021_11_13_051127_add_payment_method_to_expense_table.php @@ -0,0 +1,33 @@ +integer('payment_method_id')->unsigned()->nullable(); + $table->foreign('payment_method_id')->references('id')->on('payment_methods'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropColumn('payment_method_id'); + }); + } +} diff --git a/crater/database/migrations/2021_11_13_114808_calculate_base_values_for_existing_data.php b/crater/database/migrations/2021_11_13_114808_calculate_base_values_for_existing_data.php new file mode 100644 index 0000000..cba5cd5 --- /dev/null +++ b/crater/database/migrations/2021_11_13_114808_calculate_base_values_for_existing_data.php @@ -0,0 +1,144 @@ +first(); + + if ($user) { + $companyId = $user->companies()->first()->id; + + $currency_id = CompanySetting::getSetting('currency', $companyId); + + $items = Item::all(); + + foreach ($items as $item) { + $item->currency_id = $currency_id; + $item->save(); + } + + $customers = Customer::all(); + + foreach ($customers as $customer) { + if ($customer->invoices()->exists()) { + $customer->invoices->map(function ($invoice) use ($currency_id, $customer) { + if ($customer->currency_id == $currency_id) { + $invoice->update([ + 'currency_id' => $currency_id, + 'exchange_rate' => 1, + 'base_discount_val' => $invoice->sub_total, + 'base_sub_total' => $invoice->sub_total, + 'base_total' => $invoice->total, + 'base_tax' => $invoice->tax, + 'base_due_amount' => $invoice->due_amount + ]); + } else { + $invoice->update([ + 'currency_id' => $customer->currency_id, + ]); + } + $this->items($invoice); + }); + } + + if ($customer->expenses()->exists()) { + $customer->expenses->map(function ($expense) use ($currency_id) { + $expense->update([ + 'currency_id' => $currency_id, + 'exchange_rate' => 1, + 'base_amount' => $expense->amount, + ]); + }); + } + + if ($customer->estimates()->exists()) { + $customer->estimates->map(function ($estimate) use ($currency_id, $customer) { + if ($customer->currency_id == $currency_id) { + $estimate->update([ + 'currency_id' => $currency_id, + 'exchange_rate' => 1, + 'base_discount_val' => $estimate->sub_total, + 'base_sub_total' => $estimate->sub_total, + 'base_total' => $estimate->total, + 'base_tax' => $estimate->tax + ]); + } else { + $estimate->update([ + 'currency_id' => $customer->currency_id, + ]); + } + $this->items($estimate); + }); + } + + if ($customer->payments()->exists()) { + $customer->payments->map(function ($payment) use ($currency_id, $customer) { + if ($customer->currency_id == $currency_id) { + $payment->update([ + 'currency_id' => $currency_id, + 'base_amount' => $payment->amount, + 'exchange_rate' => 1 + ]); + } else { + $payment->update([ + 'currency_id' => $customer->currency_id, + ]); + } + }); + } + } + } + } + + public function items($model) + { + $model->items->map(function ($item) use ($model) { + $item->update([ + 'exchange_rate' => $model->exchange_rate, + 'base_discount_val' => $item->discount_val * $model->exchange_rate, + 'base_price' => $item->price * $model->exchange_rate, + 'base_tax' => $item->tax * $model->exchange_rate, + 'base_total' => $item->total * $model->exchange_rate + ]); + + $this->taxes($item, $model->currency_id); + }); + + $this->taxes($model, $model->currency_id); + } + + public function taxes($model, $currency_id) + { + if ($model->taxes()->exists()) { + $model->taxes->map(function ($tax) use ($model, $currency_id) { + $tax->update([ + 'currency_id' => $currency_id, + 'exchange_rate' => $model->exchange_rate, + 'base_amount' => $tax->amount * $model->exchange_rate, + ]); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_11_23_092111_add_new_company_settings.php b/crater/database/migrations/2021_11_23_092111_add_new_company_settings.php new file mode 100644 index 0000000..3056381 --- /dev/null +++ b/crater/database/migrations/2021_11_23_092111_add_new_company_settings.php @@ -0,0 +1,60 @@ +map(function ($company) { + $settingsToRemove = [ + 'invoice_number_length', + 'estimate_number_length', + 'payment_number_length', + 'invoice_prefix', + 'estimate_prefix', + 'payment_prefix', + ]; + + $oldSettings = CompanySetting::getSettings($settingsToRemove, $company->id); + $oldSettings = $oldSettings->toArray(); + + $settings = [ + 'invoice_set_due_date_automatically' => 'YES', + 'invoice_due_date_days' => 7, + 'estimate_set_expiry_date_automatically' => 'YES', + 'estimate_expiry_date_days' => 7, + 'estimate_convert_action' => 'no_action', + 'bulk_exchange_rate_configured' => "NO", + 'invoice_number_format' => "{{SERIES:{$oldSettings['invoice_prefix']}}}{{DELIMITER:-}}{{SEQUENCE:{$oldSettings['invoice_number_length']}}}", + 'estimate_number_format' => "{{SERIES:{$oldSettings['estimate_prefix']}}}{{DELIMITER:-}}{{SEQUENCE:{$oldSettings['estimate_number_length']}}}", + 'payment_number_format' => "{{SERIES:{$oldSettings['payment_prefix']}}}{{DELIMITER:-}}{{SEQUENCE:{$oldSettings['payment_number_length']}}}", + ]; + + CompanySetting::whereIn('option', $settingsToRemove)->delete(); + + CompanySetting::setSettings($settings, $company->id); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_11_23_093811_update_crater_version_500.php b/crater/database/migrations/2021_11_23_093811_update_crater_version_500.php new file mode 100644 index 0000000..3c205d6 --- /dev/null +++ b/crater/database/migrations/2021_11_23_093811_update_crater_version_500.php @@ -0,0 +1,27 @@ +exchange_rate) { + $invoice->base_due_amount = $invoice->due_amount * $invoice->exchange_rate; + $invoice->save(); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_02_074516_migrate_templates_from_version_4.php b/crater/database/migrations/2021_12_02_074516_migrate_templates_from_version_4.php new file mode 100644 index 0000000..2e1aa0f --- /dev/null +++ b/crater/database/migrations/2021_12_02_074516_migrate_templates_from_version_4.php @@ -0,0 +1,45 @@ +files('/app/pdf/invoice'); + + foreach ($templates as $key => $template) { + $templateName = Str::before(basename($template), '.blade.php'); + if (! file_exists(resource_path("/static/img/PDF/{$templateName}.png"))) { + copy(public_path("/assets/img/PDF/{$templateName}.png"), public_path("/build/img/PDF/{$templateName}.png")); + copy(public_path("/assets/img/PDF/{$templateName}.png"), resource_path("/static/img/PDF/{$templateName}.png")); + } + } + + $templates = Storage::disk('views')->files('/app/pdf/estimate'); + + foreach ($templates as $key => $template) { + $templateName = Str::before(basename($template), '.blade.php'); + if (! file_exists(resource_path("/static/img/PDF/{$templateName}.png"))) { + copy(public_path("/assets/img/PDF/{$templateName}.png"), public_path("/build/img/PDF/{$templateName}.png")); + copy(public_path("/assets/img/PDF/{$templateName}.png"), resource_path("/static/img/PDF/{$templateName}.png")); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_02_123007_update_crater_version_502.php b/crater/database/migrations/2021_12_02_123007_update_crater_version_502.php new file mode 100644 index 0000000..ad1f4f0 --- /dev/null +++ b/crater/database/migrations/2021_12_02_123007_update_crater_version_502.php @@ -0,0 +1,27 @@ +id(); + $table->string('transaction_id')->nullable(); + $table->string('unique_hash')->nullable(); + $table->string('type')->nullable(); + $table->string('status'); + $table->dateTime('transaction_date'); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->unsignedInteger('invoice_id'); + $table->foreign('invoice_id')->references('id')->on('invoices'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('transactions'); + } +} diff --git a/crater/database/migrations/2021_12_04_123315_add_transaction_id_to_payments_table.php b/crater/database/migrations/2021_12_04_123315_add_transaction_id_to_payments_table.php new file mode 100644 index 0000000..97b281c --- /dev/null +++ b/crater/database/migrations/2021_12_04_123315_add_transaction_id_to_payments_table.php @@ -0,0 +1,33 @@ +unsignedBigInteger('transaction_id')->nullable(); + $table->foreign('transaction_id')->references('id')->on('transactions'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function (Blueprint $table) { + $table->dropColumn('transaction_id'); + }); + } +} diff --git a/crater/database/migrations/2021_12_04_123415_add_type_to_payment_methods_table.php b/crater/database/migrations/2021_12_04_123415_add_type_to_payment_methods_table.php new file mode 100644 index 0000000..18cb3d2 --- /dev/null +++ b/crater/database/migrations/2021_12_04_123415_add_type_to_payment_methods_table.php @@ -0,0 +1,51 @@ +string('driver')->nullable(); + $table->enum('type', ['GENERAL', 'MODULE'])->default(PaymentMethod::TYPE_GENERAL); + $table->json('settings')->nullable(); + $table->boolean('active')->default(false); + $table->boolean('use_test_env')->default(false); + }); + + $paymentMethods = PaymentMethod::all(); + + if ($paymentMethods) { + foreach ($paymentMethods as $paymentMethod) { + $paymentMethod->type = PaymentMethod::TYPE_GENERAL; + $paymentMethod->save(); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payment_methods', function (Blueprint $table) { + $table->dropColumn([ + 'driver', + 'type', + 'settings', + 'active' + ]); + }); + } +} diff --git a/crater/database/migrations/2021_12_06_131201_update_crater_version_504.php b/crater/database/migrations/2021_12_06_131201_update_crater_version_504.php new file mode 100644 index 0000000..240cc64 --- /dev/null +++ b/crater/database/migrations/2021_12_06_131201_update_crater_version_504.php @@ -0,0 +1,27 @@ +first(); + if ($user) { + $companyId = $user->companies()->first()->id; + + $currency_id = CompanySetting::getSetting('currency', $companyId); + + $expenses = Expense::where('company_id', $companyId)->where('currency_id', null)->get(); + if ($expenses) { + $expenses->map(function ($expense) use ($currency_id) { + $expense->update([ + 'currency_id' => $currency_id, + 'exchange_rate' => 1, + 'base_amount' => $expense->amount, + ]); + }); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_09_062434_update_crater_version_505.php b/crater/database/migrations/2021_12_09_062434_update_crater_version_505.php new file mode 100644 index 0000000..9136f42 --- /dev/null +++ b/crater/database/migrations/2021_12_09_062434_update_crater_version_505.php @@ -0,0 +1,27 @@ +dropUnique(['email']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_10_121739_update_creater_version_506.php b/crater/database/migrations/2021_12_10_121739_update_creater_version_506.php new file mode 100644 index 0000000..6894a40 --- /dev/null +++ b/crater/database/migrations/2021_12_10_121739_update_creater_version_506.php @@ -0,0 +1,27 @@ +', null)->get(); + + if ($payments) { + foreach ($payments as $payment) { + $payment->base_amount = $payment->exchange_rate * $payment->amount; + $payment->save(); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_13_093701_add_fields_to_email_logs_table.php b/crater/database/migrations/2021_12_13_093701_add_fields_to_email_logs_table.php new file mode 100644 index 0000000..bd1522d --- /dev/null +++ b/crater/database/migrations/2021_12_13_093701_add_fields_to_email_logs_table.php @@ -0,0 +1,50 @@ +string('token')->unique()->nullable(); + }); + + $user = User::where('role', 'super admin')->first(); + + if ($user) { + $settings = [ + 'automatically_expire_public_links' => 'Yes', + 'link_expiry_days' => 7 + ]; + + $companies = Company::all(); + + foreach ($companies as $company) { + CompanySetting::setSettings($settings, $company->id); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('email_logs', function (Blueprint $table) { + $table->dropColumn('token'); + }); + } +} diff --git a/crater/database/migrations/2021_12_15_053223_create_modules_table.php b/crater/database/migrations/2021_12_15_053223_create_modules_table.php new file mode 100644 index 0000000..af7360a --- /dev/null +++ b/crater/database/migrations/2021_12_15_053223_create_modules_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name'); + $table->string('version'); + $table->boolean('installed')->default(false); + $table->boolean('enabled')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('modules'); + } +} diff --git a/crater/database/migrations/2021_12_21_102521_change_enable_portal_field_of_customers_table.php b/crater/database/migrations/2021_12_21_102521_change_enable_portal_field_of_customers_table.php new file mode 100644 index 0000000..35d3b33 --- /dev/null +++ b/crater/database/migrations/2021_12_21_102521_change_enable_portal_field_of_customers_table.php @@ -0,0 +1,40 @@ +boolean('enable_portal')->default(false)->change(); + }); + + $customers = Customer::all(); + + if ($customers) { + $customers->map(function ($customer) { + $customer->enable_portal = false; + $customer->save(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2021_12_31_042453_add_type_to_tax_types_table.php b/crater/database/migrations/2021_12_31_042453_add_type_to_tax_types_table.php new file mode 100644 index 0000000..bdd8c72 --- /dev/null +++ b/crater/database/migrations/2021_12_31_042453_add_type_to_tax_types_table.php @@ -0,0 +1,42 @@ +enum('type', ['GENERAL', 'MODULE'])->default(TaxType::TYPE_GENERAL); + }); + + $taxTypes = TaxType::all(); + + if ($taxTypes) { + foreach ($taxTypes as $taxType) { + $taxType->type = TaxType::TYPE_GENERAL; + $taxType->save(); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tax_types', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +} diff --git a/crater/database/migrations/2022_01_05_101841_add_sales_tax_fields_to_invoices_table.php b/crater/database/migrations/2022_01_05_101841_add_sales_tax_fields_to_invoices_table.php new file mode 100644 index 0000000..558d380 --- /dev/null +++ b/crater/database/migrations/2022_01_05_101841_add_sales_tax_fields_to_invoices_table.php @@ -0,0 +1,36 @@ +string('sales_tax_type')->nullable(); + $table->string('sales_tax_address_type')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn([ + 'sales_tax_type', + 'sales_tax_address_type', + ]); + }); + } +} diff --git a/crater/database/migrations/2022_01_05_102538_add_sales_tax_fields_to_estimates_table.php b/crater/database/migrations/2022_01_05_102538_add_sales_tax_fields_to_estimates_table.php new file mode 100644 index 0000000..8b5675e --- /dev/null +++ b/crater/database/migrations/2022_01_05_102538_add_sales_tax_fields_to_estimates_table.php @@ -0,0 +1,36 @@ +string('sales_tax_type')->nullable(); + $table->string('sales_tax_address_type')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('estimates', function (Blueprint $table) { + $table->dropColumn([ + 'sales_tax_type', + 'sales_tax_address_type', + ]); + }); + } +} diff --git a/crater/database/migrations/2022_01_05_103607_add_sales_tax_fields_to_recurring_invoices_table.php b/crater/database/migrations/2022_01_05_103607_add_sales_tax_fields_to_recurring_invoices_table.php new file mode 100644 index 0000000..cbe6dc6 --- /dev/null +++ b/crater/database/migrations/2022_01_05_103607_add_sales_tax_fields_to_recurring_invoices_table.php @@ -0,0 +1,36 @@ +string('sales_tax_type')->nullable(); + $table->string('sales_tax_address_type')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('recurring_invoices', function (Blueprint $table) { + $table->dropColumn([ + 'sales_tax_type', + 'sales_tax_address_type', + ]); + }); + } +} diff --git a/crater/database/migrations/2022_01_05_115423_update_crater_version_600.php b/crater/database/migrations/2022_01_05_115423_update_crater_version_600.php new file mode 100644 index 0000000..6e61a8f --- /dev/null +++ b/crater/database/migrations/2022_01_05_115423_update_crater_version_600.php @@ -0,0 +1,27 @@ +get(); + + if ($companies) { + foreach ($companies as $company) { + $company->slug = Str::slug($company->name); + $company->save(); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2022_01_12_132859_update_crater_version_601.php b/crater/database/migrations/2022_01_12_132859_update_crater_version_601.php new file mode 100644 index 0000000..7b91d99 --- /dev/null +++ b/crater/database/migrations/2022_01_12_132859_update_crater_version_601.php @@ -0,0 +1,27 @@ +string('value')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2022_03_02_120210_add_overdue_to_invoices_table.php b/crater/database/migrations/2022_03_02_120210_add_overdue_to_invoices_table.php new file mode 100644 index 0000000..e8572b2 --- /dev/null +++ b/crater/database/migrations/2022_03_02_120210_add_overdue_to_invoices_table.php @@ -0,0 +1,32 @@ +boolean('overdue')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropForeign(['overdue']); + }); + } +} diff --git a/crater/database/migrations/2022_03_03_060121_crater_version_605.php b/crater/database/migrations/2022_03_03_060121_crater_version_605.php new file mode 100644 index 0000000..0ab61d0 --- /dev/null +++ b/crater/database/migrations/2022_03_03_060121_crater_version_605.php @@ -0,0 +1,27 @@ +get(); + + if ($overdueInvoices) { + $overdueInvoices->map(function ($overdueInvoice) { + $overdueInvoice->status = Invoice::STATUS_SENT; + $overdueInvoice->overdue = true; + $overdueInvoice->save(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2022_03_04_051438_calculate_base_values_for_invoice_items.php b/crater/database/migrations/2022_03_04_051438_calculate_base_values_for_invoice_items.php new file mode 100644 index 0000000..1c6c877 --- /dev/null +++ b/crater/database/migrations/2022_03_04_051438_calculate_base_values_for_invoice_items.php @@ -0,0 +1,38 @@ +get(); + + if ($taxes) { + $taxes->map(function ($tax) { + $invoiceItem = InvoiceItem::find($tax->invoice_item_id); + $exchange_rate = $invoiceItem->exchange_rate; + $tax->exchange_rate = $exchange_rate; + $tax->base_amount = $tax->amount * $exchange_rate; + $tax->save(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/crater/database/migrations/2022_03_06_070829_update_crater_version_606.php b/crater/database/migrations/2022_03_06_070829_update_crater_version_606.php new file mode 100644 index 0000000..d1247e7 --- /dev/null +++ b/crater/database/migrations/2022_03_06_070829_update_crater_version_606.php @@ -0,0 +1,27 @@ +delete(); + $countries = [ + ['id' => 1,'code' => 'AF' ,'name' => "Afghanistan",'phonecode' => 93], + ['id' => 2,'code' => 'AL' ,'name' => "Albania",'phonecode' => 355], + ['id' => 3,'code' => 'DZ' ,'name' => "Algeria",'phonecode' => 213], + ['id' => 4,'code' => 'AS' ,'name' => "American Samoa",'phonecode' => 1684], + ['id' => 5,'code' => 'AD' ,'name' => "Andorra",'phonecode' => 376], + ['id' => 6,'code' => 'AO' ,'name' => "Angola",'phonecode' => 244], + ['id' => 7,'code' => 'AI' ,'name' => "Anguilla",'phonecode' => 1264], + ['id' => 8,'code' => 'AQ' ,'name' => "Antarctica",'phonecode' => 0], + ['id' => 9,'code' => 'AG' ,'name' => "Antigua And Barbuda",'phonecode' => 1268], + ['id' => 10,'code' => 'AR','name' => "Argentina",'phonecode' => 54], + ['id' => 11,'code' => 'AM','name' => "Armenia",'phonecode' => 374], + ['id' => 12,'code' => 'AW','name' => "Aruba",'phonecode' => 297], + ['id' => 13,'code' => 'AU','name' => "Australia",'phonecode' => 61], + ['id' => 14,'code' => 'AT','name' => "Austria",'phonecode' => 43], + ['id' => 15,'code' => 'AZ','name' => "Azerbaijan",'phonecode' => 994], + ['id' => 16,'code' => 'BS','name' => "Bahamas The",'phonecode' => 1242], + ['id' => 17,'code' => 'BH','name' => "Bahrain",'phonecode' => 973], + ['id' => 18,'code' => 'BD','name' => "Bangladesh",'phonecode' => 880], + ['id' => 19,'code' => 'BB','name' => "Barbados",'phonecode' => 1246], + ['id' => 20,'code' => 'BY','name' => "Belarus",'phonecode' => 375], + ['id' => 21,'code' => 'BE','name' => "Belgium",'phonecode' => 32], + ['id' => 22,'code' => 'BZ','name' => "Belize",'phonecode' => 501], + ['id' => 23,'code' => 'BJ','name' => "Benin",'phonecode' => 229], + ['id' => 24,'code' => 'BM','name' => "Bermuda",'phonecode' => 1441], + ['id' => 25,'code' => 'BT','name' => "Bhutan",'phonecode' => 975], + ['id' => 26,'code' => 'BO','name' => "Bolivia",'phonecode' => 591], + ['id' => 27,'code' => 'BA','name' => "Bosnia and Herzegovina",'phonecode' => 387], + ['id' => 28,'code' => 'BW','name' => "Botswana",'phonecode' => 267], + ['id' => 29,'code' => 'BV','name' => "Bouvet Island",'phonecode' => 0], + ['id' => 30,'code' => 'BR','name' => "Brazil",'phonecode' => 55], + ['id' => 31,'code' => 'IO','name' => "British Indian Ocean Territory",'phonecode' => 246], + ['id' => 32,'code' => 'BN','name' => "Brunei",'phonecode' => 673], + ['id' => 33,'code' => 'BG','name' => "Bulgaria",'phonecode' => 359], + ['id' => 34,'code' => 'BF','name' => "Burkina Faso",'phonecode' => 226], + ['id' => 35,'code' => 'BI','name' => "Burundi",'phonecode' => 257], + ['id' => 36,'code' => 'KH','name' => "Cambodia",'phonecode' => 855], + ['id' => 37,'code' => 'CM','name' => "Cameroon",'phonecode' => 237], + ['id' => 38,'code' => 'CA','name' => "Canada",'phonecode' => 1], + ['id' => 39,'code' => 'CV','name' => "Cape Verde",'phonecode' => 238], + ['id' => 40,'code' => 'KY','name' => "Cayman Islands",'phonecode' => 1345], + ['id' => 41,'code' => 'CF','name' => "Central African Republic",'phonecode' => 236], + ['id' => 42,'code' => 'TD','name' => "Chad",'phonecode' => 235], + ['id' => 43,'code' => 'CL','name' => "Chile",'phonecode' => 56], + ['id' => 44,'code' => 'CN','name' => "China",'phonecode' => 86], + ['id' => 45,'code' => 'CX','name' => "Christmas Island",'phonecode' => 61], + ['id' => 46,'code' => 'CC','name' => "Cocos (Keeling) Islands",'phonecode' => 672], + ['id' => 47,'code' => 'CO','name' => "Colombia",'phonecode' => 57], + ['id' => 48,'code' => 'KM','name' => "Comoros",'phonecode' => 269], + ['id' => 49,'code' => 'CG','name' => "Congo",'phonecode' => 242], + ['id' => 50,'code' => 'CD','name' => "Congo The Democratic Republic Of The",'phonecode' => 242], + ['id' => 51,'code' => 'CK','name' => "Cook Islands",'phonecode' => 682], + ['id' => 52,'code' => 'CR','name' => "Costa Rica",'phonecode' => 506], + ['id' => 53,'code' => 'CI','name' => "Cote D Ivoire (Ivory Coast)",'phonecode' => 225], + ['id' => 54,'code' => 'HR','name' => "Croatia (Hrvatska)",'phonecode' => 385], + ['id' => 55,'code' => 'CU','name' => "Cuba",'phonecode' => 53], + ['id' => 56,'code' => 'CY','name' => "Cyprus",'phonecode' => 357], + ['id' => 57,'code' => 'CZ','name' => "Czech Republic",'phonecode' => 420], + ['id' => 58,'code' => 'DK','name' => "Denmark",'phonecode' => 45], + ['id' => 59,'code' => 'DJ','name' => "Djibouti",'phonecode' => 253], + ['id' => 60,'code' => 'DM','name' => "Dominica",'phonecode' => 1767], + ['id' => 61,'code' => 'DO','name' => "Dominican Republic",'phonecode' => 1809], + ['id' => 62,'code' => 'TP','name' => "East Timor",'phonecode' => 670], + ['id' => 63,'code' => 'EC','name' => "Ecuador",'phonecode' => 593], + ['id' => 64,'code' => 'EG','name' => "Egypt",'phonecode' => 20], + ['id' => 65,'code' => 'SV','name' => "El Salvador",'phonecode' => 503], + ['id' => 66,'code' => 'GQ','name' => "Equatorial Guinea",'phonecode' => 240], + ['id' => 67,'code' => 'ER','name' => "Eritrea",'phonecode' => 291], + ['id' => 68,'code' => 'EE','name' => "Estonia",'phonecode' => 372], + ['id' => 69,'code' => 'ET','name' => "Ethiopia",'phonecode' => 251], + ['id' => 70,'code' => 'XA','name' => "External Territories of Australia",'phonecode' => 61], + ['id' => 71,'code' => 'FK','name' => "Falkland Islands",'phonecode' => 500], + ['id' => 72,'code' => 'FO','name' => "Faroe Islands",'phonecode' => 298], + ['id' => 73,'code' => 'FJ','name' => "Fiji Islands",'phonecode' => 679], + ['id' => 74,'code' => 'FI','name' => "Finland",'phonecode' => 358], + ['id' => 75,'code' => 'FR','name' => "France",'phonecode' => 33], + ['id' => 76,'code' => 'GF','name' => "French Guiana",'phonecode' => 594], + ['id' => 77,'code' => 'PF','name' => "French Polynesia",'phonecode' => 689], + ['id' => 78,'code' => 'TF','name' => "French Southern Territories",'phonecode' => 0], + ['id' => 79,'code' => 'GA','name' => "Gabon",'phonecode' => 241], + ['id' => 80,'code' => 'GM','name' => "Gambia The",'phonecode' => 220], + ['id' => 81,'code' => 'GE','name' => "Georgia",'phonecode' => 995], + ['id' => 82,'code' => 'DE','name' => "Germany",'phonecode' => 49], + ['id' => 83,'code' => 'GH','name' => "Ghana",'phonecode' => 233], + ['id' => 84,'code' => 'GI','name' => "Gibraltar",'phonecode' => 350], + ['id' => 85,'code' => 'GR','name' => "Greece",'phonecode' => 30], + ['id' => 86,'code' => 'GL','name' => "Greenland",'phonecode' => 299], + ['id' => 87,'code' => 'GD','name' => "Grenada",'phonecode' => 1473], + ['id' => 88,'code' => 'GP','name' => "Guadeloupe",'phonecode' => 590], + ['id' => 89,'code' => 'GU','name' => "Guam",'phonecode' => 1671], + ['id' => 90,'code' => 'GT','name' => "Guatemala",'phonecode' => 502], + ['id' => 91,'code' => 'XU','name' => "Guernsey and Alderney",'phonecode' => 44], + ['id' => 92,'code' => 'GN','name' => "Guinea",'phonecode' => 224], + ['id' => 93,'code' => 'GW','name' => "Guinea-Bissau",'phonecode' => 245], + ['id' => 94,'code' => 'GY','name' => "Guyana",'phonecode' => 592], + ['id' => 95,'code' => 'HT','name' => "Haiti",'phonecode' => 509], + ['id' => 96,'code' => 'HM','name' => "Heard and McDonald Islands",'phonecode' => 0], + ['id' => 97,'code' => 'HN','name' => "Honduras",'phonecode' => 504], + ['id' => 98,'code' => 'HK','name' => "Hong Kong S.A.R.",'phonecode' => 852], + ['id' => 99,'code' => 'HU','name' => "Hungary",'phonecode' => 36], + ['id' => 100,'code' => 'IS','name' => "Iceland",'phonecode' => 354], + ['id' => 101,'code' => 'IN','name' => "India",'phonecode' => 91], + ['id' => 102,'code' => 'ID','name' => "Indonesia",'phonecode' => 62], + ['id' => 103,'code' => 'IR','name' => "Iran",'phonecode' => 98], + ['id' => 104,'code' => 'IQ','name' => "Iraq",'phonecode' => 964], + ['id' => 105,'code' => 'IE','name' => "Ireland",'phonecode' => 353], + ['id' => 106,'code' => 'IL','name' => "Israel",'phonecode' => 972], + ['id' => 107,'code' => 'IT','name' => "Italy",'phonecode' => 39], + ['id' => 108,'code' => 'JM','name' => "Jamaica",'phonecode' => 1876], + ['id' => 109,'code' => 'JP','name' => "Japan",'phonecode' => 81], + ['id' => 110,'code' => 'XJ','name' => "Jersey",'phonecode' => 44], + ['id' => 111,'code' => 'JO','name' => "Jordan",'phonecode' => 962], + ['id' => 112,'code' => 'KZ','name' => "Kazakhstan",'phonecode' => 7], + ['id' => 113,'code' => 'KE','name' => "Kenya",'phonecode' => 254], + ['id' => 114,'code' => 'KI','name' => "Kiribati",'phonecode' => 686], + ['id' => 115,'code' => 'KP','name' => "Korea North",'phonecode' => 850], + ['id' => 116,'code' => 'KR','name' => "Korea South",'phonecode' => 82], + ['id' => 117,'code' => 'KW','name' => "Kuwait",'phonecode' => 965], + ['id' => 118,'code' => 'KG','name' => "Kyrgyzstan",'phonecode' => 996], + ['id' => 119,'code' => 'LA','name' => "Laos",'phonecode' => 856], + ['id' => 120,'code' => 'LV','name' => "Latvia",'phonecode' => 371], + ['id' => 121,'code' => 'LB','name' => "Lebanon",'phonecode' => 961], + ['id' => 122,'code' => 'LS','name' => "Lesotho",'phonecode' => 266], + ['id' => 123,'code' => 'LR','name' => "Liberia",'phonecode' => 231], + ['id' => 124,'code' => 'LY','name' => "Libya",'phonecode' => 218], + ['id' => 125,'code' => 'LI','name' => "Liechtenstein",'phonecode' => 423], + ['id' => 126,'code' => 'LT','name' => "Lithuania",'phonecode' => 370], + ['id' => 127,'code' => 'LU','name' => "Luxembourg",'phonecode' => 352], + ['id' => 128,'code' => 'MO','name' => "Macau S.A.R.",'phonecode' => 853], + ['id' => 129,'code' => 'MK','name' => "Macedonia",'phonecode' => 389], + ['id' => 130,'code' => 'MG','name' => "Madagascar",'phonecode' => 261], + ['id' => 131,'code' => 'MW','name' => "Malawi",'phonecode' => 265], + ['id' => 132,'code' => 'MY','name' => "Malaysia",'phonecode' => 60], + ['id' => 133,'code' => 'MV','name' => "Maldives",'phonecode' => 960], + ['id' => 134,'code' => 'ML','name' => "Mali",'phonecode' => 223], + ['id' => 135,'code' => 'MT','name' => "Malta",'phonecode' => 356], + ['id' => 136,'code' => 'XM','name' => "Man (Isle of)",'phonecode' => 44], + ['id' => 137,'code' => 'MH','name' => "Marshall Islands",'phonecode' => 692], + ['id' => 138,'code' => 'MQ','name' => "Martinique",'phonecode' => 596], + ['id' => 139,'code' => 'MR','name' => "Mauritania",'phonecode' => 222], + ['id' => 140,'code' => 'MU','name' => "Mauritius",'phonecode' => 230], + ['id' => 141,'code' => 'YT','name' => "Mayotte",'phonecode' => 269], + ['id' => 142,'code' => 'MX','name' => "Mexico",'phonecode' => 52], + ['id' => 143,'code' => 'FM','name' => "Micronesia",'phonecode' => 691], + ['id' => 144,'code' => 'MD','name' => "Moldova",'phonecode' => 373], + ['id' => 145,'code' => 'MC','name' => "Monaco",'phonecode' => 377], + ['id' => 146,'code' => 'MN','name' => "Mongolia",'phonecode' => 976], + ['id' => 147,'code' => 'MS','name' => "Montserrat",'phonecode' => 1664], + ['id' => 148,'code' => 'MA','name' => "Morocco",'phonecode' => 212], + ['id' => 149,'code' => 'MZ','name' => "Mozambique",'phonecode' => 258], + ['id' => 150,'code' => 'MM','name' => "Myanmar",'phonecode' => 95], + ['id' => 151,'code' => 'NA','name' => "Namibia",'phonecode' => 264], + ['id' => 152,'code' => 'NR','name' => "Nauru",'phonecode' => 674], + ['id' => 153,'code' => 'NP','name' => "Nepal",'phonecode' => 977], + ['id' => 154,'code' => 'AN','name' => "Netherlands Antilles",'phonecode' => 599], + ['id' => 155,'code' => 'NL','name' => "Netherlands",'phonecode' => 31], + ['id' => 156,'code' => 'NC','name' => "New Caledonia",'phonecode' => 687], + ['id' => 157,'code' => 'NZ','name' => "New Zealand",'phonecode' => 64], + ['id' => 158,'code' => 'NI','name' => "Nicaragua",'phonecode' => 505], + ['id' => 159,'code' => 'NE','name' => "Niger",'phonecode' => 227], + ['id' => 160,'code' => 'NG','name' => "Nigeria",'phonecode' => 234], + ['id' => 161,'code' => 'NU','name' => "Niue",'phonecode' => 683], + ['id' => 162,'code' => 'NF','name' => "Norfolk Island",'phonecode' => 672], + ['id' => 163,'code' => 'MP','name' => "Northern Mariana Islands",'phonecode' => 1670], + ['id' => 164,'code' => 'NO','name' => "Norway",'phonecode' => 47], + ['id' => 165,'code' => 'OM','name' => "Oman",'phonecode' => 968], + ['id' => 166,'code' => 'PK','name' => "Pakistan",'phonecode' => 92], + ['id' => 167,'code' => 'PW','name' => "Palau",'phonecode' => 680], + ['id' => 168,'code' => 'PS','name' => "Palestinian Territory Occupied",'phonecode' => 970], + ['id' => 169,'code' => 'PA','name' => "Panama",'phonecode' => 507], + ['id' => 170,'code' => 'PG','name' => "Papua new Guinea",'phonecode' => 675], + ['id' => 171,'code' => 'PY','name' => "Paraguay",'phonecode' => 595], + ['id' => 172,'code' => 'PE','name' => "Peru",'phonecode' => 51], + ['id' => 173,'code' => 'PH','name' => "Philippines",'phonecode' => 63], + ['id' => 174,'code' => 'PN','name' => "Pitcairn Island",'phonecode' => 0], + ['id' => 175,'code' => 'PL','name' => "Poland",'phonecode' => 48], + ['id' => 176,'code' => 'PT','name' => "Portugal",'phonecode' => 351], + ['id' => 177,'code' => 'PR','name' => "Puerto Rico",'phonecode' => 1787], + ['id' => 178,'code' => 'QA','name' => "Qatar",'phonecode' => 974], + ['id' => 179,'code' => 'RE','name' => "Reunion",'phonecode' => 262], + ['id' => 180,'code' => 'RO','name' => "Romania",'phonecode' => 40], + ['id' => 181,'code' => 'RU','name' => "Russia",'phonecode' => 70], + ['id' => 182,'code' => 'RW','name' => "Rwanda",'phonecode' => 250], + ['id' => 183,'code' => 'SH','name' => "Saint Helena",'phonecode' => 290], + ['id' => 184,'code' => 'KN','name' => "Saint Kitts And Nevis",'phonecode' => 1869], + ['id' => 185,'code' => 'LC','name' => "Saint Lucia",'phonecode' => 1758], + ['id' => 186,'code' => 'PM','name' => "Saint Pierre and Miquelon",'phonecode' => 508], + ['id' => 187,'code' => 'VC','name' => "Saint Vincent And The Grenadines",'phonecode' => 1784], + ['id' => 188,'code' => 'WS','name' => "Samoa",'phonecode' => 684], + ['id' => 189,'code' => 'SM','name' => "San Marino",'phonecode' => 378], + ['id' => 190,'code' => 'ST','name' => "Sao Tome and Principe",'phonecode' => 239], + ['id' => 191,'code' => 'SA','name' => "Saudi Arabia",'phonecode' => 966], + ['id' => 192,'code' => 'SN','name' => "Senegal",'phonecode' => 221], + ['id' => 193,'code' => 'RS','name' => "Serbia",'phonecode' => 381], + ['id' => 194,'code' => 'SC','name' => "Seychelles",'phonecode' => 248], + ['id' => 195,'code' => 'SL','name' => "Sierra Leone",'phonecode' => 232], + ['id' => 196,'code' => 'SG','name' => "Singapore",'phonecode' => 65], + ['id' => 197,'code' => 'SK','name' => "Slovakia",'phonecode' => 421], + ['id' => 198,'code' => 'SI','name' => "Slovenia",'phonecode' => 386], + ['id' => 199,'code' => 'XG','name' => "Smaller Territories of the UK",'phonecode' => 44], + ['id' => 200,'code' => 'SB','name' => "Solomon Islands",'phonecode' => 677], + ['id' => 201,'code' => 'SO','name' => "Somalia",'phonecode' => 252], + ['id' => 202,'code' => 'ZA','name' => "South Africa",'phonecode' => 27], + ['id' => 203,'code' => 'GS','name' => "South Georgia",'phonecode' => 0], + ['id' => 204,'code' => 'SS','name' => "South Sudan",'phonecode' => 211], + ['id' => 205,'code' => 'ES','name' => "Spain",'phonecode' => 34], + ['id' => 206,'code' => 'LK','name' => "Sri Lanka",'phonecode' => 94], + ['id' => 207,'code' => 'SD','name' => "Sudan",'phonecode' => 249], + ['id' => 208,'code' => 'SR','name' => "Suriname",'phonecode' => 597], + ['id' => 209,'code' => 'SJ','name' => "Svalbard And Jan Mayen Islands",'phonecode' => 47], + ['id' => 210,'code' => 'SZ','name' => "Swaziland",'phonecode' => 268], + ['id' => 211,'code' => 'SE','name' => "Sweden",'phonecode' => 46], + ['id' => 212,'code' => 'CH','name' => "Switzerland",'phonecode' => 41], + ['id' => 213,'code' => 'SY','name' => "Syria",'phonecode' => 963], + ['id' => 214,'code' => 'TW','name' => "Taiwan",'phonecode' => 886], + ['id' => 215,'code' => 'TJ','name' => "Tajikistan",'phonecode' => 992], + ['id' => 216,'code' => 'TZ','name' => "Tanzania",'phonecode' => 255], + ['id' => 217,'code' => 'TH','name' => "Thailand",'phonecode' => 66], + ['id' => 218,'code' => 'TG','name' => "Togo",'phonecode' => 228], + ['id' => 219,'code' => 'TK','name' => "Tokelau",'phonecode' => 690], + ['id' => 220,'code' => 'TO','name' => "Tonga",'phonecode' => 676], + ['id' => 221,'code' => 'TT','name' => "Trinidad And Tobago",'phonecode' => 1868], + ['id' => 222,'code' => 'TN','name' => "Tunisia",'phonecode' => 216], + ['id' => 223,'code' => 'TR','name' => "Turkey",'phonecode' => 90], + ['id' => 224,'code' => 'TM','name' => "Turkmenistan",'phonecode' => 7370], + ['id' => 225,'code' => 'TC','name' => "Turks And Caicos Islands",'phonecode' => 1649], + ['id' => 226,'code' => 'TV','name' => "Tuvalu",'phonecode' => 688], + ['id' => 227,'code' => 'UG','name' => "Uganda",'phonecode' => 256], + ['id' => 228,'code' => 'UA','name' => "Ukraine",'phonecode' => 380], + ['id' => 229,'code' => 'AE','name' => "United Arab Emirates",'phonecode' => 971], + ['id' => 230,'code' => 'GB','name' => "United Kingdom",'phonecode' => 44], + ['id' => 231,'code' => 'US','name' => "United States",'phonecode' => 1], + ['id' => 232,'code' => 'UM','name' => "United States Minor Outlying Islands",'phonecode' => 1], + ['id' => 233,'code' => 'UY','name' => "Uruguay",'phonecode' => 598], + ['id' => 234,'code' => 'UZ','name' => "Uzbekistan",'phonecode' => 998], + ['id' => 235,'code' => 'VU','name' => "Vanuatu",'phonecode' => 678], + ['id' => 236,'code' => 'VA','name' => "Vatican City State (Holy See)",'phonecode' => 39], + ['id' => 237,'code' => 'VE','name' => "Venezuela",'phonecode' => 58], + ['id' => 238,'code' => 'VN','name' => "Vietnam",'phonecode' => 84], + ['id' => 239,'code' => 'VG','name' => "Virgin Islands (British)",'phonecode' => 1284], + ['id' => 240,'code' => 'VI','name' => "Virgin Islands (US)",'phonecode' => 1340], + ['id' => 241,'code' => 'WF','name' => "Wallis And Futuna Islands",'phonecode' => 681], + ['id' => 242,'code' => 'EH','name' => "Western Sahara",'phonecode' => 212], + ['id' => 243,'code' => 'YE','name' => "Yemen",'phonecode' => 967], + ['id' => 244,'code' => 'YU','name' => "Yugoslavia",'phonecode' => 38], + ['id' => 245,'code' => 'ZM','name' => "Zambia",'phonecode' => 260], + ['id' => 246,'code' => 'ZW','name' => "Zimbabwe",'phonecode' => 263], + ]; + DB::table('countries')->insert($countries); + } +} diff --git a/crater/database/seeders/CurrenciesTableSeeder.php b/crater/database/seeders/CurrenciesTableSeeder.php new file mode 100644 index 0000000..149d568 --- /dev/null +++ b/crater/database/seeders/CurrenciesTableSeeder.php @@ -0,0 +1,610 @@ + 'US Dollar', + 'code' => 'USD', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'British Pound', + 'code' => 'GBP', + 'symbol' => '£', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Euro', + 'code' => 'EUR', + 'symbol' => '€', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'South African Rand', + 'code' => 'ZAR', + 'symbol' => 'R', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Danish Krone', + 'code' => 'DKK', + 'symbol' => 'kr', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Israeli Shekel', + 'code' => 'ILS', + 'symbol' => 'NIS ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Swedish Krona', + 'code' => 'SEK', + 'symbol' => 'kr', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Kenyan Shilling', + 'code' => 'KES', + 'symbol' => 'KSh ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Kuwaiti Dinar', + 'code' => 'KWD', + 'symbol' => 'KWD ', + 'precision' => '3', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Canadian Dollar', + 'code' => 'CAD', + 'symbol' => 'C$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Philippine Peso', + 'code' => 'PHP', + 'symbol' => 'P ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Nepali Rupee', + 'code' => 'NPR', + 'symbol' => 'रू', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Indian Rupee', + 'code' => 'INR', + 'symbol' => '₹', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Australian Dollar', + 'code' => 'AUD', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Singapore Dollar', + 'code' => 'SGD', + 'symbol' => 'S$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Norske Kroner', + 'code' => 'NOK', + 'symbol' => 'kr', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'New Zealand Dollar', + 'code' => 'NZD', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Vietnamese Dong', + 'code' => 'VND', + 'symbol' => '₫', + 'precision' => '0', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Swiss Franc', + 'code' => 'CHF', + 'symbol' => 'Fr.', + 'precision' => '2', + 'thousand_separator' => '\'', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Guatemalan Quetzal', + 'code' => 'GTQ', + 'symbol' => 'Q', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Malaysian Ringgit', + 'code' => 'MYR', + 'symbol' => 'RM', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Brazilian Real', + 'code' => 'BRL', + 'symbol' => 'R$', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Thai Baht', + 'code' => 'THB', + 'symbol' => '฿', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Nigerian Naira', + 'code' => 'NGN', + 'symbol' => '₦', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Argentine Peso', + 'code' => 'ARS', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Bangladeshi Taka', + 'code' => 'BDT', + 'symbol' => 'Tk', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'United Arab Emirates Dirham', + 'code' => 'AED', + 'symbol' => 'DH ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Hong Kong Dollar', + 'code' => 'HKD', + 'symbol' => 'HK$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Indonesian Rupiah', + 'code' => 'IDR', + 'symbol' => 'Rp', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Mexican Peso', + 'code' => 'MXN', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Egyptian Pound', + 'code' => 'EGP', + 'symbol' => 'E£', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Colombian Peso', + 'code' => 'COP', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Central African Franc', + 'code' => 'XAF', + 'symbol' => 'CFA ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'West African Franc', + 'code' => 'XOF', + 'symbol' => 'CFA ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Chinese Renminbi', + 'code' => 'CNY', + 'symbol' => 'RMB ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Rwandan Franc', + 'code' => 'RWF', + 'symbol' => 'RF ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Tanzanian Shilling', + 'code' => 'TZS', + 'symbol' => 'TSh ', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Netherlands Antillean Guilder', + 'code' => 'ANG', + 'symbol' => 'NAƒ', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Trinidad and Tobago Dollar', + 'code' => 'TTD', + 'symbol' => 'TT$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'East Caribbean Dollar', + 'code' => 'XCD', + 'symbol' => 'EC$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Ghanaian Cedi', + 'code' => 'GHS', + 'symbol' => '‎GH₵', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Bulgarian Lev', + 'code' => 'BGN', + 'symbol' => 'Лв.', + 'precision' => '2', + 'thousand_separator' => ' ', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Aruban Florin', + 'code' => 'AWG', + 'symbol' => 'Afl. ', + 'precision' => '2', + 'thousand_separator' => ' ', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Turkish Lira', + 'code' => 'TRY', + 'symbol' => 'TL ', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Romanian New Leu', + 'code' => 'RON', + 'symbol' => 'RON', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Croatian Kuna', + 'code' => 'HRK', + 'symbol' => 'kn', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Saudi Riyal', + 'code' => 'SAR', + 'symbol' => '‎SِAR', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Japanese Yen', + 'code' => 'JPY', + 'symbol' => '¥', + 'precision' => '0', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Maldivian Rufiyaa', + 'code' => 'MVR', + 'symbol' => 'Rf', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Costa Rican Colón', + 'code' => 'CRC', + 'symbol' => '₡', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Pakistani Rupee', + 'code' => 'PKR', + 'symbol' => 'Rs ', + 'precision' => '0', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Polish Zloty', + 'code' => 'PLN', + 'symbol' => 'zł', + 'precision' => '2', + 'thousand_separator' => ' ', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Sri Lankan Rupee', + 'code' => 'LKR', + 'symbol' => 'LKR', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Czech Koruna', + 'code' => 'CZK', + 'symbol' => 'Kč', + 'precision' => '2', + 'thousand_separator' => ' ', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Uruguayan Peso', + 'code' => 'UYU', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Namibian Dollar', + 'code' => 'NAD', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Tunisian Dinar', + 'code' => 'TND', + 'symbol' => '‎د.ت', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Russian Ruble', + 'code' => 'RUB', + 'symbol' => '₽', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Mozambican Metical', + 'code' => 'MZN', + 'symbol' => 'MT', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + [ + 'name' => 'Omani Rial', + 'code' => 'OMR', + 'symbol' => 'ر.ع.', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Ukrainian Hryvnia', + 'code' => 'UAH', + 'symbol' => '₴', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Macanese Pataca', + 'code' => 'MOP', + 'symbol' => 'MOP$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Taiwan New Dollar', + 'code' => 'TWD', + 'symbol' => 'NT$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Dominican Peso', + 'code' => 'DOP', + 'symbol' => 'RD$', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Chilean Peso', + 'code' => 'CLP', + 'symbol' => '$', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Serbian Dinar', + 'code' => 'RSD', + 'symbol' => 'RSD', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Kyrgyzstani som', + 'code' => 'KGS', + 'symbol' => 'С̲ ', + 'precision' => '2', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + ], + [ + 'name' => 'Iraqi Dinar', + 'code' => 'IQD', + 'symbol' => 'ع.د', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Peruvian Soles', + 'code' => 'PEN', + 'symbol' => 'S/', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Moroccan Dirham', + 'code' => 'MAD', + 'symbol' => 'DH', + 'precision' => '2', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Jamaican Dollar', + 'code' => 'JMD', + 'symbol' => '$', + 'precision' => '0', + 'thousand_separator' => ',', + 'decimal_separator' => '.', + ], + [ + 'name' => 'Macedonian Denar', + 'code' => 'MKD', + 'symbol' => 'ден', + 'precision' => '0', + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'swap_currency_symbol' => true, + ], + ]; + + + foreach ($currencies as $currency) { + Currency::create($currency); + } + } +} diff --git a/crater/database/seeders/DatabaseSeeder.php b/crater/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..96eb347 --- /dev/null +++ b/crater/database/seeders/DatabaseSeeder.php @@ -0,0 +1,20 @@ +call(CurrenciesTableSeeder::class); + $this->call(CountriesTableSeeder::class); + $this->call(UsersTableSeeder::class); + } +} diff --git a/crater/database/seeders/DemoSeeder.php b/crater/database/seeders/DemoSeeder.php new file mode 100644 index 0000000..ece3b59 --- /dev/null +++ b/crater/database/seeders/DemoSeeder.php @@ -0,0 +1,29 @@ +first(); + + $user->setSettings(['language' => 'en']); + + Address::create(['company_id' => $user->companies()->first()->id, 'country_id' => 1]); + + Setting::setSetting('profile_complete', 'COMPLETED'); + + \Storage::disk('local')->put('database_created', 'database_created'); + } +} diff --git a/crater/database/seeders/UsersTableSeeder.php b/crater/database/seeders/UsersTableSeeder.php new file mode 100644 index 0000000..8076ba3 --- /dev/null +++ b/crater/database/seeders/UsersTableSeeder.php @@ -0,0 +1,44 @@ + 'admin@craterapp.com', + 'name' => 'Jane Doe', + 'role' => 'super admin', + 'password' => 'crater@123', + ]); + + $company = Company::create([ + 'name' => 'xyz', + 'owner_id' => $user->id, + 'slug' => 'xyz' + ]); + + $company->unique_hash = Hashids::connection(Company::class)->encode($company->id); + $company->save(); + $company->setupDefaultData(); + $user->companies()->attach($company->id); + BouncerFacade::scope()->to($company->id); + + $user->assign('super admin'); + + Setting::setSetting('profile_complete', 0); + } +} diff --git a/crater/docker-compose.yml b/crater/docker-compose.yml new file mode 100644 index 0000000..af0185f --- /dev/null +++ b/crater/docker-compose.yml @@ -0,0 +1,65 @@ +services: + app: + container_name: crater-app + build: + args: + user: crater-user + uid: 1000 + context: ./ + dockerfile: Dockerfile + image: crater-php + restart: unless-stopped + working_dir: /var/www/ + volumes: + - ./:/var/www + - ./docker-compose/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:rw,delegated + networks: + - crater + + db: + container_name: crater-db + image: mariadb + restart: always + volumes: + - db:/var/lib/mysql + # If you want to persist data on the host, comment the line above this one... + # and uncomment the line under this one. + #- ./docker-compose/db/data:/var/lib/mysql:rw,delegated + environment: + MYSQL_USER: crater + MYSQL_PASSWORD: crater + MYSQL_DATABASE: crater + MYSQL_ROOT_PASSWORD: crater + ports: + - 3306 + networks: + - crater + + nginx: + container_name: crater-nginx + image: nginx:1.17-alpine + restart: unless-stopped + ports: + - 41378:80 + volumes: + - ./:/var/www + - ./docker-compose/nginx:/etc/nginx/conf.d/ + networks: + - crater + + cron: + container_name: crater-cron + build: + context: ./ + dockerfile: ./docker-compose/cron.dockerfile + volumes: + - ./:/var/www + networks: + - crater + +volumes: + db: + +networks: + crater: + driver: bridge diff --git a/crater/docker-compose/cron.dockerfile b/crater/docker-compose/cron.dockerfile new file mode 100644 index 0000000..38598c0 --- /dev/null +++ b/crater/docker-compose/cron.dockerfile @@ -0,0 +1,10 @@ +FROM php:8.0-fpm-alpine + +RUN apk add --no-cache \ + php8-bcmath + +RUN docker-php-ext-install pdo pdo_mysql bcmath + +COPY docker-compose/crontab /etc/crontabs/root + +CMD ["crond", "-f"] diff --git a/crater/docker-compose/crontab b/crater/docker-compose/crontab new file mode 100644 index 0000000..777a48e --- /dev/null +++ b/crater/docker-compose/crontab @@ -0,0 +1 @@ +* * * * * cd /var/www && php artisan schedule:run >> /dev/stdout 2>&1 diff --git a/crater/docker-compose/nginx/nginx.conf b/crater/docker-compose/nginx/nginx.conf new file mode 100644 index 0000000..ba2aa53 --- /dev/null +++ b/crater/docker-compose/nginx/nginx.conf @@ -0,0 +1,22 @@ +server { + client_max_body_size 64M; + listen 80; + index index.php index.html; + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /var/www/public; + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass app:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_read_timeout 300; + } + location / { + try_files $uri $uri/ /index.php?$query_string; + gzip_static on; + } +} diff --git a/crater/docker-compose/php/uploads.ini b/crater/docker-compose/php/uploads.ini new file mode 100644 index 0000000..293cde9 --- /dev/null +++ b/crater/docker-compose/php/uploads.ini @@ -0,0 +1,4 @@ +file_uploads = On +upload_max_filesize = 64M +post_max_size = 64M +max_execution_time = 300 diff --git a/crater/docker-compose/setup.sh b/crater/docker-compose/setup.sh new file mode 100755 index 0000000..a7ce313 --- /dev/null +++ b/crater/docker-compose/setup.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +docker-compose exec app composer install --no-interaction --prefer-dist --optimize-autoloader + +docker-compose exec app php artisan storage:link || true +docker-compose exec app php artisan key:generate diff --git a/crater/package-lock.json b/crater/package-lock.json new file mode 100644 index 0000000..0a9ec0d --- /dev/null +++ b/crater/package-lock.json @@ -0,0 +1,3403 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@aesoper/normal-utils": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@aesoper/normal-utils/-/normal-utils-0.1.5.tgz", + "integrity": "sha512-LFF/6y6h5mfwhnJaWqqxuC8zzDaHCG62kMRkd8xhDtq62TQj9dM17A9DhE87W7DhiARJsHLgcina/9P4eNCN1w==" + }, + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==" + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", + "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==" + }, + "@babel/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + } + } + } + }, + "@headlessui/vue": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.4.0.tgz", + "integrity": "sha512-BBLDciyKiGK03whaSVkUacDY2Cd5AR05JCUPWQLvQ9HtjQc9tv5RyPpcdmoXJa+XWI10e3U1JxL+8FY7kJMcEQ==" + }, + "@heroicons/vue": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-1.0.4.tgz", + "integrity": "sha512-jm7JMoUGr7Asn07oYNmewxkdQALnskTzRo17iGpHG/apLcc+GFdvdN4XvWZ2awStodaqeZ4eYWg7UcI0LvLETQ==" + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, + "@intlify/core-base": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.7.tgz", + "integrity": "sha512-q1W2j81xbHyfKrNcca/CeJyf0Bcx4u9UDu05l7AaiJbqOseTme2o2I3wp1hDDCtmC7k7HgX0sAygyHNJH9swuQ==", + "requires": { + "@intlify/devtools-if": "9.1.7", + "@intlify/message-compiler": "9.1.7", + "@intlify/message-resolver": "9.1.7", + "@intlify/runtime": "9.1.7", + "@intlify/shared": "9.1.7", + "@intlify/vue-devtools": "9.1.7" + } + }, + "@intlify/devtools-if": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.7.tgz", + "integrity": "sha512-/DcN5FUySSkQhDqx5y1RvxfuCXO3Ot/dUEIOs472qbM7Hyb2qif+eXCnwHBzlI4+wEfQVT6L0PiM1a7Er/ro9g==", + "requires": { + "@intlify/shared": "9.1.7" + } + }, + "@intlify/message-compiler": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.7.tgz", + "integrity": "sha512-JZNkAhr3O7tnbdbRBcpYfqr/Ai26WTzX0K/lV8Y1KVdOIj/dGiamaffdWUdFiDXUnbJRNbPiOaKxy7Pwip3KxQ==", + "requires": { + "@intlify/message-resolver": "9.1.7", + "@intlify/shared": "9.1.7", + "source-map": "0.6.1" + } + }, + "@intlify/message-resolver": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.7.tgz", + "integrity": "sha512-WTK+OaXJYjyquLGhuCyDvU2WHkG+kXzXeHagmVFHn+s118Jf2143zzkLLUrapP5CtZ/csuyjmYg7b3xQRQAmvw==" + }, + "@intlify/runtime": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.7.tgz", + "integrity": "sha512-QURPSlzhOVnRwS2XMGpCDsDkP42kfVBh94aAORxh/gVGzdgJip2vagrIFij/J69aEqdB476WJkMhVjP8VSHmiA==", + "requires": { + "@intlify/message-compiler": "9.1.7", + "@intlify/message-resolver": "9.1.7", + "@intlify/shared": "9.1.7" + } + }, + "@intlify/shared": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.7.tgz", + "integrity": "sha512-zt0zlUdalumvT9AjQNxPXA36UgOndUyvBMplh8uRZU0fhWHAwhnJTcf0NaG9Qvr8I1n3HPSs96+kLb/YdwTavQ==" + }, + "@intlify/vue-devtools": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.7.tgz", + "integrity": "sha512-DI5Wc0aOiohtBUGUkKAcryCWbbuaO4/PK4Pa/LaNCsFNxbtgR5qkIDmhBv9xVPYGTUhySXxaDDAMvOpBjhPJjw==", + "requires": { + "@intlify/message-resolver": "9.1.7", + "@intlify/runtime": "9.1.7", + "@intlify/shared": "9.1.7" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@popperjs/core": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz", + "integrity": "sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ==" + }, + "@tailwindcss/forms": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.4.0.tgz", + "integrity": "sha512-DeaQBx6EgEeuZPQACvC+mKneJsD8am1uiJugjgQK1+/Vt+Ai0GpFBC2T2fqnUad71WgOxyrZPE6BG1VaI6YqfQ==", + "dev": true, + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, + "@tiptap/core": { + "version": "2.0.0-beta.99", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.99.tgz", + "integrity": "sha512-DoSIgeYyWGWTDVHyquVM5SM61T4U8kKWjlmOtSPcee13Z5zXrbCBSxCTgtC3uh7I+OcoE/PNQQFMU9yWZzKnhw==", + "requires": { + "@types/prosemirror-commands": "^1.0.4", + "@types/prosemirror-inputrules": "^1.0.4", + "@types/prosemirror-keymap": "^1.0.4", + "@types/prosemirror-model": "^1.13.1", + "@types/prosemirror-schema-list": "^1.0.3", + "@types/prosemirror-state": "^1.2.7", + "@types/prosemirror-transform": "^1.1.4", + "@types/prosemirror-view": "^1.17.2", + "prosemirror-commands": "^1.1.10", + "prosemirror-inputrules": "^1.1.3", + "prosemirror-keymap": "^1.1.3", + "prosemirror-model": "^1.14.3", + "prosemirror-schema-list": "^1.1.5", + "prosemirror-state": "^1.3.4", + "prosemirror-transform": "^1.3.2", + "prosemirror-view": "^1.19.0" + } + }, + "@tiptap/extension-blockquote": { + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.15.tgz", + "integrity": "sha512-Cso44KsYsqKqaNveQmx5KVaLy9krq5AzE9WhGVDBSFqWhvuIJkQYrTRBbOTfUDs/st9VuwJrbjTDD65ow50wEw==", + "requires": { + "prosemirror-inputrules": "^1.1.3" + } + }, + "@tiptap/extension-bold": { + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.15.tgz", + "integrity": "sha512-jKyV6iiwhxwa0+7uuKD74jNDVNLNOS1GmU14MgaA95pY5e1fyaRBPPX8Gtt89niz2CLOY711AV17RPZTe/e60w==" + }, + "@tiptap/extension-bubble-menu": { + "version": "2.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.39.tgz", + "integrity": "sha512-hmA+ePR+MnRaTJ5MxoZ3yqOcK54cW2KQllZx16ZwSyM+yU9bXVhfMmyZwqRD7GGQFkrfnPm5QnedXDBYJD19OQ==", + "requires": { + "prosemirror-state": "^1.3.4", + "prosemirror-view": "^1.20.1", + "tippy.js": "^6.3.1" + } + }, + "@tiptap/extension-bullet-list": { + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.15.tgz", + "integrity": "sha512-5i44JzsZOh8Ci6CuYRQy6W3jCpYgX0+VuJKeHvZ6Aomy4Qqrtc9Jk43PBmCj91lNUUtH6Io9l+kDrLCumEFnEg==", + "requires": { + "prosemirror-inputrules": "^1.1.3" + } + }, + "@tiptap/extension-code": { + "version": "2.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.16.tgz", + "integrity": "sha512-Kakg/RMiVrxjzIkLVDXtbCzRh/9W8dgSG04IhMZNOI8N9vWn8Z78jdUyxEEDTcL/JyWWcMxn9AsJw2U5ajO3pA==" + }, + "@tiptap/extension-code-block": { + "version": "2.0.0-beta.18", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.18.tgz", + "integrity": "sha512-E2gz7ovl9nXLZzheqLyN3hi7A10fCaodDn4DvIl4wiEbKZpF7WFBNeb+FQetWNay9UWNeDO94SCX9+rT9H+yHA==", + "requires": { + "prosemirror-inputrules": "^1.1.3" + } + }, + "@tiptap/extension-document": { + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.13.tgz", + "integrity": "sha512-nrufdKziA/wovaY4DjGkc8OGuIZi8CH8CW3+yYfeWbruwFKkyZHlZy9nplFWSEqBHPAeqD+px9r91yGMW3ontA==" + }, + "@tiptap/extension-dropcursor": { + "version": "2.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.19.tgz", + "integrity": "sha512-rslIcVvD42NNh5sEbkCkG03DWMFBrS5KoK+lDOdIcC1DjmTtpVgcLvvE01btzaB3ljx+UVqI2Zaxa6VOiTeEMw==", + "requires": { + "@types/prosemirror-dropcursor": "^1.0.3", + "prosemirror-dropcursor": "^1.3.5" + } + }, + "@tiptap/extension-floating-menu": { + "version": "2.0.0-beta.33", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.33.tgz", + "integrity": "sha512-8s8DPnHIzXg7E7S/DjuS1AAFZKVYXY0KBKaEd1f2V45YOkKwN9El46Ugk/4Ir3yrrllvnisbP9ol+BAQmI0bMg==", + "requires": { + "prosemirror-state": "^1.3.4", + "prosemirror-view": "^1.20.1", + "tippy.js": "^6.3.1" + } + }, + "@tiptap/extension-gapcursor": { + "version": "2.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.24.tgz", + "integrity": "sha512-/6Ru0wNLIb3fo30Ar3z/rcakoUA2EIJL9sBFiuyHWTAIujeEaBzA6oG5L4PpP+daKd31JF0I6LjeWMSU9CBSFw==", + "requires": { + "@types/prosemirror-gapcursor": "^1.0.4", + "prosemirror-gapcursor": "^1.2.0" + } + }, + "@tiptap/extension-hard-break": { + "version": "2.0.0-beta.21", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.21.tgz", + "integrity": "sha512-Ukl+wjfLhE0tW7lWRpSPPo2tajjGnEaSc/Irey1JineFf+x/azA9rREzQy0r2AhORTalH7lj/KDmSdG8IT6syA==" + }, + "@tiptap/extension-heading": { + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.15.tgz", + "integrity": "sha512-UoXDwEdCV9KiPh0wj0jj2Jt6VDqkoTaSU3d9bmEBLwg1Gjgbuv39JDst7oxSqbf9rgbl3txbeOy35wVBKe9CqA==", + "requires": { + "prosemirror-inputrules": "^1.1.3" + } + }, + "@tiptap/extension-history": { + "version": "2.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.16.tgz", + "integrity": "sha512-nrNwV8a7zUt1t2I/kPX5Y6N9vZ8mrugimJIQmPGIp/4mmw1SEUzkaPpIsv6+ELmqMHSDktQ0ofb3pXeWDXWZvw==", + "requires": { + "@types/prosemirror-history": "^1.0.3", + "prosemirror-history": "^1.2.0" + } + }, + "@tiptap/extension-horizontal-rule": { + "version": "2.0.0-beta.21", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.21.tgz", + "integrity": "sha512-fgvRGuNEGWAitbcoz6VZSR9gcVIHksTy2QpXPnQC+N9Mi7havaxreYdMZn+oePW/5kdZoZNRx+jsf5DjKomvoQ==", + "requires": { + "prosemirror-state": "^1.3.4" + } + }, + "@tiptap/extension-italic": { + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.15.tgz", + "integrity": "sha512-ZCz1vCysLdvOUrwODuyBP0BDaemCLh6ib7qTYoSDKdive9kfn0Vc5Fg3o8xgHrtrUfwKIJz/sWOknjDEGIc9cw==" + }, + "@tiptap/extension-list-item": { + "version": "2.0.0-beta.14", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.14.tgz", + "integrity": "sha512-t6xwEqP+d5443Ul2Jvqz9kXb3ro7bA7yY9HA0vskm3120WxxHW9jxgxZN+82Ot5Tm7nXOAlsN6vuqnt4idnxZQ==" + }, + "@tiptap/extension-ordered-list": { + "version": "2.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.16.tgz", + "integrity": "sha512-3n0h5FBfQqBrN/zqF/Ngoyd1bZxeIRLwWI7ak4KulpvOg5V/yw3sw5CSxr2f13ZI9AgGaTq8yOsTYs9dkCCnsQ==", + "requires": { + "prosemirror-inputrules": "^1.1.3" + } + }, + "@tiptap/extension-paragraph": { + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.17.tgz", + "integrity": "sha512-qCQVCf9c2hgaeIdfy22PaoZyW5Vare/1aGkOEAaZma5RjrUbV9hrRKwoW9LsDjnh1EN1fIeKdg02yEhnHWtG8A==" + }, + "@tiptap/extension-strike": { + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.17.tgz", + "integrity": "sha512-+WRd0RuCK4+jFKNVN+4rHTa5VMqqGDO2uc+TknkqhFqWp/z96OAGlpHJOwPrnW1fLbpjEBBQIr1vVYSw6KgcZg==" + }, + "@tiptap/extension-text": { + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.13.tgz", + "integrity": "sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw==" + }, + "@tiptap/starter-kit": { + "version": "2.0.0-beta.97", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.97.tgz", + "integrity": "sha512-ySnJPG6px/Pv99TGCrgXOi7Ahh1qkpV171C791lLlFuH+lXMo719bWaeCTEiBDxjamVzh18nEJkIxyu6sucpSg==", + "requires": { + "@tiptap/core": "^2.0.0-beta.99", + "@tiptap/extension-blockquote": "^2.0.0-beta.15", + "@tiptap/extension-bold": "^2.0.0-beta.15", + "@tiptap/extension-bullet-list": "^2.0.0-beta.15", + "@tiptap/extension-code": "^2.0.0-beta.16", + "@tiptap/extension-code-block": "^2.0.0-beta.17", + "@tiptap/extension-document": "^2.0.0-beta.13", + "@tiptap/extension-dropcursor": "^2.0.0-beta.18", + "@tiptap/extension-gapcursor": "^2.0.0-beta.19", + "@tiptap/extension-hard-break": "^2.0.0-beta.15", + "@tiptap/extension-heading": "^2.0.0-beta.15", + "@tiptap/extension-history": "^2.0.0-beta.15", + "@tiptap/extension-horizontal-rule": "^2.0.0-beta.19", + "@tiptap/extension-italic": "^2.0.0-beta.15", + "@tiptap/extension-list-item": "^2.0.0-beta.14", + "@tiptap/extension-ordered-list": "^2.0.0-beta.15", + "@tiptap/extension-paragraph": "^2.0.0-beta.17", + "@tiptap/extension-strike": "^2.0.0-beta.17", + "@tiptap/extension-text": "^2.0.0-beta.13" + } + }, + "@tiptap/vue-3": { + "version": "2.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@tiptap/vue-3/-/vue-3-2.0.0-beta.52.tgz", + "integrity": "sha512-bHfJuhlCYOp+V3njGS4qQUVwyfjjb7KtPhZwl0FfYSNJ6/BTHYltd6L+UiQzVdcaoWFvPyF47fZajx602B5FGA==", + "requires": { + "@tiptap/extension-bubble-menu": "^2.0.0-beta.29", + "@tiptap/extension-floating-menu": "^2.0.0-beta.23", + "prosemirror-state": "^1.3.4", + "prosemirror-view": "^1.19.0", + "vue": "^3.0.0" + } + }, + "@types/orderedmap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/orderedmap/-/orderedmap-1.0.0.tgz", + "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/prosemirror-commands": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz", + "integrity": "sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*", + "@types/prosemirror-view": "*" + } + }, + "@types/prosemirror-dropcursor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz", + "integrity": "sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg==", + "requires": { + "@types/prosemirror-state": "*" + } + }, + "@types/prosemirror-gapcursor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz", + "integrity": "sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*" + } + }, + "@types/prosemirror-history": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz", + "integrity": "sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*" + } + }, + "@types/prosemirror-inputrules": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz", + "integrity": "sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*" + } + }, + "@types/prosemirror-keymap": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz", + "integrity": "sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==", + "requires": { + "@types/prosemirror-commands": "*", + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*", + "@types/prosemirror-view": "*" + } + }, + "@types/prosemirror-model": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.13.2.tgz", + "integrity": "sha512-a2rDB0aZ+7aIP7uBqQq1wLb4Hg4qqEvpkCqvhsgT/gG8IWC0peCAZfQ24sgTco0qSJLeDgIbtPeU6mgr869/kg==", + "requires": { + "@types/orderedmap": "*" + } + }, + "@types/prosemirror-schema-list": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz", + "integrity": "sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==", + "requires": { + "@types/orderedmap": "*", + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*" + } + }, + "@types/prosemirror-state": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.7.tgz", + "integrity": "sha512-clJf5uw3/XQnBJtl2RqYXoLMGBySnLYl43xtDvFfQZKkLnnYcM1SDU8dcz7lWjl2Dm+H98RpLOl44pp7DYT+wA==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-transform": "*", + "@types/prosemirror-view": "*" + } + }, + "@types/prosemirror-transform": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.4.tgz", + "integrity": "sha512-HP1PauvkqSgDquZut8HaLOTUDQ6jja/LAy4OA7tTS1XG7wqRnX3gLUyEj0mD6vFd4y8BPkNddNdOh/BeGHlUjg==", + "requires": { + "@types/prosemirror-model": "*" + } + }, + "@types/prosemirror-view": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.19.1.tgz", + "integrity": "sha512-fyQ4NVxAdfISWrE2qT8cpZdosXoH/1JuVYMBs9CdaXPbvi/8R2L2tkkcMRM314piKrO8nfYH5OBZKzP2Ax3jtA==", + "requires": { + "@types/prosemirror-model": "*", + "@types/prosemirror-state": "*", + "@types/prosemirror-transform": "*" + } + }, + "@vue/compiler-core": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.4.tgz", + "integrity": "sha512-c8NuQq7mUXXxA4iqD5VUKpyVeklK53+DMbojYMyZ0VPPrb0BUWrZWFiqSDT+MFDv0f6Hv3QuLiHWb1BWMXBbrw==", + "requires": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.2.4", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.4.tgz", + "integrity": "sha512-uj1nwO4794fw2YsYas5QT+FU/YGrXbS0Qk+1c7Kp1kV7idhZIghWLTjyvYibpGoseFbYLPd+sW2/noJG5H04EQ==", + "requires": { + "@vue/compiler-core": "3.2.4", + "@vue/shared": "3.2.4" + } + }, + "@vue/compiler-ssr": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.19.tgz", + "integrity": "sha512-oLon0Cn3O7WEYzzmzZavGoqXH+199LT+smdjBT3Uf3UX4HwDNuBFCmvL0TsqV9SQnIgKvBRbQ7lhbpnd4lqM3w==", + "requires": { + "@vue/compiler-dom": "3.2.19", + "@vue/shared": "3.2.19" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.19.tgz", + "integrity": "sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz", + "integrity": "sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw==", + "requires": { + "@vue/compiler-core": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + } + } + }, + "@vue/devtools-api": { + "version": "6.0.0-beta.18", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.18.tgz", + "integrity": "sha512-56vRhO7nXWWFYTx520BQSDlQH5VYpwy62hFDEqi2yHHEBpEqseOP5WYQusq7BEW3DXSY9E9cfPVR5CFtJbKuMg==" + }, + "@vue/reactivity": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.4.tgz", + "integrity": "sha512-ljWTR0hr8Tn09hM2tlmWxZzCBPlgGLnq/k8K8X6EcJhtV+C8OzFySnbWqMWataojbrQOocThwsC8awKthSl2uQ==", + "requires": { + "@vue/shared": "3.2.4" + } + }, + "@vue/ref-transform": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.19.tgz", + "integrity": "sha512-03wwUnoIAeKti5IGGx6Vk/HEBJ+zUcm5wrUM3+PQsGf7IYnXTbeIfHHpx4HeSeWhnLAjqZjADQwW8uA4rBmVbg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.19", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.19.tgz", + "integrity": "sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + } + } + }, + "@vue/runtime-core": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.4.tgz", + "integrity": "sha512-W6PtEOs8P8jKYPo3JwaMAozZQivxInUleGfNwI2pK1t8ZLZIxn4kAf7p4VF4jJdQB8SZBzpfWdLUc06j7IOmpQ==", + "requires": { + "@vue/reactivity": "3.2.4", + "@vue/shared": "3.2.4" + } + }, + "@vue/runtime-dom": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.4.tgz", + "integrity": "sha512-HcVtLyn2SGwsf6BFPwkvDPDOhOqkOKcfHDpBp5R1coX+qMsOFrY8lJnGXIY+JnxqFjND00E9+u+lq5cs/W7ooA==", + "requires": { + "@vue/runtime-core": "3.2.4", + "@vue/shared": "3.2.4", + "csstype": "^2.6.8" + } + }, + "@vue/server-renderer": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.19.tgz", + "integrity": "sha512-A9FNT7fgQJXItwdzWREntAgWKVtKYuXHBKGev/H4+ByTu8vB7gQXGcim01QxaJshdNg4dYuH2tEBZXCNCNx+/w==", + "requires": { + "@vue/compiler-ssr": "3.2.19", + "@vue/shared": "3.2.19" + }, + "dependencies": { + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + } + } + }, + "@vue/shared": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.4.tgz", + "integrity": "sha512-j2j1MRmjalVKr3YBTxl/BClSIc8UQ8NnPpLYclxerK65JIowI4O7n8O8lElveEtEoHxy1d7BelPUDI0Q4bumqg==" + }, + "@vueuse/core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-6.0.0.tgz", + "integrity": "sha512-PuBfNo/Zv+NkLcZaYWBA1WjqxQhTDC0DMQpoAIJdo/GFul/1SpBbONhUho2zqtOmq8vyGuK200wNFvyA4YUAMg==", + "requires": { + "@vueuse/shared": "6.0.0", + "vue-demi": "*" + } + }, + "@vueuse/shared": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-6.0.0.tgz", + "integrity": "sha512-PLjjqL8bxI5q86qk/ifXy572nfQE3rJc1RMem+dKcGayaagMnC4kXHEt64V98DVielSwr2FuYaeFodi4KJrvdg==", + "requires": { + "vue-demi": "*" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", + "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", + "dev": true, + "requires": { + "browserslist": "^4.17.5", + "caniuse-lite": "^1.0.30001272", + "fraction.js": "^4.1.1", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.1.0" + } + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "body-scroll-lock": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz", + "integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001287", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001287.tgz", + "integrity": "sha512-4udbs9bc0hfNrcje++AxBuc6PfLNHwh3PO9kbwnfCQWyqtlzg3py0YgFu8jyRTTo85VAz4U+VLxSlID09vNtWA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chart.js": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", + "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "clipboard": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", + "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + }, + "dependencies": { + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + } + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-js": { + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz", + "integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==" + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "csstype": { + "version": "2.6.18", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.18.tgz", + "integrity": "sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==" + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.23", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.23.tgz", + "integrity": "sha512-q3tB59Api3+DMbLnDPkW/UBHBO7KTGcF+rDCeb0GAGyqFj562s6y+c/2tDKTS/y5lbC+JOvT4MSUALJLPqlcSA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true + }, + "eslint-plugin-vue": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.16.0.tgz", + "integrity": "sha512-0E2dVvVC7I2Xm1HXyx+ZwPj9CNX4NJjs4K4r+GVsHWyt5Pew3JLD4fI7A91b2jeL0TXE7LlszrwLSTJU9eqehw==", + "dev": true, + "requires": { + "eslint-utils": "^2.1.0", + "natural-compare": "^1.4.0", + "semver": "^6.3.0", + "vue-eslint-parser": "^7.10.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "fraction.js": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", + "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", + "dev": true + }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "guid": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/guid/-/guid-0.0.12.tgz", + "integrity": "sha1-kTfFKxhffeEkkLm+vMFmC5Al/gw=" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "laravel-vite": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/laravel-vite/-/laravel-vite-0.0.7.tgz", + "integrity": "sha512-ko4Ux1bBXBnGoIFAvhmXuTwZ39RIIzdX2u7cXorfFlNLmSLvB0B5w0zZuykZmWdIK4GrGohLmkAtEYS/5pR08Q==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "deepmerge": "^4.2.2", + "dotenv": "^8.2.0", + "execa": "^5.0.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "maska": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/maska/-/maska-1.4.6.tgz", + "integrity": "sha512-dEZcoGp5Wufm2PZ4qZD81WKNaWO6XBIiHLazt5xShl4lydlH/5ZoLGEyJfzBaREXbAnsE5THShLyJKIaIeIuvA==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-svg-data-uri": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz", + "integrity": "sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "dev": true + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.1.30", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", + "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "orderedmap": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.1.tgz", + "integrity": "sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "postcss": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", + "requires": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + } + }, + "postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + } + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prosemirror-commands": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.1.10.tgz", + "integrity": "sha512-IWyBBXNAd44RM6NnBPljwq+/CM2oYCQJkF+YhKEAZNwzW0uFdGf4qComhjbKZzqFdu6Iub2ZhNsXgwPibA0lCQ==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-dropcursor": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.3.5.tgz", + "integrity": "sha512-tNUwcF2lPAkwKBZPZRtbxpwljnODRNZ3eiYloN1DSUqDjMT1nBZm0nejaEMS1TvNQ+3amibUSAiV4hX+jpASFA==", + "requires": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "prosemirror-gapcursor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.0.tgz", + "integrity": "sha512-yCLy5+0rVqLir/KcHFathQj4Rf8aRHi80FmEfKtM0JmyzvwdomslLzDZ/pX4oFhFKDgjl/WBBBFNqDyNifWg7g==", + "requires": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "prosemirror-history": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz", + "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==", + "requires": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "rope-sequence": "^1.3.0" + } + }, + "prosemirror-inputrules": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz", + "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==", + "requires": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-keymap": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz", + "integrity": "sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==", + "requires": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "prosemirror-model": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.14.3.tgz", + "integrity": "sha512-yzZlBaSxfUPIIP6U5Edh5zKxJPZ5f7bwZRhiCuH3UYkWhj+P3d8swHsbuAMOu/iDatDc5J/Qs5Mb3++mZf+CvQ==", + "requires": { + "orderedmap": "^1.1.0" + } + }, + "prosemirror-schema-list": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz", + "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-state": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz", + "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-transform": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz", + "integrity": "sha512-9NLVXy1Sfa2G6qPqhWMkEvwQQMTw7OyTqOZbJaGQWsCeH3hH5Cw+c5eNaLM1Uu75EyKLsEZhJ93XpHJBa6RX8A==", + "requires": { + "prosemirror-model": "^1.0.0" + } + }, + "prosemirror-view": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.20.1.tgz", + "integrity": "sha512-djWORhy3a706mUH4A2dgEEV0IPZqQd1tFyz/ZVHJNoqhSgq82FwG6dq7uqHeUB2KdVSNfI2yc3rwfqlC/ll2pA==", + "requires": { + "prosemirror-model": "^1.14.3", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "purgecss": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", + "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", + "dev": true, + "requires": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dev": true, + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rope-sequence": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz", + "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "sass": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.38.0.tgz", + "integrity": "sha512-WBccZeMigAGKoI+NgD7Adh0ab1HUq+6BmyBUEaGxtErbUtWUevEbdgo5EZiJQofLUGcKtlNaO2IdN73AHEua5g==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + } + } + }, + "sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", + "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "tailwind-scrollbar": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-1.3.1.tgz", + "integrity": "sha512-FeYuLxLtCRMO4PmjPJCzm5wQouFro2BInZXKPxqg54DR/55NAHoS8uNYWMiRG5l6qsLkWBfVEM34gq2XAQUwVg==", + "dev": true, + "requires": { + "tailwindcss": ">1.9.6" + }, + "dependencies": { + "color": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", + "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", + "dev": true, + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.6.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.6" + } + }, + "tailwindcss": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.16.tgz", + "integrity": "sha512-EireCtpQyyJ4Xz8NYzHafBoy4baCOO96flM0+HgtsFcIQ9KFy/YBK3GEtlnD+rXen0e4xm8t3WiUcKBJmN6yjg==", + "dev": true, + "requires": { + "arg": "^5.0.1", + "bytes": "^3.0.0", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "glob-parent": "^6.0.1", + "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", + "is-glob": "^4.0.1", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.1.0", + "node-emoji": "^1.11.0", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss-js": "^3.0.3", + "postcss-load-config": "^3.1.0", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "purgecss": "^4.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + } + } + } + }, + "tailwindcss": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.6.tgz", + "integrity": "sha512-+CA2f09rbHFDsdQ1iDvsOGbF1tZFmyPoRhUeaF9/5FRT5GYObtp+UjTSCdmeDcu6T90bx4WAaOkddYFPBkjbAA==", + "dev": true, + "requires": { + "arg": "^5.0.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "color-name": "^1.1.4", + "cosmiconfig": "^7.0.1", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss-js": "^3.0.3", + "postcss-load-config": "^3.1.0", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.7", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "postcss-selector-parser": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.7.tgz", + "integrity": "sha512-U+b/Deoi4I/UmE6KOVPpnhS7I7AYdKbhGcat+qTQ27gycvaACvNEw11ba6RrkwVmDVRW7sigWgLj4/KbbJjeDA==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tinycolor2": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", + "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==" + }, + "tippy.js": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.2.tgz", + "integrity": "sha512-35XVQI7Zl/jHZ51+8eHu/vVRXBjWYGobPm5G9FxOchj4r5dWhghKGS0nm0ARUKZTF96V7pPn7EbXS191NTwldw==", + "requires": { + "@popperjs/core": "^2.9.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v-money3": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/v-money3/-/v-money3-3.16.1.tgz", + "integrity": "sha512-U0GjmdybvEwfxCpZiTUbKugSglJbX6wxlyMeg0YJdLTAKlnjMRDph3hpNJlTlg5Gs8MQRpDVdaLysBjV749HLg==" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "vue": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.4.tgz", + "integrity": "sha512-rNCFmoewm8IwmTK0nj3ysKq53iRpNEFKoBJ4inar6tIh7Oj7juubS39RI8UI+VE7x+Cs2z6PBsadtZu7z2qppg==", + "requires": { + "@vue/compiler-dom": "3.2.4", + "@vue/runtime-dom": "3.2.4", + "@vue/shared": "3.2.4" + } + }, + "vue-demi": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.11.4.tgz", + "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==" + }, + "vue-eslint-parser": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz", + "integrity": "sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.2.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^6.3.0" + }, + "dependencies": { + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + } + } + }, + "vue-flatpickr-component": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/vue-flatpickr-component/-/vue-flatpickr-component-9.0.4.tgz", + "integrity": "sha512-E8XfzLhrPsQBtZluWYEn3m21VHn7PArYnel3QPYL3auBrVMc07WaK6b20e04OK8LUCq9V+OKNZe4MoI0znY/Hw==", + "requires": { + "flatpickr": "^4.6.9" + } + }, + "vue-i18n": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.7.tgz", + "integrity": "sha512-ujuuDanoHqtEd4GejWrbG/fXE9nrP51ElsEGxp0WBHfv+/ki0/wyUqkO+4fLikki2obGtXdviTPH0VNpas5K6g==", + "requires": { + "@intlify/core-base": "9.1.7", + "@intlify/shared": "9.1.7", + "@intlify/vue-devtools": "9.1.7", + "@vue/devtools-api": "^6.0.0-beta.7" + } + }, + "vue-router": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.11.tgz", + "integrity": "sha512-sha6I8fx9HWtvTrFZfxZkiQQBpqSeT+UCwauYjkdOQYRvwsGwimlQQE2ayqUwuuXGzquFpCPoXzYKWlzL4OuXg==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.14" + } + }, + "vue-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-4.1.0.tgz", + "integrity": "sha512-oPAeKKx5vY5Q8c7lMQPQyrBIbmWQGael5XEHqO1f+Y3V/RUZNuISz7KxI4woGjh79Vy/gDDaPX9j9zKYpaaA2g==", + "requires": { + "is-plain-object": "5.0.0" + } + }, + "vue3-colorpicker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/vue3-colorpicker/-/vue3-colorpicker-1.0.8.tgz", + "integrity": "sha512-QwAk8Ttu4aoZdIuBETB5Mn6ZE8/95cf7HeLjnEVF83ABqUYTbH7sZQww/AoNWvJhq05txFqiuFGXaS47aPpZdQ==", + "requires": { + "@aesoper/normal-utils": "^0.1.5", + "@popperjs/core": "^2.10.1", + "@vueuse/core": "^6.5.3", + "lodash-es": "^4.17.21", + "tinycolor2": "^1.4.2", + "vue": "^3.2.6", + "vue3-normal-directive": "^0.1.4", + "vue3-normal-library": "^0.1.6", + "vue3-storage": "^0.1.11" + }, + "dependencies": { + "@popperjs/core": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", + "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" + }, + "@vue/compiler-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.19.tgz", + "integrity": "sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz", + "integrity": "sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw==", + "requires": { + "@vue/compiler-core": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/compiler-sfc": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz", + "integrity": "sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.19", + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-ssr": "3.2.19", + "@vue/ref-transform": "3.2.19", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "@vue/reactivity": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.19.tgz", + "integrity": "sha512-FtachoYs2SnyrWup5UikP54xDX6ZJ1s5VgHcJp4rkGoutU3Ry61jhs+nCX7J64zjX992Mh9gGUC0LqTs8q9vCA==", + "requires": { + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.19.tgz", + "integrity": "sha512-qArZSWKxWsgKfxk9BelZ32nY0MZ31CAW2kUUyVJyxh4cTfHaXGbjiQB5JgsvKc49ROMNffv9t3/qjasQqAH+RQ==", + "requires": { + "@vue/reactivity": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.19.tgz", + "integrity": "sha512-hIRboxXwafeHhbZEkZYNV0MiJXPNf4fP0X6hM2TJb0vssz8BKhD9cF92BkRgZztTQevecbhk0gu4uAPJ3dxL9A==", + "requires": { + "@vue/runtime-core": "3.2.19", + "@vue/shared": "3.2.19", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + }, + "@vueuse/core": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-6.5.3.tgz", + "integrity": "sha512-o3CTu4nEqs371sDY5qLBX0r4QOm6GVpm3ApQc2Y+p8OMI2rRGartQo8xRykpUfsyq602A+SVtm/wxIWBkD/KCQ==", + "requires": { + "@vueuse/shared": "6.5.3", + "vue-demi": "*" + } + }, + "@vueuse/shared": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-6.5.3.tgz", + "integrity": "sha512-ChOKu3mECyZeqGJ/gHVm0CaHoZK5/TwNZr1ZM/aqH+RaRNQvC1qkLf1/8PBugzN3yRgC3BtZ/M1kLpGe/BFylw==", + "requires": { + "vue-demi": "*" + } + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "vue": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.19.tgz", + "integrity": "sha512-6KAMdIfAtlK+qohTIUE4urwAv4A3YRuo8uAbByApUmiB0CziGAAPs6qVugN6oHPia8YIafHB/37K0O6KZ7sGmA==", + "requires": { + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-sfc": "3.2.19", + "@vue/runtime-dom": "3.2.19", + "@vue/server-renderer": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "vue3-storage": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/vue3-storage/-/vue3-storage-0.1.11.tgz", + "integrity": "sha512-4pLQUMeGFduP2IaFage8Y/9AtUljKkm3z9N4ko30kTcKDwyr7JXOAsNFjYqw58SWNNLQdXqaGGAxZFVnk/JfUg==", + "requires": { + "core-js": "^3.6.5", + "cross-env": "^7.0.3", + "vue": "^3.0.0", + "vue-class-component": "^8.0.0-0", + "vue-router": "^4.0.0-0" + }, + "dependencies": { + "vue-class-component": { + "version": "8.0.0-rc.1", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz", + "integrity": "sha512-w1nMzsT/UdbDAXKqhwTmSoyuJzUXKrxLE77PCFVuC6syr8acdFDAq116xgvZh9UCuV0h+rlCtxXolr3Hi3HyPQ==" + } + } + } + } + }, + "vue3-normal-directive": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/vue3-normal-directive/-/vue3-normal-directive-0.1.4.tgz", + "integrity": "sha512-aO1xGJqdgb0a6LkMn1Q5GAkjISL6fCdhedMegFBLNKVlMDEi3YY+Vx9SaNEuLmQHCuQUY91m0TS17S/WSrn90g==", + "requires": { + "body-scroll-lock": "^3.1.5", + "clipboard": "^2.0.6", + "lodash-es": "^4.17.21", + "vue": "^3.2.6" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.19.tgz", + "integrity": "sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz", + "integrity": "sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw==", + "requires": { + "@vue/compiler-core": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/compiler-sfc": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz", + "integrity": "sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.19", + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-ssr": "3.2.19", + "@vue/ref-transform": "3.2.19", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "@vue/reactivity": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.19.tgz", + "integrity": "sha512-FtachoYs2SnyrWup5UikP54xDX6ZJ1s5VgHcJp4rkGoutU3Ry61jhs+nCX7J64zjX992Mh9gGUC0LqTs8q9vCA==", + "requires": { + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.19.tgz", + "integrity": "sha512-qArZSWKxWsgKfxk9BelZ32nY0MZ31CAW2kUUyVJyxh4cTfHaXGbjiQB5JgsvKc49ROMNffv9t3/qjasQqAH+RQ==", + "requires": { + "@vue/reactivity": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.19.tgz", + "integrity": "sha512-hIRboxXwafeHhbZEkZYNV0MiJXPNf4fP0X6hM2TJb0vssz8BKhD9cF92BkRgZztTQevecbhk0gu4uAPJ3dxL9A==", + "requires": { + "@vue/runtime-core": "3.2.19", + "@vue/shared": "3.2.19", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + }, + "vue": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.19.tgz", + "integrity": "sha512-6KAMdIfAtlK+qohTIUE4urwAv4A3YRuo8uAbByApUmiB0CziGAAPs6qVugN6oHPia8YIafHB/37K0O6KZ7sGmA==", + "requires": { + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-sfc": "3.2.19", + "@vue/runtime-dom": "3.2.19", + "@vue/server-renderer": "3.2.19", + "@vue/shared": "3.2.19" + } + } + } + }, + "vue3-normal-library": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/vue3-normal-library/-/vue3-normal-library-0.1.6.tgz", + "integrity": "sha512-TSqCeD092ETnjqamNKtXencLnG4a+NVWFZgalmyPtFH1FHvpxLP7eptT8krOL2sZVspficic8DghfDakw3tKRQ==", + "requires": { + "lodash-es": "^4.17.21", + "raf": "^3.4.1", + "vue": "^3.2.6", + "vue-types": "^4.1.0" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.19.tgz", + "integrity": "sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz", + "integrity": "sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw==", + "requires": { + "@vue/compiler-core": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/compiler-sfc": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz", + "integrity": "sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.19", + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-ssr": "3.2.19", + "@vue/ref-transform": "3.2.19", + "@vue/shared": "3.2.19", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "@vue/reactivity": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.19.tgz", + "integrity": "sha512-FtachoYs2SnyrWup5UikP54xDX6ZJ1s5VgHcJp4rkGoutU3Ry61jhs+nCX7J64zjX992Mh9gGUC0LqTs8q9vCA==", + "requires": { + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-core": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.19.tgz", + "integrity": "sha512-qArZSWKxWsgKfxk9BelZ32nY0MZ31CAW2kUUyVJyxh4cTfHaXGbjiQB5JgsvKc49ROMNffv9t3/qjasQqAH+RQ==", + "requires": { + "@vue/reactivity": "3.2.19", + "@vue/shared": "3.2.19" + } + }, + "@vue/runtime-dom": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.19.tgz", + "integrity": "sha512-hIRboxXwafeHhbZEkZYNV0MiJXPNf4fP0X6hM2TJb0vssz8BKhD9cF92BkRgZztTQevecbhk0gu4uAPJ3dxL9A==", + "requires": { + "@vue/runtime-core": "3.2.19", + "@vue/shared": "3.2.19", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", + "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" + }, + "vue": { + "version": "3.2.19", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.19.tgz", + "integrity": "sha512-6KAMdIfAtlK+qohTIUE4urwAv4A3YRuo8uAbByApUmiB0CziGAAPs6qVugN6oHPia8YIafHB/37K0O6KZ7sGmA==", + "requires": { + "@vue/compiler-dom": "3.2.19", + "@vue/compiler-sfc": "3.2.19", + "@vue/runtime-dom": "3.2.19", + "@vue/server-renderer": "3.2.19", + "@vue/shared": "3.2.19" + } + } + } + }, + "vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "requires": { + "sortablejs": "1.14.0" + } + }, + "w3c-keyname": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", + "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } + } +} diff --git a/crater/package.json b/crater/package.json new file mode 100644 index 0000000..c8da7a6 --- /dev/null +++ b/crater/package.json @@ -0,0 +1,60 @@ +{ + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "test": "eslint ./resources/scripts --ext .js,.vue" + }, + "devDependencies": { + "@rvxlab/tailwind-plugin-ios-full-height": "^1.0.0", + "@tailwindcss/aspect-ratio": "^0.4.0", + "@tailwindcss/forms": "^0.4.0", + "@tailwindcss/typography": "^0.5.0", + "@vitejs/plugin-vue": "^1.10.0", + "@vue/compiler-sfc": "^3.2.22", + "autoprefixer": "^10.4.0", + "cross-env": "^5.1", + "eslint": "^7.27.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-vue": "^7.0.0-beta.4", + "laravel-vite": "^0.0.7", + "postcss": "^8.4.5", + "prettier": "^2.3.0", + "sass": "^1.32.12", + "tailwind-scrollbar": "^1.3.1", + "tailwindcss": "^3.0.6", + "vite": "^2.6.1" + }, + "dependencies": { + "@headlessui/vue": "^1.4.0", + "@heroicons/vue": "^1.0.1", + "@popperjs/core": "^2.9.2", + "@stripe/stripe-js": "^1.21.2", + "@tailwindcss/line-clamp": "^0.3.0", + "@tiptap/core": "^2.0.0-beta.85", + "@tiptap/extension-text-align": "^2.0.0-beta.29", + "@tiptap/starter-kit": "^2.0.0-beta.81", + "@tiptap/vue-3": "^2.0.0-beta.38", + "@vuelidate/components": "^1.1.12", + "@vuelidate/core": "^2.0.0-alpha.32", + "@vuelidate/validators": "^2.0.0-alpha.25", + "@vueuse/core": "^6.0.0", + "axios": "^0.19", + "chart.js": "^2.7.3", + "guid": "0.0.12", + "lodash": "^4.17.13", + "maska": "^1.4.6", + "mini-svg-data-uri": "^1.3.3", + "moment": "^2.29.1", + "pinia": "^2.0.4", + "v-money3": "^3.13.5", + "v-tooltip": "^4.0.0-alpha.1", + "vue": "^3.2.0-beta.5", + "vue-flatpickr-component": "^9.0.3", + "vue-i18n": "^9.1.7", + "vue-router": "^4.0.8", + "vue3-colorpicker": "^1.0.5", + "vuedraggable": "^4.1.0" + } +} diff --git a/crater/phpunit.xml b/crater/phpunit.xml new file mode 100644 index 0000000..2ae2351 --- /dev/null +++ b/crater/phpunit.xml @@ -0,0 +1,30 @@ + + + + + ./tests/Unit + + + ./tests/Feature + + + + + ./app + + + + + + + + + + + + + + diff --git a/crater/postcss.config.js b/crater/postcss.config.js new file mode 100644 index 0000000..61bb5f9 --- /dev/null +++ b/crater/postcss.config.js @@ -0,0 +1,7 @@ +// postcss.config.js +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/crater/public/.htaccess b/crater/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/crater/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/crater/public/build/assets/404.e81599b7.js b/crater/public/build/assets/404.e81599b7.js new file mode 100644 index 0000000..a9832d5 --- /dev/null +++ b/crater/public/build/assets/404.e81599b7.js @@ -0,0 +1 @@ +import{G as u,aN as d,k as m,r as n,o as h,e as p,h as s,t as o,f as c,w as f,i as _,u as x}from"./vendor.d12b5734.js";const g={class:"w-full h-screen"},w={class:"flex items-center justify-center w-full h-full"},y={class:"flex flex-col items-center justify-center"},b={class:"text-primary-500",style:{"font-size":"10rem"}},v={class:"mb-10 text-3xl text-primary-500"},$={setup(k){const e=u();d();const l=m(()=>{if(e.path.indexOf("customer")>-1&&e.params.company)return`/${e.params.company}/customer/dashboard`;if(e.params.catchAll){let a=e.params.catchAll.indexOf("/");return a>-1?`/${e.params.catchAll.substring(a,0)}/customer/dashboard`:"/"}else return"/admin/dashboard"});return(t,a)=>{const r=n("BaseIcon"),i=n("router-link");return h(),p("div",g,[s("div",w,[s("div",y,[s("h1",b,o(t.$t("general.four_zero_four")),1),s("h5",v,o(t.$t("general.you_got_lost")),1),c(i,{class:"flex items-center w-32 h-12 px-3 py-1 text-base font-medium leading-none text-center text-white rounded whitespace-nowrap bg-primary-500 btn-lg hover:text-white",to:x(l)},{default:f(()=>[c(r,{name:"ArrowLeftIcon",class:"mr-2 text-white icon"}),_(" "+o(t.$t("general.go_home")),1)]),_:1},8,["to"])])])])}}};export{$ as default}; diff --git a/crater/public/build/assets/AccountSetting.7f3b69b7.js b/crater/public/build/assets/AccountSetting.7f3b69b7.js new file mode 100644 index 0000000..2ff3744 --- /dev/null +++ b/crater/public/build/assets/AccountSetting.7f3b69b7.js @@ -0,0 +1 @@ +var L=Object.defineProperty,P=Object.defineProperties;var T=Object.getOwnPropertyDescriptors;var V=Object.getOwnPropertySymbols;var z=Object.prototype.hasOwnProperty,E=Object.prototype.propertyIsEnumerable;var U=(u,s,i)=>s in u?L(u,s,{enumerable:!0,configurable:!0,writable:!0,value:i}):u[s]=i,S=(u,s)=>{for(var i in s||(s={}))z.call(s,i)&&U(u,i,s[i]);if(V)for(var i of V(s))E.call(s,i)&&U(u,i,s[i]);return u},I=(u,s)=>P(u,T(s));import{J,B as b,k as y,L as _,M as C,Q,N as H,P as K,a0 as O,T as W,r as m,o as M,e as X,f as r,w as d,u as e,x as Y,l as Z,m as x,j as ee,i as ae,t as se,U as te,h as ne}from"./vendor.d12b5734.js";import{e as oe,d as re,b as le}from"./main.465728e1.js";const ie=["onSubmit"],ue=ne("span",null,null,-1),ce={setup(u){const s=oe(),i=re(),F=le(),{t:v}=J();let p=b(!1),c=b(null),f=b([]);const $=b(!1);s.currentUser.avatar&&f.value.push({image:s.currentUser.avatar});const q=y(()=>({name:{required:_.withMessage(v("validation.required"),C)},email:{required:_.withMessage(v("validation.required"),C),email:_.withMessage(v("validation.email_incorrect"),Q)},password:{minLength:_.withMessage(v("validation.password_length",{count:8}),H(8))},confirm_password:{sameAsPassword:_.withMessage(v("validation.password_incorrect"),K(t.password))}})),t=O({name:s.currentUser.name,email:s.currentUser.email,language:s.currentUserSettings.language||F.selectedCompanySettings.language,password:"",confirm_password:""}),o=W(q,y(()=>t));function k(l,a){c.value=a}function N(){c.value=null,$.value=!0}async function A(){if(o.value.$touch(),o.value.$invalid)return!0;p.value=!0;let l={name:t.name,email:t.email};try{if(t.password!=null&&t.password!==void 0&&t.password!==""&&(l=I(S({},l),{password:t.password})),s.currentUserSettings.language!==t.language&&await s.updateUserSettings({settings:{language:t.language}}),(await s.updateCurrentUser(l)).data.data){if(p.value=!1,c.value||$.value){let w=new FormData;c.value&&w.append("admin_avatar",c.value),w.append("is_admin_avatar_removed",$.value),await s.uploadAvatar(w),c.value=null,$.value=!1}t.password="",t.confirm_password=""}}catch{return p.value=!1,!0}}return(l,a)=>{const w=m("BaseFileUploader"),g=m("BaseInputGroup"),B=m("BaseInput"),G=m("BaseMultiselect"),D=m("BaseInputGrid"),R=m("BaseIcon"),h=m("BaseButton"),j=m("BaseSettingCard");return M(),X("form",{class:"relative",onSubmit:te(A,["prevent"])},[r(j,{title:l.$t("settings.account_settings.account_settings"),description:l.$t("settings.account_settings.section_description")},{default:d(()=>[r(D,null,{default:d(()=>[r(g,{label:l.$tc("settings.account_settings.profile_picture")},{default:d(()=>[r(w,{modelValue:e(f),"onUpdate:modelValue":a[0]||(a[0]=n=>Y(f)?f.value=n:f=n),avatar:!0,accept:"image/*",onChange:k,onRemove:N},null,8,["modelValue"])]),_:1},8,["label"]),ue,r(g,{label:l.$tc("settings.account_settings.name"),error:e(o).name.$error&&e(o).name.$errors[0].$message,required:""},{default:d(()=>[r(B,{modelValue:e(t).name,"onUpdate:modelValue":a[1]||(a[1]=n=>e(t).name=n),invalid:e(o).name.$error,onInput:a[2]||(a[2]=n=>e(o).name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),r(g,{label:l.$tc("settings.account_settings.email"),error:e(o).email.$error&&e(o).email.$errors[0].$message,required:""},{default:d(()=>[r(B,{modelValue:e(t).email,"onUpdate:modelValue":a[3]||(a[3]=n=>e(t).email=n),invalid:e(o).email.$error,onInput:a[4]||(a[4]=n=>e(o).email.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),r(g,{error:e(o).password.$error&&e(o).password.$errors[0].$message,label:l.$tc("settings.account_settings.password")},{default:d(()=>[r(B,{modelValue:e(t).password,"onUpdate:modelValue":a[5]||(a[5]=n=>e(t).password=n),type:"password",onInput:a[6]||(a[6]=n=>e(o).password.$touch())},null,8,["modelValue"])]),_:1},8,["error","label"]),r(g,{label:l.$tc("settings.account_settings.confirm_password"),error:e(o).confirm_password.$error&&e(o).confirm_password.$errors[0].$message},{default:d(()=>[r(B,{modelValue:e(t).confirm_password,"onUpdate:modelValue":a[7]||(a[7]=n=>e(t).confirm_password=n),type:"password",onInput:a[8]||(a[8]=n=>e(o).confirm_password.$touch())},null,8,["modelValue"])]),_:1},8,["label","error"]),r(g,{label:l.$tc("settings.language")},{default:d(()=>[r(G,{modelValue:e(t).language,"onUpdate:modelValue":a[9]||(a[9]=n=>e(t).language=n),options:e(i).config.languages,label:"name","value-prop":"code","track-by":"name","open-direction":"top"},null,8,["modelValue","options"])]),_:1},8,["label"])]),_:1}),r(h,{loading:e(p),disabled:e(p),class:"mt-6"},{left:d(n=>[e(p)?ee("",!0):(M(),Z(R,{key:0,name:"SaveIcon",class:x(n.class)},null,8,["class"]))]),default:d(()=>[ae(" "+se(l.$tc("settings.company_info.save")),1)]),_:1},8,["loading","disabled"])]),_:1},8,["title","description"])],40,ie)}}};export{ce as default}; diff --git a/crater/public/build/assets/AddressInformation.7455dbc9.js b/crater/public/build/assets/AddressInformation.7455dbc9.js new file mode 100644 index 0000000..df97a57 --- /dev/null +++ b/crater/public/build/assets/AddressInformation.7455dbc9.js @@ -0,0 +1 @@ +import{G as C,J as z,B as I,r as m,o as b,e as y,f as o,w as r,h as d,t as p,u as e,m as h,i as F,j as v,l as S,U as j}from"./vendor.d12b5734.js";import{a as k,u as w}from"./global.dc565c4e.js";import"./auth.c88ceb4c.js";import"./main.465728e1.js";const D=["onSubmit"],G={class:"mb-6"},N={class:"font-bold text-left"},A={class:"mt-2 text-sm leading-snug text-left text-gray-500",style:{"max-width":"680px"}},T={class:"grid grid-cols-5 gap-4 mb-8"},E={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},J={class:"grid col-span-5 lg:col-span-4 gap-y-6 gap-x-4 md:grid-cols-6"},R={class:"md:col-span-3"},q={class:"flex items-center justify-start mb-6 md:justify-end md:mb-0"},H={class:"p-1"},K={class:"grid grid-cols-5 gap-4 mb-8"},L={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},O={key:0,class:"grid col-span-5 lg:col-span-4 gap-y-6 gap-x-4 md:grid-cols-6"},P={class:"md:col-span-3"},Q={class:"flex items-center justify-end"},se={setup(W){const s=k();C();const{tm:$,t:X}=z(),g=w();let u=I(!1);g.fetchCountries();function B(){u.value=!0;let a=s.userForm;s.updateCurrentUser({data:a,message:$("customers.address_updated_message")}).then(t=>{u.value=!1}).catch(t=>{u.value=!1})}return(a,t)=>{const i=m("BaseInput"),n=m("BaseInputGroup"),f=m("BaseMultiselect"),c=m("BaseTextarea"),U=m("BaseDivider"),_=m("BaseIcon"),V=m("BaseButton"),M=m("BaseCard");return b(),y("form",{class:"relative h-full mt-4",onSubmit:j(B,["prevent"])},[o(M,null,{default:r(()=>[d("div",G,[d("h6",N,p(a.$t("settings.menu_title.address_information")),1),d("p",A,p(a.$t("settings.address_information.section_description")),1)]),d("div",T,[d("h6",E,p(a.$t("customers.billing_address")),1),d("div",J,[o(n,{label:a.$t("customers.name"),class:"w-full md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.billing.name,"onUpdate:modelValue":t[0]||(t[0]=l=>e(s).userForm.billing.name=l),modelModifiers:{trim:!0},type:"text",class:"w-full",name:"address_name"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.country"),class:"md:col-span-3"},{default:r(()=>[o(f,{modelValue:e(s).userForm.billing.country_id,"onUpdate:modelValue":t[1]||(t[1]=l=>e(s).userForm.billing.country_id=l),"value-prop":"id",label:"name","track-by":"name","resolve-on-load":"",searchable:"",options:e(g).countries,placeholder:a.$t("general.select_country"),class:"w-full"},null,8,["modelValue","options","placeholder"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.state"),class:"md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.billing.state,"onUpdate:modelValue":t[2]||(t[2]=l=>e(s).userForm.billing.state=l),name:"billing.state",type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.city"),class:"md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.billing.city,"onUpdate:modelValue":t[3]||(t[3]=l=>e(s).userForm.billing.city=l),name:"billing.city",type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.address"),class:"md:col-span-3"},{default:r(()=>[o(c,{modelValue:e(s).userForm.billing.address_street_1,"onUpdate:modelValue":t[4]||(t[4]=l=>e(s).userForm.billing.address_street_1=l),modelModifiers:{trim:!0},placeholder:a.$t("general.street_1"),type:"text",name:"billing_street1","container-class":"mt-3"},null,8,["modelValue","placeholder"]),o(c,{modelValue:e(s).userForm.billing.address_street_2,"onUpdate:modelValue":t[5]||(t[5]=l=>e(s).userForm.billing.address_street_2=l),modelModifiers:{trim:!0},placeholder:a.$t("general.street_2"),type:"text",class:"mt-3",name:"billing_street2","container-class":"mt-3"},null,8,["modelValue","placeholder"])]),_:1},8,["label"]),d("div",R,[o(n,{label:a.$t("customers.phone"),class:"text-left"},{default:r(()=>[o(i,{modelValue:e(s).userForm.billing.phone,"onUpdate:modelValue":t[6]||(t[6]=l=>e(s).userForm.billing.phone=l),modelModifiers:{trim:!0},type:"text",name:"phone"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.zip_code"),class:"mt-2 text-left"},{default:r(()=>[o(i,{modelValue:e(s).userForm.billing.zip,"onUpdate:modelValue":t[7]||(t[7]=l=>e(s).userForm.billing.zip=l),modelModifiers:{trim:!0},type:"text",name:"zip"},null,8,["modelValue"])]),_:1},8,["label"])])])]),o(U,{class:"mb-5 md:mb-8"}),d("div",q,[d("div",H,[o(V,{ref:(l,x)=>{x.sameAddress=l},type:"button",onClick:t[8]||(t[8]=l=>e(s).copyAddress(!0))},{left:r(l=>[o(_,{name:"DocumentDuplicateIcon",class:h(l.class)},null,8,["class"])]),default:r(()=>[F(" "+p(a.$t("customers.copy_billing_address")),1)]),_:1},512)])]),d("div",K,[d("h6",L,p(a.$t("customers.shipping_address")),1),e(s).userForm.shipping?(b(),y("div",O,[o(n,{label:a.$t("customers.name"),class:"md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.shipping.name,"onUpdate:modelValue":t[9]||(t[9]=l=>e(s).userForm.shipping.name=l),modelModifiers:{trim:!0},type:"text",name:"address_name"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.country"),class:"md:col-span-3"},{default:r(()=>[o(f,{modelValue:e(s).userForm.shipping.country_id,"onUpdate:modelValue":t[10]||(t[10]=l=>e(s).userForm.shipping.country_id=l),"value-prop":"id",label:"name","track-by":"name","resolve-on-load":"",searchable:"",options:e(g).countries,placeholder:a.$t("general.select_country"),class:"w-full"},null,8,["modelValue","options","placeholder"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.state"),class:"md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.shipping.state,"onUpdate:modelValue":t[11]||(t[11]=l=>e(s).userForm.shipping.state=l),name:"shipping.state",type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.city"),class:"md:col-span-3"},{default:r(()=>[o(i,{modelValue:e(s).userForm.shipping.city,"onUpdate:modelValue":t[12]||(t[12]=l=>e(s).userForm.shipping.city=l),name:"shipping.city",type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.address"),class:"md:col-span-3"},{default:r(()=>[o(c,{modelValue:e(s).userForm.shipping.address_street_1,"onUpdate:modelValue":t[13]||(t[13]=l=>e(s).userForm.shipping.address_street_1=l),modelModifiers:{trim:!0},type:"text",placeholder:a.$t("general.street_1"),name:"shipping_street1"},null,8,["modelValue","placeholder"]),o(c,{modelValue:e(s).userForm.shipping.address_street_2,"onUpdate:modelValue":t[14]||(t[14]=l=>e(s).userForm.shipping.address_street_2=l),modelModifiers:{trim:!0},type:"text",placeholder:a.$t("general.street_2"),name:"shipping_street2",class:"mt-3"},null,8,["modelValue","placeholder"])]),_:1},8,["label"]),d("div",P,[o(n,{label:a.$t("customers.phone"),class:"text-left"},{default:r(()=>[o(i,{modelValue:e(s).userForm.shipping.phone,"onUpdate:modelValue":t[15]||(t[15]=l=>e(s).userForm.shipping.phone=l),modelModifiers:{trim:!0},type:"text",name:"phone"},null,8,["modelValue"])]),_:1},8,["label"]),o(n,{label:a.$t("customers.zip_code"),class:"mt-2 text-left"},{default:r(()=>[o(i,{modelValue:e(s).userForm.shipping.zip,"onUpdate:modelValue":t[16]||(t[16]=l=>e(s).userForm.shipping.zip=l),modelModifiers:{trim:!0},type:"text",name:"zip"},null,8,["modelValue"])]),_:1},8,["label"])])])):v("",!0)]),d("div",Q,[o(V,{loading:e(u),disabled:e(u)},{left:r(l=>[e(u)?v("",!0):(b(),S(_,{key:0,name:"SaveIcon",class:h(l.class)},null,8,["class"]))]),default:r(()=>[F(" "+p(a.$t("general.save")),1)]),_:1},8,["loading","disabled"])])]),_:1})],40,D)}}};export{se as default}; diff --git a/crater/public/build/assets/AstronautIcon.82b952e2.js b/crater/public/build/assets/AstronautIcon.82b952e2.js new file mode 100644 index 0000000..13d1807 --- /dev/null +++ b/crater/public/build/assets/AstronautIcon.82b952e2.js @@ -0,0 +1 @@ +import{o,e as i,h as l,m as d}from"./vendor.d12b5734.js";const n={width:"125",height:"110",viewBox:"0 0 125 110",fill:"none",xmlns:"http://www.w3.org/2000/svg"},r={"clip-path":"url(#clip0)"},C=l("defs",null,[l("clipPath",{id:"clip0"},[l("rect",{width:"124.808",height:"110",fill:"white"})])],-1),s={props:{primaryFillColor:{type:String,default:"fill-primary-500"},secondaryFillColor:{type:String,default:"fill-gray-600"}},setup(e){return(a,c)=>(o(),i("svg",n,[l("g",r,[l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M46.8031 84.4643C46.8031 88.8034 43.3104 92.3215 39.0026 92.3215C34.6948 92.3215 31.2021 88.8034 31.2021 84.4643C31.2021 80.1252 34.6948 76.6072 39.0026 76.6072C43.3104 76.6072 46.8031 80.1252 46.8031 84.4643Z",class:d(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M60.4536 110H64.3539V72.6785H60.4536V110Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M85.8055 76.6072H70.2045C69.1319 76.6072 68.2544 77.4911 68.2544 78.5715V82.5C68.2544 83.5804 69.1319 84.4643 70.2045 84.4643H85.8055C86.878 84.4643 87.7556 83.5804 87.7556 82.5V78.5715C87.7556 77.4911 86.878 76.6072 85.8055 76.6072ZM70.2045 82.5H85.8055V78.5715H70.2045V82.5Z",class:d(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M91.6556 1.96429C94.8811 1.96429 97.506 4.60821 97.506 7.85714V19.6429H83.8181L85.308 21.6071H99.4561V7.85714C99.4561 3.53571 95.9459 0 91.6556 0H33.152C28.8618 0 25.3516 3.53571 25.3516 7.85714V21.6071H39.3203L40.8745 19.6429H27.3017V7.85714C27.3017 4.60821 29.9265 1.96429 33.152 1.96429H91.6556Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M122.858 92.3213H117.007C115.935 92.3213 115.057 93.2052 115.057 94.2856V102.143C115.057 103.223 115.935 104.107 117.007 104.107H122.858C123.93 104.107 124.808 103.223 124.808 102.143V94.2856C124.808 93.2052 123.93 92.3213 122.858 92.3213ZM117.007 102.143H122.858V94.2856H117.007V102.143Z",class:d(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M103.356 43.2142V70.7142H21.4511V43.2142H26.1821V41.2498H19.501V72.6783H105.306V41.2498H98.3541L98.2839 43.2142H103.356Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M101.406 21.6071C104.632 21.6071 107.257 24.251 107.257 27.5V41.25H98.2257L98.0853 43.2142H109.207V27.5C109.207 23.1609 105.714 19.6428 101.406 19.6428H83.8182L85.0878 21.6071H101.406ZM40.8746 19.6428H23.4016C19.0937 19.6428 15.6011 23.1609 15.6011 27.5V43.2142H26.1961L26.3365 41.25H17.5512V27.5C17.5512 24.251 20.1761 21.6071 23.4016 21.6071H39.3204L40.8746 19.6428Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M62.4041 9.82153C45.1709 9.82153 31.2021 23.8917 31.2021 41.2501C31.2021 58.6085 45.1709 72.6787 62.4041 72.6787C79.6373 72.6787 93.606 58.6085 93.606 41.2501C93.606 23.8917 79.6373 9.82153 62.4041 9.82153ZM62.4041 11.7858C78.5335 11.7858 91.6559 25.0035 91.6559 41.2501C91.6559 57.4967 78.5335 70.7144 62.4041 70.7144C46.2746 70.7144 33.1523 57.4967 33.1523 41.2501C33.1523 25.0035 46.2746 11.7858 62.4041 11.7858Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M62.4041 19.6428C45.1709 19.6428 31.2021 23.8916 31.2021 41.25C31.2021 58.6084 45.1709 66.7857 62.4041 66.7857C79.6373 66.7857 93.606 58.6084 93.606 41.25C93.606 23.8916 79.6373 19.6428 62.4041 19.6428ZM62.4041 21.6071C82.6346 21.6071 91.6559 27.665 91.6559 41.25C91.6559 56.0096 80.7216 64.8214 62.4041 64.8214C44.0866 64.8214 33.1523 56.0096 33.1523 41.25C33.1523 27.665 42.1735 21.6071 62.4041 21.6071Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M101.406 70.7144H23.4014C10.478 70.7144 0 81.2685 0 94.2858V110H124.808V94.2858C124.808 81.2685 114.33 70.7144 101.406 70.7144ZM101.406 72.6786C113.234 72.6786 122.858 82.3724 122.858 94.2858V108.036H1.95012V94.2858C1.95012 82.3724 11.574 72.6786 23.4014 72.6786H101.406Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M33.152 33.3928H29.2518C27.0969 33.3928 25.3516 35.1509 25.3516 37.3214V45.1785C25.3516 47.3491 27.0969 49.1071 29.2518 49.1071H33.152V33.3928ZM31.2019 35.3571V47.1428H29.2518C28.1773 47.1428 27.3017 46.2609 27.3017 45.1785V37.3214C27.3017 36.2391 28.1773 35.3571 29.2518 35.3571H31.2019Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M95.556 33.3928H91.6558V49.1071H95.556C97.7109 49.1071 99.4562 47.3491 99.4562 45.1785V37.3214C99.4562 35.1509 97.7109 33.3928 95.556 33.3928ZM95.556 35.3571C96.6305 35.3571 97.5061 36.2391 97.5061 37.3214V45.1785C97.5061 46.2609 96.6305 47.1428 95.556 47.1428H93.6059V35.3571H95.556Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M94.581 15.7144C94.0447 15.7144 93.606 16.1563 93.606 16.6965V34.3751C93.606 34.9152 94.0447 35.3572 94.581 35.3572C95.1173 35.3572 95.5561 34.9152 95.5561 34.3751V16.6965C95.5561 16.1563 95.1173 15.7144 94.581 15.7144Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M38.0273 41.2499C37.4891 41.2499 37.0522 40.8099 37.0522 40.2678C37.0522 33.3142 44.1409 25.5356 53.6283 25.5356C54.1665 25.5356 54.6033 25.9756 54.6033 26.5178C54.6033 27.0599 54.1665 27.4999 53.6283 27.4999C45.2564 27.4999 39.0024 34.2414 39.0024 40.2678C39.0024 40.8099 38.5655 41.2499 38.0273 41.2499Z",class:d(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M97.5059 110H99.456V72.6785H97.5059V110Z",class:d(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M25.3516 110H27.3017V72.6785H25.3516V110Z",class:d(e.secondaryFillColor)},null,2)]),C]))}};export{s as _}; diff --git a/crater/public/build/assets/BackupSetting.135768cd.js b/crater/public/build/assets/BackupSetting.135768cd.js new file mode 100644 index 0000000..e110dc2 --- /dev/null +++ b/crater/public/build/assets/BackupSetting.135768cd.js @@ -0,0 +1 @@ +var te=Object.defineProperty,ae=Object.defineProperties;var se=Object.getOwnPropertyDescriptors;var U=Object.getOwnPropertySymbols;var oe=Object.prototype.hasOwnProperty,ne=Object.prototype.propertyIsEnumerable;var F=(u,t,l)=>t in u?te(u,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):u[t]=l,q=(u,t)=>{for(var l in t||(t={}))oe.call(t,l)&&F(u,l,t[l]);if(U)for(var l of U(t))ne.call(t,l)&&F(u,l,t[l]);return u},G=(u,t)=>ae(u,se(t));import{a as x,d as le,B as w,a0 as E,J as O,k as D,L as R,M as A,T as ce,r as d,o as L,l as H,w as i,h as $,i as S,t as C,u as o,f as n,m as J,j as ie,U as re,e as de,F as ue}from"./vendor.d12b5734.js";import{h as P,u as X,c as K,j as pe}from"./main.465728e1.js";import{u as Q}from"./disk.0ffde448.js";const W=(u=!1)=>{const t=u?window.pinia.defineStore:le,{global:l}=window.i18n;return t({id:"backup",state:()=>({backups:[],currentBackupData:{option:"full",selected_disk:null}}),actions:{fetchBackups(b){return new Promise((c,s)=>{x.get("/api/v1/backups",{params:b}).then(e=>{this.backups=e.data.data,c(e)}).catch(e=>{P(e),s(e)})})},createBackup(b){return new Promise((c,s)=>{x.post("/api/v1/backups",b).then(e=>{X().showNotification({type:"success",message:l.t("settings.backup.created_message")}),c(e)}).catch(e=>{P(e),s(e)})})},removeBackup(b){return new Promise((c,s)=>{x.delete(`/api/v1/backups/${b.disk}`,{params:b}).then(e=>{X().showNotification({type:"success",message:l.t("settings.backup.deleted_message")}),c(e)}).catch(e=>{P(e),s(e)})})}}})()},ke={class:"flex justify-between w-full"},me=["onSubmit"],fe={class:"p-6"},_e={class:"z-0 flex justify-end p-4 border-t border-gray-200 border-solid"},be={setup(u){w(null),w(!1);let t=w(!1),l=w(!1);const b=E(["full","only-db","only-files"]),c=W(),s=K(),e=Q(),{t:f}=O(),_=D(()=>s.active&&s.componentName==="BackupModal"),M=D(()=>e.disks.map(r=>G(q({},r),{name:r.name+" \u2014 ["+r.driver+"]"}))),V=D(()=>({currentBackupData:{option:{required:R.withMessage(f("validation.required"),A)},selected_disk:{required:R.withMessage(f("validation.required"),A)}}})),g=ce(V,D(()=>c));async function N(){if(g.value.currentBackupData.$touch(),g.value.currentBackupData.$invalid)return!0;let r={option:c.currentBackupData.option,file_disk_id:c.currentBackupData.selected_disk.id};try{t.value=!0,(await c.createBackup(r)).data&&(t.value=!1,s.refreshData&&s.refreshData(),s.closeModal())}catch{t.value=!1}}async function j(){l.value=!0;let r=await e.fetchDisks({limit:"all"});c.currentBackupData.selected_disk=r.data.data[0],l.value=!1}function I(){s.closeModal(),setTimeout(()=>{g.value.$reset(),c.$reset()})}return(r,h)=>{const a=d("BaseIcon"),p=d("BaseMultiselect"),m=d("BaseInputGroup"),k=d("BaseInputGrid"),y=d("BaseButton"),T=d("BaseModal");return L(),H(T,{show:o(_),onClose:I,onOpen:j},{header:i(()=>[$("div",ke,[S(C(o(s).title)+" ",1),n(a,{name:"XIcon",class:"w-6 h-6 text-gray-500 cursor-pointer",onClick:I})])]),default:i(()=>[$("form",{onSubmit:re(N,["prevent"])},[$("div",fe,[n(k,{layout:"one-column"},{default:i(()=>[n(m,{label:r.$t("settings.backup.select_backup_type"),error:o(g).currentBackupData.option.$error&&o(g).currentBackupData.option.$errors[0].$message,horizontal:"",required:"",class:"py-2"},{default:i(()=>[n(p,{modelValue:o(c).currentBackupData.option,"onUpdate:modelValue":h[0]||(h[0]=v=>o(c).currentBackupData.option=v),options:o(b),"can-deselect":!1,placeholder:r.$t("settings.backup.select_backup_type"),searchable:""},null,8,["modelValue","options","placeholder"])]),_:1},8,["label","error"]),n(m,{label:r.$t("settings.disk.select_disk"),error:o(g).currentBackupData.selected_disk.$error&&o(g).currentBackupData.selected_disk.$errors[0].$message,horizontal:"",required:"",class:"py-2"},{default:i(()=>[n(p,{modelValue:o(c).currentBackupData.selected_disk,"onUpdate:modelValue":h[1]||(h[1]=v=>o(c).currentBackupData.selected_disk=v),"content-loading":o(l),options:o(M),searchable:!0,"allow-empty":!1,label:"name","value-prop":"id",placeholder:r.$t("settings.disk.select_disk"),"track-by":"name",object:""},null,8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["label","error"])]),_:1})]),$("div",_e,[n(y,{class:"mr-3",variant:"primary-outline",type:"button",onClick:I},{default:i(()=>[S(C(r.$t("general.cancel")),1)]),_:1}),n(y,{loading:o(t),disabled:o(t),variant:"primary",type:"submit"},{left:i(v=>[o(t)?ie("",!0):(L(),H(a,{key:0,name:"SaveIcon",class:J(v.class)},null,8,["class"]))]),default:i(()=>[S(" "+C(r.$t("general.create")),1)]),_:1},8,["loading","disabled"])])],40,me)]),_:1},8,["show"])}}},ge={class:"grid my-14 md:grid-cols-3"},Be={class:"inline-block"},De={setup(u){const t=pe(),l=W(),b=K(),c=Q(),{t:s}=O(),e=E({selected_disk:{driver:"local"}}),f=w("");let _=w(!0);const M=D(()=>[{key:"path",label:s("settings.backup.path"),thClass:"extra",tdClass:"font-medium text-gray-900"},{key:"created_at",label:s("settings.backup.created_at"),tdClass:"font-medium text-gray-900"},{key:"size",label:s("settings.backup.size"),tdClass:"font-medium text-gray-900"},{key:"actions",label:"",tdClass:"text-right text-sm font-medium",sortable:!1}]),V=D(()=>c.disks.map(a=>G(q({},a),{name:a.name+" \u2014 ["+a.driver+"]"})));j();function g(a){t.openDialog({title:s("general.are_you_sure"),message:s("settings.backup.backup_confirm_delete"),yesLabel:s("general.ok"),noLabel:s("general.cancel"),variant:"danger",hideNoButton:!1,size:"lg"}).then(async p=>{if(p){let m={disk:e.selected_disk.driver,file_disk_id:e.selected_disk.id,path:a.path},k=await l.removeBackup(m);if(k.data.success||k.data.backup)return f.value&&f.value.refresh(),!0}})}function N(){setTimeout(()=>{f.value.refresh()},100)}async function j(){_.value=!0;let a=await c.fetchDisks({limit:"all"});a.data.error,e.selected_disk=a.data.data.find(p=>p.set_as_default==0),_.value=!1}async function I({page:a,filter:p,sort:m}){let k={disk:e.selected_disk.driver,filed_disk_id:e.selected_disk.id};_.value=!0;let y=await l.fetchBackups(k);return _.value=!1,{data:y.data.backups,pagination:{totalPages:1,currentPage:1}}}async function r(){b.openModal({title:s("settings.backup.create_backup"),componentName:"BackupModal",refreshData:f.value&&f.value.refresh,size:"sm"})}async function h(a){_.value=!0,window.axios({method:"GET",url:"/api/v1/download-backup",responseType:"blob",params:{disk:e.selected_disk.driver,file_disk_id:e.selected_disk.id,path:a.path}}).then(p=>{const m=window.URL.createObjectURL(new Blob([p.data])),k=document.createElement("a");k.href=m,k.setAttribute("download",a.path.split("/")[1]),document.body.appendChild(k),k.click(),_.value=!1}).catch(p=>{_.value=!1})}return(a,p)=>{const m=d("BaseIcon"),k=d("BaseButton"),y=d("BaseMultiselect"),T=d("BaseInputGroup"),v=d("BaseDropdownItem"),Y=d("BaseDropdown"),Z=d("BaseTable"),ee=d("BaseSettingCard");return L(),de(ue,null,[n(be),n(ee,{title:a.$tc("settings.backup.title",1),description:a.$t("settings.backup.description")},{action:i(()=>[n(k,{variant:"primary-outline",onClick:r},{left:i(B=>[n(m,{class:J(B.class),name:"PlusIcon"},null,8,["class"])]),default:i(()=>[S(" "+C(a.$t("settings.backup.new_backup")),1)]),_:1})]),default:i(()=>[$("div",ge,[n(T,{label:a.$t("settings.disk.select_disk"),"content-loading":o(_)},{default:i(()=>[n(y,{modelValue:o(e).selected_disk,"onUpdate:modelValue":p[0]||(p[0]=B=>o(e).selected_disk=B),"content-loading":o(_),options:o(V),"track-by":"name",placeholder:a.$t("settings.disk.select_disk"),label:"name",searchable:!0,object:"",class:"w-full","value-prop":"id",onSelect:N},null,8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["label","content-loading"])]),n(Z,{ref:(B,z)=>{z.table=B,f.value=B},class:"mt-10","show-filter":!1,data:I,columns:o(M)},{"cell-actions":i(({row:B})=>[n(Y,null,{activator:i(()=>[$("div",Be,[n(m,{name:"DotsHorizontalIcon",class:"text-gray-500"})])]),default:i(()=>[n(v,{onClick:z=>h(B.data)},{default:i(()=>[n(m,{name:"CloudDownloadIcon",class:"mr-3 text-gray-600"}),S(" "+C(a.$t("general.download")),1)]),_:2},1032,["onClick"]),n(v,{onClick:z=>g(B.data)},{default:i(()=>[n(m,{name:"TrashIcon",class:"mr-3 text-gray-600"}),S(" "+C(a.$t("general.delete")),1)]),_:2},1032,["onClick"])]),_:2},1024)]),_:1},8,["columns"])]),_:1},8,["title","description"])],64)}}};export{De as default}; diff --git a/crater/public/build/assets/BaseEditor.bacb9608.css b/crater/public/build/assets/BaseEditor.bacb9608.css new file mode 100644 index 0000000..37f6af0 --- /dev/null +++ b/crater/public/build/assets/BaseEditor.bacb9608.css @@ -0,0 +1 @@ +.ProseMirror{min-height:200px;padding:8px 12px;outline:none;border-radius:.375rem;border-top-left-radius:0;border-top-right-radius:0;border-width:1px;border-color:transparent}.ProseMirror h1{font-size:2em;font-weight:700}.ProseMirror h2{font-size:1.5em;font-weight:700}.ProseMirror h3{font-size:1.17em;font-weight:700}.ProseMirror ul{padding:0 1rem;list-style:disc!important}.ProseMirror ol{padding:0 1rem;list-style:auto!important}.ProseMirror blockquote{padding-left:1rem;border-left:2px solid rgba(13,13,13,.1)}.ProseMirror code{background-color:#6161611a;color:#616161;border-radius:.4rem;font-size:.9rem;padding:.1rem .3rem}.ProseMirror pre{background:#0d0d0d;color:#fff;font-family:JetBrainsMono,monospace;padding:.75rem 1rem;border-radius:.5rem}.ProseMirror pre code{color:inherit;padding:0;background:none;font-size:.8rem}.ProseMirror:focus{border-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--color-primary-400),var(--tw-border-opacity));--tw-ring-opacity: 1;--tw-ring-color: rgba(var(--color-primary-400), var(--tw-ring-opacity))} diff --git a/crater/public/build/assets/BaseEditor.c76beb41.js b/crater/public/build/assets/BaseEditor.c76beb41.js new file mode 100644 index 0000000..6991d43 --- /dev/null +++ b/crater/public/build/assets/BaseEditor.c76beb41.js @@ -0,0 +1,73 @@ +var nc=Object.defineProperty,oc=Object.defineProperties;var ic=Object.getOwnPropertyDescriptors;var Mr=Object.getOwnPropertySymbols;var Ko=Object.prototype.hasOwnProperty,$o=Object.prototype.propertyIsEnumerable;var Uo=(e,t,r)=>t in e?nc(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,S=(e,t)=>{for(var r in t||(t={}))Ko.call(t,r)&&Uo(e,r,t[r]);if(Mr)for(var r of Mr(t))$o.call(t,r)&&Uo(e,r,t[r]);return e},Tt=(e,t)=>oc(e,ic(t));var Go=(e,t)=>{var r={};for(var n in e)Ko.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Mr)for(var n of Mr(e))t.indexOf(n)<0&&$o.call(e,n)&&(r[n]=e[n]);return r};import{bf as sc,a8 as ac,bg as $e,B as xr,D as sn,b1 as Cr,E as ye,bh as cc,a7 as lc,be as uc,u as fc,al as pc,a0 as dc,bi as hc,bj as mc,o as gt,e as Mt,h as A,ai as vc,bk as gc,bl as yc,bm as bc,b6 as kc,C as Sc,aS as Mc,r as Q,l as xc,w as an,f as V,m as L,j as Cc}from"./vendor.d12b5734.js";import{_ as At}from"./main.465728e1.js";function ft(e){this.content=e}ft.prototype={constructor:ft,find:function(e){for(var t=0;t>1}};ft.from=function(e){if(e instanceof ft)return e;var t=[];if(e)for(var r in e)t.push(r,e[r]);return new ft(t)};var Yo=ft;function Xo(e,t,r){for(var n=0;;n++){if(n==e.childCount||n==t.childCount)return e.childCount==t.childCount?null:r;var o=e.child(n),i=t.child(n);if(o==i){r+=o.nodeSize;continue}if(!o.sameMarkup(i))return r;if(o.isText&&o.text!=i.text){for(var s=0;o.text[s]==i.text[s];s++)r++;return r}if(o.content.size||i.content.size){var a=Xo(o.content,i.content,r+1);if(a!=null)return a}r+=o.nodeSize}}function Qo(e,t,r,n){for(var o=e.childCount,i=t.childCount;;){if(o==0||i==0)return o==i?null:{a:r,b:n};var s=e.child(--o),a=t.child(--i),c=s.nodeSize;if(s==a){r-=c,n-=c;continue}if(!s.sameMarkup(a))return{a:r,b:n};if(s.isText&&s.text!=a.text){for(var l=0,u=Math.min(s.text.length,a.text.length);lt&&n(c,o+a,i,s)!==!1&&c.content.size){var u=a+1;c.nodesBetween(Math.max(0,t-u),Math.min(c.content.size,r-u),n,o+u)}a=l}};k.prototype.descendants=function(t){this.nodesBetween(0,this.size,t)};k.prototype.textBetween=function(t,r,n,o){var i="",s=!0;return this.nodesBetween(t,r,function(a,c){a.isText?(i+=a.text.slice(Math.max(t,c)-c,r-c),s=!n):a.isLeaf&&o?(i+=o,s=!n):!s&&a.isBlock&&(i+=n,s=!0)},0),i};k.prototype.append=function(t){if(!t.size)return this;if(!this.size)return t;var r=this.lastChild,n=t.firstChild,o=this.content.slice(),i=0;for(r.isText&&r.sameMarkup(n)&&(o[o.length-1]=r.withText(r.text+n.text),i=1);it)for(var i=0,s=0;st&&((sr)&&(a.isText?a=a.cut(Math.max(0,t-s),Math.min(a.text.length,r-s)):a=a.cut(Math.max(0,t-s-1),Math.min(a.content.size,r-s-1))),n.push(a),o+=a.nodeSize),s=c}return new k(n,o)};k.prototype.cutByIndex=function(t,r){return t==r?k.empty:t==0&&r==this.content.length?this:new k(this.content.slice(t,r))};k.prototype.replaceChild=function(t,r){var n=this.content[t];if(n==r)return this;var o=this.content.slice(),i=this.size+r.nodeSize-n.nodeSize;return o[t]=r,new k(o,i)};k.prototype.addToStart=function(t){return new k([t].concat(this.content),this.size+t.nodeSize)};k.prototype.addToEnd=function(t){return new k(this.content.concat(t),this.size+t.nodeSize)};k.prototype.eq=function(t){if(this.content.length!=t.content.length)return!1;for(var r=0;rthis.size||t<0)throw new RangeError("Position "+t+" outside of fragment ("+this+")");for(var n=0,o=0;;n++){var i=this.child(n),s=o+i.nodeSize;if(s>=t)return s==t||r>0?wr(n+1,s):wr(n,o);o=s}};k.prototype.toString=function(){return"<"+this.toStringInner()+">"};k.prototype.toStringInner=function(){return this.content.join(", ")};k.prototype.toJSON=function(){return this.content.length?this.content.map(function(t){return t.toJSON()}):null};k.fromJSON=function(t,r){if(!r)return k.empty;if(!Array.isArray(r))throw new RangeError("Invalid input for Fragment.fromJSON");return new k(r.map(t.nodeFromJSON))};k.fromArray=function(t){if(!t.length)return k.empty;for(var r,n=0,o=0;othis.type.rank&&(r||(r=t.slice(0,o)),r.push(this),n=!0),r&&r.push(i)}}return r||(r=t.slice()),n||r.push(this),r};P.prototype.removeFromSet=function(t){for(var r=0;r0&&(t.openStart=this.openStart),this.openEnd>0&&(t.openEnd=this.openEnd),t};C.fromJSON=function(t,r){if(!r)return C.empty;var n=r.openStart||0,o=r.openEnd||0;if(typeof n!="number"||typeof o!="number")throw new RangeError("Invalid input for Slice.fromJSON");return new C(k.fromJSON(t,r.content),n,o)};C.maxOpen=function(t,r){r===void 0&&(r=!0);for(var n=0,o=0,i=t.firstChild;i&&!i.isLeaf&&(r||!i.type.spec.isolating);i=i.firstChild)n++;for(var s=t.lastChild;s&&!s.isLeaf&&(r||!s.type.spec.isolating);s=s.lastChild)o++;return new C(t,n,o)};Object.defineProperties(C.prototype,Zo);function ti(e,t,r){var n=e.findIndex(t),o=n.index,i=n.offset,s=e.maybeChild(o),a=e.findIndex(r),c=a.index,l=a.offset;if(i==t||s.isText){if(l!=r&&!e.child(c).isText)throw new RangeError("Removing non-flat range");return e.cut(0,t).append(e.cut(r))}if(o!=c)throw new RangeError("Removing non-flat range");return e.replaceChild(o,s.copy(ti(s.content,t-i-1,r-i-1)))}function ei(e,t,r,n){var o=e.findIndex(t),i=o.index,s=o.offset,a=e.maybeChild(i);if(s==t||a.isText)return n&&!n.canReplace(i,i,r)?null:e.cut(0,t).append(r).append(e.cut(t));var c=ei(a.content,t-s-1,r);return c&&e.replaceChild(i,a.copy(c))}C.empty=new C(k.empty,0,0);function Oc(e,t,r){if(r.openStart>e.depth)throw new Jt("Inserted content deeper than insertion position");if(e.depth-r.openStart!=t.depth-r.openEnd)throw new Jt("Inconsistent open depths");return ri(e,t,r,0)}function ri(e,t,r,n){var o=e.index(n),i=e.node(n);if(o==t.index(n)&&n=0&&e.isText&&e.sameMarkup(t[r])?t[r]=e.withText(t[r].text+e.text):t.push(e)}function Ue(e,t,r,n){var o=(t||e).node(r),i=0,s=t?t.index(r):o.childCount;e&&(i=e.index(r),e.depth>r?i++:e.textOffset&&(be(e.nodeAfter,n),i++));for(var a=i;ao&&ln(e,t,o+1),s=n.depth>o&&ln(r,n,o+1),a=[];return Ue(null,e,o,a),i&&s&&t.index(o)==r.index(o)?(ni(i,s),be(ke(i,oi(e,t,r,n,o+1)),a)):(i&&be(ke(i,Ar(e,t,o+1)),a),Ue(t,r,o,a),s&&be(ke(s,Ar(r,n,o+1)),a)),Ue(n,null,o,a),new k(a)}function Ar(e,t,r){var n=[];if(Ue(null,e,r,n),e.depth>r){var o=ln(e,t,r+1);be(ke(o,Ar(e,t,r+1)),n)}return Ue(t,null,r,n),new k(n)}function wc(e,t){for(var r=t.depth-e.openStart,n=t.node(r),o=n.copy(e.content),i=r-1;i>=0;i--)o=t.node(i).copy(k.from(o));return{start:o.resolveNoCache(e.openStart+r),end:o.resolveNoCache(o.content.size-e.openEnd-r)}}var $=function(t,r,n){this.pos=t,this.path=r,this.depth=r.length/3-1,this.parentOffset=n},De={parent:{configurable:!0},doc:{configurable:!0},textOffset:{configurable:!0},nodeAfter:{configurable:!0},nodeBefore:{configurable:!0}};$.prototype.resolveDepth=function(t){return t==null?this.depth:t<0?this.depth+t:t};De.parent.get=function(){return this.node(this.depth)};De.doc.get=function(){return this.node(0)};$.prototype.node=function(t){return this.path[this.resolveDepth(t)*3]};$.prototype.index=function(t){return this.path[this.resolveDepth(t)*3+1]};$.prototype.indexAfter=function(t){return t=this.resolveDepth(t),this.index(t)+(t==this.depth&&!this.textOffset?0:1)};$.prototype.start=function(t){return t=this.resolveDepth(t),t==0?0:this.path[t*3-1]+1};$.prototype.end=function(t){return t=this.resolveDepth(t),this.start(t)+this.node(t).content.size};$.prototype.before=function(t){if(t=this.resolveDepth(t),!t)throw new RangeError("There is no position before the top-level node");return t==this.depth+1?this.pos:this.path[t*3-1]};$.prototype.after=function(t){if(t=this.resolveDepth(t),!t)throw new RangeError("There is no position after the top-level node");return t==this.depth+1?this.pos:this.path[t*3-1]+this.path[t*3].nodeSize};De.textOffset.get=function(){return this.pos-this.path[this.path.length-1]};De.nodeAfter.get=function(){var e=this.parent,t=this.index(this.depth);if(t==e.childCount)return null;var r=this.pos-this.path[this.path.length-1],n=e.child(t);return r?e.child(t).cut(r):n};De.nodeBefore.get=function(){var e=this.index(this.depth),t=this.pos-this.path[this.path.length-1];return t?this.parent.child(e).cut(0,t):e==0?null:this.parent.child(e-1)};$.prototype.posAtIndex=function(t,r){r=this.resolveDepth(r);for(var n=this.path[r*3],o=r==0?0:this.path[r*3-1]+1,i=0;i0;r--)if(this.start(r)<=t&&this.end(r)>=t)return r;return 0};$.prototype.blockRange=function(t,r){if(t===void 0&&(t=this),t.pos=0;n--)if(t.pos<=this.end(n)&&(!r||r(this.node(n))))return new Ge(this,t,n)};$.prototype.sameParent=function(t){return this.pos-this.parentOffset==t.pos-t.parentOffset};$.prototype.max=function(t){return t.pos>this.pos?t:this};$.prototype.min=function(t){return t.pos=0&&r<=t.content.size))throw new RangeError("Position "+r+" out of range");for(var n=[],o=0,i=r,s=t;;){var a=s.content.findIndex(i),c=a.index,l=a.offset,u=i-l;if(n.push(s,c,o+l),!u||(s=s.child(c),s.isText))break;i=u-1,o+=l+1}return new $(r,n,i)};$.resolveCached=function(t,r){for(var n=0;nt&&this.nodesBetween(t,r,function(i){return n.isInSet(i.marks)&&(o=!0),!o}),o};_t.isBlock.get=function(){return this.type.isBlock};_t.isTextblock.get=function(){return this.type.isTextblock};_t.inlineContent.get=function(){return this.type.inlineContent};_t.isInline.get=function(){return this.type.isInline};_t.isText.get=function(){return this.type.isText};_t.isLeaf.get=function(){return this.type.isLeaf};_t.isAtom.get=function(){return this.type.isAtom};B.prototype.toString=function(){if(this.type.spec.toDebugString)return this.type.spec.toDebugString(this);var t=this.type.name;return this.content.size&&(t+="("+this.content.toStringInner()+")"),ii(this.marks,t)};B.prototype.contentMatchAt=function(t){var r=this.type.contentMatch.matchFragment(this.content,0,t);if(!r)throw new Error("Called contentMatchAt on a node with invalid content");return r};B.prototype.canReplace=function(t,r,n,o,i){n===void 0&&(n=k.empty),o===void 0&&(o=0),i===void 0&&(i=n.childCount);var s=this.contentMatchAt(t).matchFragment(n,o,i),a=s&&s.matchFragment(this.content,r);if(!a||!a.validEnd)return!1;for(var c=o;c=0;r--)t=e[r].type.name+"("+t+")";return t}var pt=function(t){this.validEnd=t,this.next=[],this.wrapCache=[]},_r={inlineContent:{configurable:!0},defaultType:{configurable:!0},edgeCount:{configurable:!0}};pt.parse=function(t,r){var n=new Nr(t,r);if(n.next==null)return pt.empty;var o=ai(n);n.next&&n.err("Unexpected trailing text");var i=Bc(Pc(o));return zc(i,n),i};pt.prototype.matchType=function(t){for(var r=0;r>1};pt.prototype.edge=function(t){var r=t<<1;if(r>=this.next.length)throw new RangeError("There's no "+t+"th edge in this content match");return{type:this.next[r],next:this.next[r+1]}};pt.prototype.toString=function(){var t=[];function r(n){t.push(n);for(var o=1;o"+t.indexOf(n.next[s+1]);return i}).join(` +`)};Object.defineProperties(pt.prototype,_r);pt.empty=new pt(!0);var Nr=function(t,r){this.string=t,this.nodeTypes=r,this.inline=null,this.pos=0,this.tokens=t.split(/\s*(?=\b|\W|$)/),this.tokens[this.tokens.length-1]==""&&this.tokens.pop(),this.tokens[0]==""&&this.tokens.shift()},si={next:{configurable:!0}};si.next.get=function(){return this.tokens[this.pos]};Nr.prototype.eat=function(t){return this.next==t&&(this.pos++||!0)};Nr.prototype.err=function(t){throw new SyntaxError(t+" (in content expression '"+this.string+"')")};Object.defineProperties(Nr.prototype,si);function ai(e){var t=[];do t.push(Nc(e));while(e.eat("|"));return t.length==1?t[0]:{type:"choice",exprs:t}}function Nc(e){var t=[];do t.push(Ec(e));while(e.next&&e.next!=")"&&e.next!="|");return t.length==1?t[0]:{type:"seq",exprs:t}}function Ec(e){for(var t=Rc(e);;)if(e.eat("+"))t={type:"plus",expr:t};else if(e.eat("*"))t={type:"star",expr:t};else if(e.eat("?"))t={type:"opt",expr:t};else if(e.eat("{"))t=Dc(e,t);else break;return t}function ci(e){/\D/.test(e.next)&&e.err("Expected number, got '"+e.next+"'");var t=Number(e.next);return e.pos++,t}function Dc(e,t){var r=ci(e),n=r;return e.eat(",")&&(e.next!="}"?n=ci(e):n=-1),e.eat("}")||e.err("Unclosed braced range"),{type:"range",min:r,max:n,expr:t}}function Ic(e,t){var r=e.nodeTypes,n=r[t];if(n)return[n];var o=[];for(var i in r){var s=r[i];s.groups.indexOf(t)>-1&&o.push(s)}return o.length==0&&e.err("No node type or group '"+t+"' found"),o}function Rc(e){if(e.eat("(")){var t=ai(e);return e.eat(")")||e.err("Missing closing paren"),t}else if(/\W/.test(e.next))e.err("Unexpected token '"+e.next+"'");else{var r=Ic(e,e.next).map(function(n){return e.inline==null?e.inline=n.isInline:e.inline!=n.isInline&&e.err("Mixing inline and block content"),{type:"name",value:n}});return e.pos++,r.length==1?r[0]:{type:"choice",exprs:r}}}function Pc(e){var t=[[]];return o(i(e,0),r()),t;function r(){return t.push([])-1}function n(s,a,c){var l={term:c,to:a};return t[s].push(l),l}function o(s,a){s.forEach(function(c){return c.to=a})}function i(s,a){if(s.type=="choice")return s.exprs.reduce(function(M,y){return M.concat(i(y,a))},[]);if(s.type=="seq")for(var c=0;;c++){var l=i(s.exprs[c],a);if(c==s.exprs.length-1)return l;o(l,a=r())}else if(s.type=="star"){var u=r();return n(a,u),o(i(s.expr,u),u),[n(u)]}else if(s.type=="plus"){var f=r();return o(i(s.expr,a),f),o(i(s.expr,f),f),[n(f)]}else{if(s.type=="opt")return[n(a)].concat(i(s.expr,a));if(s.type=="range"){for(var p=a,d=0;d-1&&o[p+1];ui(e,f).forEach(function(h){d||o.push(u,d=[]),d.indexOf(h)==-1&&d.push(h)})}})});for(var i=t[n.join(",")]=new pt(n.indexOf(e.length-1)>-1),s=0;s-1};yt.prototype.allowsMarks=function(t){if(this.markSet==null)return!0;for(var r=0;r-1};var Se=function(t){this.spec={};for(var r in t)this.spec[r]=t[r];this.spec.nodes=Yo.from(t.nodes),this.spec.marks=Yo.from(t.marks),this.nodes=yt.compile(this.spec.nodes,this),this.marks=ie.compile(this.spec.marks,this);var n=Object.create(null);for(var o in this.nodes){if(o in this.marks)throw new RangeError(o+" can not be both a node and a mark");var i=this.nodes[o],s=i.spec.content||"",a=i.spec.marks;i.contentMatch=n[s]||(n[s]=pt.parse(s,this.nodes)),i.inlineContent=i.contentMatch.inlineContent,i.markSet=a=="_"?null:a?vi(this,a.split(" ")):a==""||!i.inlineContent?[]:null}for(var c in this.marks){var l=this.marks[c],u=l.spec.excludes;l.excluded=u==null?[l]:u==""?[]:vi(this,u.split(" "))}this.nodeFromJSON=this.nodeFromJSON.bind(this),this.markFromJSON=this.markFromJSON.bind(this),this.topNodeType=this.nodes[this.spec.topNode||"doc"],this.cached=Object.create(null),this.cached.wrappings=Object.create(null)};Se.prototype.node=function(t,r,n,o){if(typeof t=="string")t=this.nodeType(t);else if(t instanceof yt){if(t.schema!=this)throw new RangeError("Node type from different schema used ("+t.name+")")}else throw new RangeError("Invalid node type: "+t);return t.createChecked(r,n,o)};Se.prototype.text=function(t,r){var n=this.nodes.text;return new _c(n,n.defaultAttrs,t,P.setFrom(r))};Se.prototype.mark=function(t,r){return typeof t=="string"&&(t=this.marks[t]),t.create(r)};Se.prototype.nodeFromJSON=function(t){return B.fromJSON(this,t)};Se.prototype.markFromJSON=function(t){return P.fromJSON(this,t)};Se.prototype.nodeType=function(t){var r=this.nodes[t];if(!r)throw new RangeError("Unknown node type: "+t);return r};function vi(e,t){for(var r=[],n=0;n-1)&&r.push(s=c)}if(!s)throw new SyntaxError("Unknown mark type: '"+t[n]+"'")}return r}var Lt=function(t,r){var n=this;this.schema=t,this.rules=r,this.tags=[],this.styles=[],r.forEach(function(o){o.tag?n.tags.push(o):o.style&&n.styles.push(o)}),this.normalizeLists=!this.tags.some(function(o){if(!/^(ul|ol)\b/.test(o.tag)||!o.node)return!1;var i=t.nodes[o.node];return i.contentMatch.matchType(i)})};Lt.prototype.parse=function(t,r){r===void 0&&(r={});var n=new K(this,r,!1);return n.addAll(t,null,r.from,r.to),n.finish()};Lt.prototype.parseSlice=function(t,r){r===void 0&&(r={});var n=new K(this,r,!0);return n.addAll(t,null,r.from,r.to),C.maxOpen(n.finish())};Lt.prototype.matchTag=function(t,r,n){for(var o=n?this.tags.indexOf(n)+1:0;ot.length&&(s.style.charCodeAt(t.length)!=61||s.style.slice(t.length+1)!=r))){if(s.getAttrs){var a=s.getAttrs(r);if(a===!1)continue;s.attrs=a}return s}}};Lt.schemaRules=function(t){var r=[];function n(c){for(var l=c.priority==null?50:c.priority,u=0;u=0;r--)if(t.eq(this.stashMarks[r]))return this.stashMarks.splice(r,1)[0]};Zt.prototype.applyPending=function(t){for(var r=0,n=this.pendingMarks;r=0;o--){var i=this.nodes[o],s=i.findWrapping(t);if(s&&(!r||r.length>s.length)&&(r=s,n=i,!s.length)||i.solid)break}if(!r)return!1;this.sync(n);for(var a=0;athis.open){for(;r>this.open;r--)this.nodes[r-1].content.push(this.nodes[r].finish(t));this.nodes.length=this.open+1}};K.prototype.finish=function(){return this.open=0,this.closeExtra(this.isOpen),this.nodes[0].finish(this.isOpen||this.options.topOpen)};K.prototype.sync=function(t){for(var r=this.open;r>=0;r--)if(this.nodes[r]==t){this.open=r;return}};mn.currentPos.get=function(){this.closeExtra();for(var e=0,t=this.open;t>=0;t--){for(var r=this.nodes[t].content,n=r.length-1;n>=0;n--)e+=r[n].nodeSize;t&&e++}return e};K.prototype.findAtPoint=function(t,r){if(this.find)for(var n=0;n-1)return t.split(/\s*\|\s*/).some(this.matchesContext,this);var n=t.split("/"),o=this.options.context,i=!this.isOpen&&(!o||o.parent.type==this.nodes[0].type),s=-(o?o.depth+1:0)+(i?0:1),a=function(c,l){for(;c>=0;c--){var u=n[c];if(u==""){if(c==n.length-1||c==0)continue;for(;l>=s;l--)if(a(c-1,l))return!0;return!1}else{var f=l>0||l==0&&i?r.nodes[l].type:o&&l>=s?o.node(l-s).type:null;if(!f||f.name!=u&&f.groups.indexOf(u)==-1)return!1;l--}}return!0};return a(n.length-1,this.open)};K.prototype.textblockFromContext=function(){var t=this.options.context;if(t)for(var r=t.depth;r>=0;r--){var n=t.node(r).contentMatchAt(t.indexAfter(r)).defaultType;if(n&&n.isTextblock&&n.defaultAttrs)return n}for(var o in this.parser.schema.nodes){var i=this.parser.schema.nodes[o];if(i.isTextblock&&i.defaultAttrs)return i}};K.prototype.addPendingMark=function(t){var r=qc(t,this.top.pendingMarks);r&&this.top.stashMarks.push(r),this.top.pendingMarks=t.addToSet(this.top.pendingMarks)};K.prototype.removePendingMark=function(t,r){for(var n=this.open;n>=0;n--){var o=this.nodes[n],i=o.pendingMarks.lastIndexOf(t);if(i>-1)o.pendingMarks=t.removeFromSet(o.pendingMarks);else{o.activeMarks=t.removeFromSet(o.activeMarks);var s=o.popFromStashMark(t);s&&o.type&&o.type.allowsMarkType(s.type)&&(o.activeMarks=s.addToSet(o.activeMarks))}if(o==r)break}};Object.defineProperties(K.prototype,mn);function Fc(e){for(var t=e.firstChild,r=null;t;t=t.nextSibling){var n=t.nodeType==1?t.nodeName.toLowerCase():null;n&&gi.hasOwnProperty(n)&&r?(r.appendChild(t),t=r):n=="li"?r=t:n&&(r=null)}}function Vc(e,t){return(e.matches||e.msMatchesSelector||e.webkitMatchesSelector||e.mozMatchesSelector).call(e,t)}function Hc(e){for(var t=/\s*([\w-]+)\s*:\s*([^;]+)/g,r,n=[];r=t.exec(e);)n.push(r[1],r[2].trim());return n}function bi(e){var t={};for(var r in e)t[r]=e[r];return t}function jc(e,t){var r=t.schema.nodes,n=function(s){var a=r[s];if(!!a.allowsMarkType(e)){var c=[],l=function(u){c.push(u);for(var f=0;f=0;o--){var i=this.serializeMark(t.marks[o],t.isInline,r);i&&((i.contentDOM||i.dom).appendChild(n),n=i.dom)}return n};ot.prototype.serializeMark=function(t,r,n){n===void 0&&(n={});var o=this.marks[t.type.name];return o&&ot.renderSpec(vn(n),o(t,r))};ot.renderSpec=function(t,r,n){if(n===void 0&&(n=null),typeof r=="string")return{dom:t.createTextNode(r)};if(r.nodeType!=null)return{dom:r};if(r.dom&&r.dom.nodeType!=null)return r;var o=r[0],i=o.indexOf(" ");i>0&&(n=o.slice(0,i),o=o.slice(i+1));var s=null,a=n?t.createElementNS(n,o):t.createElement(o),c=r[1],l=1;if(c&&typeof c=="object"&&c.nodeType==null&&!Array.isArray(c)){l=2;for(var u in c)if(c[u]!=null){var f=u.indexOf(" ");f>0?a.setAttributeNS(u.slice(0,f),u.slice(f+1),c[u]):a.setAttribute(u,c[u])}}for(var p=l;pl)throw new RangeError("Content hole must be the only child of its parent node");return{dom:a,contentDOM:a}}else{var h=ot.renderSpec(t,d,n),v=h.dom,g=h.contentDOM;if(a.appendChild(v),g){if(s)throw new RangeError("Multiple content holes");s=g}}}return{dom:a,contentDOM:s}};ot.fromSchema=function(t){return t.cached.domSerializer||(t.cached.domSerializer=new ot(this.nodesFromSchema(t),this.marksFromSchema(t)))};ot.nodesFromSchema=function(t){var r=ki(t.nodes);return r.text||(r.text=function(n){return n.text}),r};ot.marksFromSchema=function(t){return ki(t.marks)};function ki(e){var t={};for(var r in e){var n=e[r].spec.toDOM;n&&(t[r]=n)}return t}function vn(e){return e.document||window.document}var Si=65535,Mi=Math.pow(2,16);function Jc(e,t){return e+t*Mi}function xi(e){return e&Si}function Wc(e){return(e-(e&Si))/Mi}var gn=function(t,r,n){r===void 0&&(r=!1),n===void 0&&(n=null),this.pos=t,this.deleted=r,this.recover=n},it=function(t,r){r===void 0&&(r=!1),this.ranges=t,this.inverted=r};it.prototype.recover=function(t){var r=0,n=xi(t);if(!this.inverted)for(var o=0;ot)break;var l=this.ranges[a+i],u=this.ranges[a+s],f=c+l;if(t<=f){var p=l?t==c?-1:t==f?1:r:r,d=c+o+(p<0?0:u);if(n)return d;var h=t==(r<0?c:f)?null:Jc(a/3,t-c);return new gn(d,r<0?t!=c:t!=f,h)}o+=u-l}return n?t+o:new gn(t+o)};it.prototype.touches=function(t,r){for(var n=0,o=xi(r),i=this.inverted?2:1,s=this.inverted?1:2,a=0;at)break;var l=this.ranges[a+i],u=c+l;if(t<=u&&a==o*3)return!0;n+=this.ranges[a+s]-l}return!1};it.prototype.forEach=function(t){for(var r=this.inverted?2:1,n=this.inverted?1:2,o=0,i=0;o=0;r--){var o=t.getMirror(r);this.appendMap(t.maps[r].invert(),o!=null&&o>r?n-o-1:null)}};dt.prototype.invert=function(){var t=new dt;return t.appendMappingInverted(this),t};dt.prototype.map=function(t,r){if(r===void 0&&(r=1),this.mirror)return this._map(t,r,!0);for(var n=this.from;ni&&c0};X.prototype.addStep=function(t,r){this.docs.push(this.doc),this.steps.push(t),this.mapping.appendMap(t.getMap()),this.doc=r};Object.defineProperties(X.prototype,yn);function Er(){throw new Error("Override me")}var bn=Object.create(null),ht=function(){};ht.prototype.apply=function(t){return Er()};ht.prototype.getMap=function(){return it.empty};ht.prototype.invert=function(t){return Er()};ht.prototype.map=function(t){return Er()};ht.prototype.merge=function(t){return null};ht.prototype.toJSON=function(){return Er()};ht.fromJSON=function(t,r){if(!r||!r.stepType)throw new RangeError("Invalid input for Step.fromJSON");var n=bn[r.stepType];if(!n)throw new RangeError("No step type "+r.stepType+" defined");return n.fromJSON(t,r)};ht.jsonID=function(t,r){if(t in bn)throw new RangeError("Duplicate use of step JSON ID "+t);return bn[t]=r,r.prototype.jsonID=t,r};var bt=function(t,r){this.doc=t,this.failed=r};bt.ok=function(t){return new bt(t,null)};bt.fail=function(t){return new bt(null,t)};bt.fromReplace=function(t,r,n,o){try{return bt.ok(t.replace(r,n,o))}catch(i){if(i instanceof Jt)return bt.fail(i.message);throw i}};var te=function(e){function t(r,n,o,i){e.call(this),this.from=r,this.to=n,this.slice=o,this.structure=!!i}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.apply=function(n){return this.structure&&kn(n,this.from,this.to)?bt.fail("Structure replace would overwrite content"):bt.fromReplace(n,this.from,this.to,this.slice)},t.prototype.getMap=function(){return new it([this.from,this.to-this.from,this.slice.size])},t.prototype.invert=function(n){return new t(this.from,this.from+this.slice.size,n.slice(this.from,this.to))},t.prototype.map=function(n){var o=n.mapResult(this.from,1),i=n.mapResult(this.to,-1);return o.deleted&&i.deleted?null:new t(o.pos,Math.max(o.pos,i.pos),this.slice)},t.prototype.merge=function(n){if(!(n instanceof t)||n.structure||this.structure)return null;if(this.from+this.slice.size==n.from&&!this.slice.openEnd&&!n.slice.openStart){var o=this.slice.size+n.slice.size==0?C.empty:new C(this.slice.content.append(n.slice.content),this.slice.openStart,n.slice.openEnd);return new t(this.from,this.to+(n.to-n.from),o,this.structure)}else if(n.to==this.from&&!this.slice.openStart&&!n.slice.openEnd){var i=this.slice.size+n.slice.size==0?C.empty:new C(n.slice.content.append(this.slice.content),n.slice.openStart,this.slice.openEnd);return new t(n.from,this.to,i,this.structure)}else return null},t.prototype.toJSON=function(){var n={stepType:"replace",from:this.from,to:this.to};return this.slice.size&&(n.slice=this.slice.toJSON()),this.structure&&(n.structure=!0),n},t.fromJSON=function(n,o){if(typeof o.from!="number"||typeof o.to!="number")throw new RangeError("Invalid input for ReplaceStep.fromJSON");return new t(o.from,o.to,C.fromJSON(n,o.slice),!!o.structure)},t}(ht);ht.jsonID("replace",te);var xt=function(e){function t(r,n,o,i,s,a,c){e.call(this),this.from=r,this.to=n,this.gapFrom=o,this.gapTo=i,this.slice=s,this.insert=a,this.structure=!!c}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.apply=function(n){if(this.structure&&(kn(n,this.from,this.gapFrom)||kn(n,this.gapTo,this.to)))return bt.fail("Structure gap-replace would overwrite content");var o=n.slice(this.gapFrom,this.gapTo);if(o.openStart||o.openEnd)return bt.fail("Gap is not a flat range");var i=this.slice.insertAt(this.insert,o.content);return i?bt.fromReplace(n,this.from,this.to,i):bt.fail("Content does not fit in gap")},t.prototype.getMap=function(){return new it([this.from,this.gapFrom-this.from,this.insert,this.gapTo,this.to-this.gapTo,this.slice.size-this.insert])},t.prototype.invert=function(n){var o=this.gapTo-this.gapFrom;return new t(this.from,this.from+this.slice.size+o,this.from+this.insert,this.from+this.insert+o,n.slice(this.from,this.to).removeBetween(this.gapFrom-this.from,this.gapTo-this.from),this.gapFrom-this.from,this.structure)},t.prototype.map=function(n){var o=n.mapResult(this.from,1),i=n.mapResult(this.to,-1),s=n.map(this.gapFrom,-1),a=n.map(this.gapTo,1);return o.deleted&&i.deleted||si.pos?null:new t(o.pos,i.pos,s,a,this.slice,this.insert,this.structure)},t.prototype.toJSON=function(){var n={stepType:"replaceAround",from:this.from,to:this.to,gapFrom:this.gapFrom,gapTo:this.gapTo,insert:this.insert};return this.slice.size&&(n.slice=this.slice.toJSON()),this.structure&&(n.structure=!0),n},t.fromJSON=function(n,o){if(typeof o.from!="number"||typeof o.to!="number"||typeof o.gapFrom!="number"||typeof o.gapTo!="number"||typeof o.insert!="number")throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");return new t(o.from,o.to,o.gapFrom,o.gapTo,C.fromJSON(n,o.slice),o.insert,!!o.structure)},t}(ht);ht.jsonID("replaceAround",xt);function kn(e,t,r){for(var n=e.resolve(t),o=r-t,i=n.depth;o>0&&i>0&&n.indexAfter(i)==n.node(i).childCount;)i--,o--;if(o>0)for(var s=n.node(i).maybeChild(n.indexAfter(i));o>0;){if(!s||s.isLeaf)return!0;s=s.firstChild,o--}return!1}function Kc(e,t,r){return(t==0||e.canReplace(t,e.childCount))&&(r==e.childCount||e.canReplace(0,r))}function Pe(e){for(var t=e.parent,r=t.content.cutByIndex(e.startIndex,e.endIndex),n=e.depth;;--n){var o=e.$from.node(n),i=e.$from.index(n),s=e.$to.indexAfter(n);if(nt;f--)p||r.index(f)>0?(p=!0,l=k.from(r.node(f).copy(l)),u++):a--;for(var d=k.empty,h=0,v=o,g=!1;v>t;v--)g||n.after(v+1)=0;n--)r=k.from(t[n].type.create(t[n].attrs,r));var o=e.start,i=e.end;return this.step(new xt(o,i,o,i,new C(r,0,0),t.length,!0))};X.prototype.setBlockType=function(e,t,r,n){var o=this;if(t===void 0&&(t=e),!r.isTextblock)throw new RangeError("Type given to setBlockType should be a textblock");var i=this.steps.length;return this.doc.nodesBetween(e,t,function(s,a){if(s.isTextblock&&!s.hasMarkup(r,n)&&Gc(o.doc,o.mapping.slice(i).map(a),r)){o.clearIncompatible(o.mapping.slice(i).map(a,1),r);var c=o.mapping.slice(i),l=c.map(a,1),u=c.map(a+s.nodeSize,1);return o.step(new xt(l,u,l+1,u-1,new C(k.from(r.create(n,null,s.marks)),0,0),1,!0)),!1}}),this};function Gc(e,t,r){var n=e.resolve(t),o=n.index();return n.parent.canReplaceWith(o,o+1,r)}X.prototype.setNodeMarkup=function(e,t,r,n){var o=this.doc.nodeAt(e);if(!o)throw new RangeError("No node at given position");t||(t=o.type);var i=t.create(r,null,n||o.marks);if(o.isLeaf)return this.replaceWith(e,e+o.nodeSize,i);if(!t.validContent(o.content))throw new RangeError("Invalid content for node type "+t.name);return this.step(new xt(e,e+o.nodeSize,e+1,e+o.nodeSize-1,new C(k.from(i),0,0),1,!0))};function ee(e,t,r,n){r===void 0&&(r=1);var o=e.resolve(t),i=o.depth-r,s=n&&n[n.length-1]||o.parent;if(i<0||o.parent.type.spec.isolating||!o.parent.canReplace(o.index(),o.parent.childCount)||!s.type.validContent(o.parent.content.cutByIndex(o.index(),o.parent.childCount)))return!1;for(var a=o.depth-1,c=r-2;a>i;a--,c--){var l=o.node(a),u=o.index(a);if(l.type.spec.isolating)return!1;var f=l.content.cutByIndex(u,l.childCount),p=n&&n[c]||l;if(p!=l&&(f=f.replaceChild(0,p.type.create(p.attrs))),!l.canReplace(u+1,l.childCount)||!p.type.validContent(f))return!1}var d=o.indexAfter(i),h=n&&n[0];return o.node(i).canReplaceWith(d,d,h?h.type:o.node(i+1).type)}X.prototype.split=function(e,t,r){t===void 0&&(t=1);for(var n=this.doc.resolve(e),o=k.empty,i=k.empty,s=n.depth,a=n.depth-t,c=t-1;s>a;s--,c--){o=k.from(n.node(s).copy(o));var l=r&&r[c];i=k.from(l?l.type.create(l.attrs,i):n.node(s).copy(i))}return this.step(new te(e,e,new C(o.append(i),t,t),!0))};function Mn(e,t){var r=e.resolve(t),n=r.index();return Yc(r.nodeBefore,r.nodeAfter)&&r.parent.canReplace(n,n+1)}function Yc(e,t){return e&&t&&!e.isLeaf&&e.canAppend(t)}X.prototype.join=function(e,t){t===void 0&&(t=1);var r=new te(e-t,e+t,C.empty,!0);return this.step(r)};function Xc(e,t,r){var n=e.resolve(t);if(n.parent.canReplaceWith(n.index(),n.index(),r))return t;if(n.parentOffset==0)for(var o=n.depth-1;o>=0;o--){var i=n.index(o);if(n.node(o).canReplaceWith(i,i,r))return n.before(o+1);if(i>0)return null}if(n.parentOffset==n.parent.content.size)for(var s=n.depth-1;s>=0;s--){var a=n.indexAfter(s);if(n.node(s).canReplaceWith(a,a,r))return n.after(s+1);if(a=0;a--){var c=a==n.depth?0:n.pos<=(n.start(a+1)+n.end(a+1))/2?-1:1,l=n.index(a)+(c>0?1:0),u=n.node(a),f=!1;if(s==1)f=u.canReplace(l,l,o);else{var p=u.contentMatchAt(l).findWrapping(o.firstChild.type);f=p&&u.canReplaceWith(l,l,p[0])}if(f)return c==0?n.pos:c<0?n.before(a+1):n.after(a+1)}return null}function xn(e,t,r){for(var n=[],o=0;o=i.pos?null:new t(o.pos,i.pos,this.mark)},t.prototype.merge=function(n){if(n instanceof t&&n.mark.eq(this.mark)&&this.from<=n.to&&this.to>=n.from)return new t(Math.min(this.from,n.from),Math.max(this.to,n.to),this.mark)},t.prototype.toJSON=function(){return{stepType:"addMark",mark:this.mark.toJSON(),from:this.from,to:this.to}},t.fromJSON=function(n,o){if(typeof o.from!="number"||typeof o.to!="number")throw new RangeError("Invalid input for AddMarkStep.fromJSON");return new t(o.from,o.to,n.markFromJSON(o.mark))},t}(ht);ht.jsonID("addMark",Cn);var Qe=function(e){function t(r,n,o){e.call(this),this.from=r,this.to=n,this.mark=o}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.apply=function(n){var o=this,i=n.slice(this.from,this.to),s=new C(xn(i.content,function(a){return a.mark(o.mark.removeFromSet(a.marks))}),i.openStart,i.openEnd);return bt.fromReplace(n,this.from,this.to,s)},t.prototype.invert=function(){return new Cn(this.from,this.to,this.mark)},t.prototype.map=function(n){var o=n.mapResult(this.from,1),i=n.mapResult(this.to,-1);return o.deleted&&i.deleted||o.pos>=i.pos?null:new t(o.pos,i.pos,this.mark)},t.prototype.merge=function(n){if(n instanceof t&&n.mark.eq(this.mark)&&this.from<=n.to&&this.to>=n.from)return new t(Math.min(this.from,n.from),Math.max(this.to,n.to),this.mark)},t.prototype.toJSON=function(){return{stepType:"removeMark",mark:this.mark.toJSON(),from:this.from,to:this.to}},t.fromJSON=function(n,o){if(typeof o.from!="number"||typeof o.to!="number")throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");return new t(o.from,o.to,n.markFromJSON(o.mark))},t}(ht);ht.jsonID("removeMark",Qe);X.prototype.addMark=function(e,t,r){var n=this,o=[],i=[],s=null,a=null;return this.doc.nodesBetween(e,t,function(c,l,u){if(!!c.isInline){var f=c.marks;if(!r.isInSet(f)&&u.type.allowsMarkType(r.type)){for(var p=Math.max(l,e),d=Math.min(l+c.nodeSize,t),h=r.addToSet(f),v=0;v=0;p--)this.step(o[p]);return this};function Qc(e,t,r,n){if(r===void 0&&(r=t),n===void 0&&(n=C.empty),t==r&&!n.size)return null;var o=e.resolve(t),i=e.resolve(r);return wi(o,i,n)?new te(t,r,n):new It(o,i,n).fit()}X.prototype.replace=function(e,t,r){t===void 0&&(t=e),r===void 0&&(r=C.empty);var n=Qc(this.doc,e,t,r);return n&&this.step(n),this};X.prototype.replaceWith=function(e,t,r){return this.replace(e,t,new C(k.from(r),0,0))};X.prototype.delete=function(e,t){return this.replace(e,t,C.empty)};X.prototype.insert=function(e,t){return this.replaceWith(e,e,t)};function wi(e,t,r){return!r.openStart&&!r.openEnd&&e.start()==t.start()&&e.parent.canReplace(e.index(),t.index(),r.content)}var It=function(t,r,n){this.$to=r,this.$from=t,this.unplaced=n,this.frontier=[];for(var o=0;o<=t.depth;o++){var i=t.node(o);this.frontier.push({type:i.type,match:i.contentMatchAt(t.indexAfter(o))})}this.placed=k.empty;for(var s=t.depth;s>0;s--)this.placed=k.from(t.node(s).copy(this.placed))},Ti={depth:{configurable:!0}};Ti.depth.get=function(){return this.frontier.length-1};It.prototype.fit=function(){for(;this.unplaced.size;){var t=this.findFittable();t?this.placeNodes(t):this.openMore()||this.dropNode()}var r=this.mustMoveInline(),n=this.placed.size-this.depth-this.$from.depth,o=this.$from,i=this.close(r<0?this.$to:o.doc.resolve(r));if(!i)return null;for(var s=this.placed,a=o.depth,c=i.depth;a&&c&&s.childCount==1;)s=s.firstChild.content,a--,c--;var l=new C(s,a,c);if(r>-1)return new xt(o.pos,r,this.$to.pos,this.$to.end(),l,n);if(l.size||o.pos!=this.$to.pos)return new te(o.pos,i.pos,l)};It.prototype.findFittable=function(){for(var t=1;t<=2;t++)for(var r=this.unplaced.openStart;r>=0;r--){var n=void 0,o=void 0;r?(o=On(this.unplaced.content,r-1).firstChild,n=o.content):n=this.unplaced.content;for(var i=n.firstChild,s=this.depth;s>=0;s--){var a=this.frontier[s],c=a.type,l=a.match,u=void 0,f=void 0;if(t==1&&(i?l.matchType(i.type)||(f=l.fillBefore(k.from(i),!1)):c.compatibleContent(o.type)))return{sliceDepth:r,frontierDepth:s,parent:o,inject:f};if(t==2&&i&&(u=l.findWrapping(i.type)))return{sliceDepth:r,frontierDepth:s,parent:o,wrap:u};if(o&&l.matchType(o.type))break}}};It.prototype.openMore=function(){var t=this.unplaced,r=t.content,n=t.openStart,o=t.openEnd,i=On(r,n);return!i.childCount||i.firstChild.isLeaf?!1:(this.unplaced=new C(r,n+1,Math.max(o,i.size+n>=r.size-o?n+1:0)),!0)};It.prototype.dropNode=function(){var t=this.unplaced,r=t.content,n=t.openStart,o=t.openEnd,i=On(r,n);if(i.childCount<=1&&n>0){var s=r.size-n<=n+i.size;this.unplaced=new C(Ze(r,n-1,1),n-1,s?n-1:o)}else this.unplaced=new C(Ze(r,n,1),n,o)};It.prototype.placeNodes=function(t){for(var r=t.sliceDepth,n=t.frontierDepth,o=t.parent,i=t.inject,s=t.wrap;this.depth>n;)this.closeFrontierNode();if(s)for(var a=0;a1||u==0||y.content.size)&&(h=R,p.push(Ai(y.mark(v.allowedMarks(y.marks)),f==1?u:0,f==l.childCount?M:-1)))}var m=f==l.childCount;m||(M=-1),this.placed=tr(this.placed,n,k.from(p)),this.frontier[n].match=h,m&&M<0&&o&&o.type==this.frontier[this.depth].type&&this.frontier.length>1&&this.closeFrontierNode();for(var I=0,O=l;I1&&i==this.$to.end(--o);)++i;return i};It.prototype.findCloseLevel=function(t){t:for(var r=Math.min(this.depth,t.depth);r>=0;r--){var n=this.frontier[r],o=n.match,i=n.type,s=r=0;c--){var l=this.frontier[c],u=l.match,f=l.type,p=wn(t,c,f,u,!0);if(!p||p.childCount)continue t}return{depth:r,fit:a,move:s?t.doc.resolve(t.after(r+1)):t}}}};It.prototype.close=function(t){var r=this.findCloseLevel(t);if(!r)return null;for(;this.depth>r.depth;)this.closeFrontierNode();r.fit.childCount&&(this.placed=tr(this.placed,r.depth,r.fit)),t=r.move;for(var n=r.depth+1;n<=t.depth;n++){var o=t.node(n),i=o.type.contentMatch.fillBefore(o.content,!0,t.index(n));this.openFrontierNode(o.type,o.attrs,i)}return t};It.prototype.openFrontierNode=function(t,r,n){var o=this.frontier[this.depth];o.match=o.match.matchType(t),this.placed=tr(this.placed,this.depth,k.from(t.create(r,n))),this.frontier.push({type:t,match:t.contentMatch})};It.prototype.closeFrontierNode=function(){var t=this.frontier.pop(),r=t.match.fillBefore(k.empty,!0);r.childCount&&(this.placed=tr(this.placed,this.frontier.length,r))};Object.defineProperties(It.prototype,Ti);function Ze(e,t,r){return t==0?e.cutByIndex(r):e.replaceChild(0,e.firstChild.copy(Ze(e.firstChild.content,t-1,r)))}function tr(e,t,r){return t==0?e.append(r):e.replaceChild(e.childCount-1,e.lastChild.copy(tr(e.lastChild.content,t-1,r)))}function On(e,t){for(var r=0;r1&&(n=n.replaceChild(0,Ai(n.firstChild,t-1,n.childCount==1?r-1:0))),t>0&&(n=e.type.contentMatch.fillBefore(n).append(n),r<=0&&(n=n.append(e.type.contentMatch.matchFragment(n).fillBefore(k.empty,!0)))),e.copy(n)}function wn(e,t,r,n,o){var i=e.node(t),s=o?e.indexAfter(t):e.index(t);if(s==i.childCount&&!r.compatibleContent(i.type))return null;var a=n.fillBefore(i.content,!0,s);return a&&!Zc(r,i.content,s)?a:null}function Zc(e,t,r){for(var n=r;n0;a--,c--){var l=n.node(a).type.spec;if(l.defining||l.isolating)break;i.indexOf(a)>-1?s=a:n.before(a)==c&&i.splice(1,0,-a)}for(var u=i.indexOf(s),f=[],p=r.openStart,d=r.content,h=0;;h++){var v=d.firstChild;if(f.push(v),h==r.openStart)break;d=v.content}p>0&&f[p-1].type.spec.defining&&n.node(u).type!=f[p-1].type?p-=1:p>=2&&f[p-1].isTextblock&&f[p-2].type.spec.defining&&n.node(u).type!=f[p-2].type&&(p-=2);for(var g=r.openStart;g>=0;g--){var M=(g+p+1)%(r.openStart+1),y=f[M];if(!!y)for(var R=0;R=0&&(this.replace(e,t,r),!(this.steps.length>J));U--){var T=i[U];T<0||(e=n.before(T),t=o.after(T))}return this};function _i(e,t,r,n,o){if(tn){var s=o.contentMatchAt(0),a=s.fillBefore(e).append(e);e=a.append(s.matchFragment(a).fillBefore(k.empty,!0))}return e}X.prototype.replaceRangeWith=function(e,t,r){if(!r.isInline&&e==t&&this.doc.resolve(e).parent.content.size){var n=Xc(this.doc,e,r.type);n!=null&&(e=t=n)}return this.replaceRange(e,t,new C(k.from(r),0,0))};X.prototype.deleteRange=function(e,t){for(var r=this.doc.resolve(e),n=this.doc.resolve(t),o=Ni(r,n),i=0;i0&&(a||r.node(s-1).canReplace(r.index(s-1),n.indexAfter(s-1))))return this.delete(r.before(s),n.after(s))}for(var c=1;c<=r.depth&&c<=n.depth;c++)if(e-r.start(c)==r.depth-c&&t>r.end(c)&&n.end(c)-t!=n.depth-c)return this.delete(r.before(c),t);return this.delete(e,t)};function Ni(e,t){for(var r=[],n=Math.min(e.depth,t.depth),o=n;o>=0;o--){var i=e.start(o);if(it.pos+(t.depth-o)||e.node(o).type.spec.isolating||t.node(o).type.spec.isolating)break;i==t.start(o)&&r.push(o)}return r}var Tn=Object.create(null),D=function(t,r,n){this.ranges=n||[new tl(t.min(r),t.max(r))],this.$anchor=t,this.$head=r},se={anchor:{configurable:!0},head:{configurable:!0},from:{configurable:!0},to:{configurable:!0},$from:{configurable:!0},$to:{configurable:!0},empty:{configurable:!0}};se.anchor.get=function(){return this.$anchor.pos};se.head.get=function(){return this.$head.pos};se.from.get=function(){return this.$from.pos};se.to.get=function(){return this.$to.pos};se.$from.get=function(){return this.ranges[0].$from};se.$to.get=function(){return this.ranges[0].$to};se.empty.get=function(){for(var e=this.ranges,t=0;t=0;i--){var s=r<0?Be(t.node(0),t.node(i),t.before(i+1),t.index(i),r,n):Be(t.node(0),t.node(i),t.after(i+1),t.index(i)+1,r,n);if(s)return s}};D.near=function(t,r){return r===void 0&&(r=1),this.findFrom(t,r)||this.findFrom(t,-r)||new re(t.node(0))};D.atStart=function(t){return Be(t,t,0,0,1)||new re(t)};D.atEnd=function(t){return Be(t,t,t.content.size,t.childCount,-1)||new re(t)};D.fromJSON=function(t,r){if(!r||!r.type)throw new RangeError("Invalid input for Selection.fromJSON");var n=Tn[r.type];if(!n)throw new RangeError("No selection type "+r.type+" defined");return n.fromJSON(t,r)};D.jsonID=function(t,r){if(t in Tn)throw new RangeError("Duplicate use of selection JSON ID "+t);return Tn[t]=r,r.prototype.jsonID=t,r};D.prototype.getBookmark=function(){return H.between(this.$anchor,this.$head).getBookmark()};Object.defineProperties(D.prototype,se);D.prototype.visible=!0;var tl=function(t,r){this.$from=t,this.$to=r},H=function(e){function t(n,o){o===void 0&&(o=n),e.call(this,n,o)}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var r={$cursor:{configurable:!0}};return r.$cursor.get=function(){return this.$anchor.pos==this.$head.pos?this.$head:null},t.prototype.map=function(o,i){var s=o.resolve(i.map(this.head));if(!s.parent.inlineContent)return e.near(s);var a=o.resolve(i.map(this.anchor));return new t(a.parent.inlineContent?a:s,s)},t.prototype.replace=function(o,i){if(i===void 0&&(i=C.empty),e.prototype.replace.call(this,o,i),i==C.empty){var s=this.$from.marksAcross(this.$to);s&&o.ensureMarks(s)}},t.prototype.eq=function(o){return o instanceof t&&o.anchor==this.anchor&&o.head==this.head},t.prototype.getBookmark=function(){return new er(this.anchor,this.head)},t.prototype.toJSON=function(){return{type:"text",anchor:this.anchor,head:this.head}},t.fromJSON=function(o,i){if(typeof i.anchor!="number"||typeof i.head!="number")throw new RangeError("Invalid input for TextSelection.fromJSON");return new t(o.resolve(i.anchor),o.resolve(i.head))},t.create=function(o,i,s){s===void 0&&(s=i);var a=o.resolve(i);return new this(a,s==i?a:o.resolve(s))},t.between=function(o,i,s){var a=o.pos-i.pos;if((!s||a)&&(s=a>=0?1:-1),!i.parent.inlineContent){var c=e.findFrom(i,s,!0)||e.findFrom(i,-s,!0);if(c)i=c.$head;else return e.near(i,s)}return o.parent.inlineContent||(a==0?o=i:(o=(e.findFrom(o,-s,!0)||e.findFrom(o,s,!0)).$anchor,o.pos0?0:1);o>0?s=0;s+=o){var a=t.child(s);if(a.isAtom){if(!i&&E.isSelectable(a))return E.create(e,r-(o<0?a.nodeSize:0))}else{var c=Be(e,a,r+o,o<0?a.childCount:0,o,i);if(c)return c}r+=a.nodeSize*o}}function Ei(e,t,r){var n=e.steps.length-1;if(!(n0},t.prototype.setStoredMarks=function(o){return this.storedMarks=o,this.updated|=Ir,this},t.prototype.ensureMarks=function(o){return P.sameSet(this.storedMarks||this.selection.$from.marks(),o)||this.setStoredMarks(o),this},t.prototype.addStoredMark=function(o){return this.ensureMarks(o.addToSet(this.storedMarks||this.selection.$head.marks()))},t.prototype.removeStoredMark=function(o){return this.ensureMarks(o.removeFromSet(this.storedMarks||this.selection.$head.marks()))},r.storedMarksSet.get=function(){return(this.updated&Ir)>0},t.prototype.addStep=function(o,i){e.prototype.addStep.call(this,o,i),this.updated=this.updated&~Ir,this.storedMarks=null},t.prototype.setTime=function(o){return this.time=o,this},t.prototype.replaceSelection=function(o){return this.selection.replace(this,o),this},t.prototype.replaceSelectionWith=function(o,i){var s=this.selection;return i!==!1&&(o=o.mark(this.storedMarks||(s.empty?s.$from.marks():s.$from.marksAcross(s.$to)||P.none))),s.replaceWith(this,o),this},t.prototype.deleteSelection=function(){return this.selection.replace(this),this},t.prototype.insertText=function(o,i,s){s===void 0&&(s=i);var a=this.doc.type.schema;if(i==null)return o?this.replaceSelectionWith(a.text(o),!0):this.deleteSelection();if(!o)return this.deleteRange(i,s);var c=this.storedMarks;if(!c){var l=this.doc.resolve(i);c=s==i?l.marks():l.marksAcross(this.doc.resolve(s))}return this.replaceRangeWith(i,s,a.text(o,c)),this.selection.empty||this.setSelection(D.near(this.selection.$to)),this},t.prototype.setMeta=function(o,i){return this.meta[typeof o=="string"?o:o.key]=i,this},t.prototype.getMeta=function(o){return this.meta[typeof o=="string"?o:o.key]},r.isGeneric.get=function(){for(var n in this.meta)return!1;return!0},t.prototype.scrollIntoView=function(){return this.updated|=Ii,this},r.scrolledIntoView.get=function(){return(this.updated&Ii)>0},Object.defineProperties(t.prototype,r),t}(X);function Ri(e,t){return!t||!e?e:e.bind(t)}var rr=function(t,r,n){this.name=t,this.init=Ri(r.init,n),this.apply=Ri(r.apply,n)},nl=[new rr("doc",{init:function(t){return t.doc||t.schema.topNodeType.createAndFill()},apply:function(t){return t.doc}}),new rr("selection",{init:function(t,r){return t.selection||D.atStart(r.doc)},apply:function(t){return t.selection}}),new rr("storedMarks",{init:function(t){return t.storedMarks||null},apply:function(t,r,n,o){return o.selection.$cursor?t.storedMarks:null}}),new rr("scrollToSelection",{init:function(){return 0},apply:function(t,r){return t.scrolledIntoView?r+1:r}})],An=function(t,r){var n=this;this.schema=t,this.fields=nl.concat(),this.plugins=[],this.pluginsByKey=Object.create(null),r&&r.forEach(function(o){if(n.pluginsByKey[o.key])throw new RangeError("Adding different instances of a keyed plugin ("+o.key+")");n.plugins.push(o),n.pluginsByKey[o.key]=o,o.spec.state&&n.fields.push(new rr(o.key,o.spec.state,o))})},mt=function(t){this.config=t},Rr={schema:{configurable:!0},plugins:{configurable:!0},tr:{configurable:!0}};Rr.schema.get=function(){return this.config.schema};Rr.plugins.get=function(){return this.config.plugins};mt.prototype.apply=function(t){return this.applyTransaction(t).state};mt.prototype.filterTransaction=function(t,r){r===void 0&&(r=-1);for(var n=0;n-1&&nr.splice(r,1)};Object.defineProperties(mt.prototype,Rr);var nr=[];function Pi(e,t,r){for(var n in e){var o=e[n];o instanceof Function?o=o.bind(t):n=="handleDOMEvents"&&(o=Pi(o,t,{})),r[n]=o}return r}var Rt=function(t){this.props={},t.props&&Pi(t.props,this,this.props),this.spec=t,this.key=t.key?t.key.key:Bi("plugin")};Rt.prototype.getState=function(t){return t[this.key]};var _n=Object.create(null);function Bi(e){return e in _n?e+"$"+ ++_n[e]:(_n[e]=0,e+"$")}var Wt=function(t){t===void 0&&(t="key"),this.key=Bi(t)};Wt.prototype.get=function(t){return t.config.pluginsByKey[this.key]};Wt.prototype.getState=function(t){return t[this.key]};var x={};if(typeof navigator!="undefined"&&typeof document!="undefined"){var Nn=/Edge\/(\d+)/.exec(navigator.userAgent),zi=/MSIE \d/.test(navigator.userAgent),En=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);x.mac=/Mac/.test(navigator.platform);var Dn=x.ie=!!(zi||En||Nn);x.ie_version=zi?document.documentMode||6:En?+En[1]:Nn?+Nn[1]:null,x.gecko=!Dn&&/gecko\/(\d+)/i.test(navigator.userAgent),x.gecko_version=x.gecko&&+(/Firefox\/(\d+)/.exec(navigator.userAgent)||[0,0])[1];var In=!Dn&&/Chrome\/(\d+)/.exec(navigator.userAgent);x.chrome=!!In,x.chrome_version=In&&+In[1],x.safari=!Dn&&/Apple Computer/.test(navigator.vendor),x.ios=x.safari&&(/Mobile\/\w+/.test(navigator.userAgent)||navigator.maxTouchPoints>2),x.android=/Android \d/.test(navigator.userAgent),x.webkit="webkitFontSmoothing"in document.documentElement.style,x.webkit_version=x.webkit&&+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]}var Pt=function(e){for(var t=0;;t++)if(e=e.previousSibling,!e)return t},Rn=function(e){var t=e.assignedSlot||e.parentNode;return t&&t.nodeType==11?t.host:t},Li=null,ne=function(e,t,r){var n=Li||(Li=document.createRange());return n.setEnd(e,r==null?e.nodeValue.length:r),n.setStart(e,t||0),n},Pr=function(e,t,r,n){return r&&(Fi(e,t,r,n,-1)||Fi(e,t,r,n,1))},ol=/^(img|br|input|textarea|hr)$/i;function Fi(e,t,r,n,o){for(;;){if(e==r&&t==n)return!0;if(t==(o<0?0:Kt(e))){var i=e.parentNode;if(i.nodeType!=1||sl(e)||ol.test(e.nodeName)||e.contentEditable=="false")return!1;t=Pt(e)+(o<0?0:1),e=i}else if(e.nodeType==1){if(e=e.childNodes[t+(o<0?-1:0)],e.contentEditable=="false")return!1;t=o<0?Kt(e):0}else return!1}}function Kt(e){return e.nodeType==3?e.nodeValue.length:e.childNodes.length}function il(e,t,r){for(var n=t==0,o=t==Kt(e);n||o;){if(e==r)return!0;var i=Pt(e);if(e=e.parentNode,!e)return!1;n=n&&i==0,o=o&&i==Kt(e)}}function sl(e){for(var t,r=e;r&&!(t=r.pmViewDesc);r=r.parentNode);return t&&t.node&&t.node.isBlock&&(t.dom==e||t.contentDOM==e)}var Pn=function(e){var t=e.isCollapsed;return t&&x.chrome&&e.rangeCount&&!e.getRangeAt(0).collapsed&&(t=!1),t};function ze(e,t){var r=document.createEvent("Event");return r.initEvent("keydown",!0,!0),r.keyCode=e,r.key=r.code=t,r}function al(e){return{left:0,right:e.documentElement.clientWidth,top:0,bottom:e.documentElement.clientHeight}}function ae(e,t){return typeof e=="number"?e:e[t]}function cl(e){var t=e.getBoundingClientRect(),r=t.width/e.offsetWidth||1,n=t.height/e.offsetHeight||1;return{left:t.left,right:t.left+e.clientWidth*r,top:t.top,bottom:t.top+e.clientHeight*n}}function Vi(e,t,r){for(var n=e.someProp("scrollThreshold")||0,o=e.someProp("scrollMargin")||5,i=e.dom.ownerDocument,s=r||e.dom;s;s=Rn(s))if(s.nodeType==1){var a=s==i.body||s.nodeType!=1,c=a?al(i):cl(s),l=0,u=0;if(t.topc.bottom-ae(n,"bottom")&&(u=t.bottom-c.bottom+ae(o,"bottom")),t.leftc.right-ae(n,"right")&&(l=t.right-c.right+ae(o,"right")),l||u)if(a)i.defaultView.scrollBy(l,u);else{var f=s.scrollLeft,p=s.scrollTop;u&&(s.scrollTop+=u),l&&(s.scrollLeft+=l);var d=s.scrollLeft-f,h=s.scrollTop-p;t={left:t.left-d,top:t.top-h,right:t.right-d,bottom:t.bottom-h}}if(a)break}}function ll(e){for(var t=e.dom.getBoundingClientRect(),r=Math.max(0,t.top),n,o,i=(t.left+t.right)/2,s=r+1;s=r-20){n=a,o=c.top;break}}}return{refDOM:n,refTop:o,stack:Hi(e.dom)}}function Hi(e){for(var t=[],r=e.ownerDocument;e&&(t.push({dom:e,top:e.scrollTop,left:e.scrollLeft}),e!=r);e=Rn(e));return t}function ul(e){var t=e.refDOM,r=e.refTop,n=e.stack,o=t?t.getBoundingClientRect().top:0;ji(n,o==0?0:o-r)}function ji(e,t){for(var r=0;r=a){s=Math.max(p.bottom,s),a=Math.min(p.top,a);var d=p.left>t.left?p.left-t.left:p.right=(p.left+p.right)/2?1:0));continue}}!r&&(t.left>=p.right&&t.top>=p.top||t.left>=p.left&&t.top>=p.bottom)&&(i=l+1)}}return r&&r.nodeType==3?pl(r,o):!r||n&&r.nodeType==1?{node:e,offset:i}:qi(r,o)}function pl(e,t){for(var r=e.nodeValue.length,n=document.createRange(),o=0;o=(i.left+i.right)/2?1:0)}}return{node:e,offset:0}}function Bn(e,t){return e.left>=t.left-1&&e.left<=t.right+1&&e.top>=t.top-1&&e.top<=t.bottom+1}function dl(e,t){var r=e.parentNode;return r&&/^li$/i.test(r.nodeName)&&t.left(a.left+a.right)/2?1:-1}return e.docView.posFromDOM(o,i,s)}function ml(e,t,r,n){for(var o=-1,i=t;i!=e.dom;){var s=e.docView.nearestDesc(i,!0);if(!s)return null;if(s.node.isBlock&&s.parent){var a=s.dom.getBoundingClientRect();if(a.left>n.left||a.top>n.top)o=s.posBefore;else if(a.right-1?o:e.docView.posFromDOM(t,r)}function Ji(e,t,r){var n=e.childNodes.length;if(n&&r.topt.top&&s++}i==e.dom&&s==i.childNodes.length-1&&i.lastChild.nodeType==1&&t.top>i.lastChild.getBoundingClientRect().bottom?u=e.state.doc.content.size:(s==0||i.nodeType!=1||i.childNodes[s-1].nodeName!="BR")&&(u=ml(e,i,s,t))}u==null&&(u=hl(e,l,t));var v=e.docView.nearestDesc(l,!0);return{pos:u,inside:v?v.posAtStart-v.border:-1}}function ce(e,t){var r=e.getClientRects();return r.length?r[t<0?0:r.length-1]:e.getBoundingClientRect()}var gl=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;function Wi(e,t,r){var n=e.docView.domFromPos(t,r<0?-1:1),o=n.node,i=n.offset,s=x.webkit||x.gecko;if(o.nodeType==3)if(s&&(gl.test(o.nodeValue)||(r<0?!i:i==o.nodeValue.length))){var a=ce(ne(o,i,i),r);if(x.gecko&&i&&/\s/.test(o.nodeValue[i-1])&&i=0&&i==o.nodeValue.length?(u--,p=1):r<0?u--:f++,or(ce(ne(o,u,f),p),p<0)}if(!e.state.doc.resolve(t).parent.inlineContent){if(i&&(r<0||i==Kt(o))){var d=o.childNodes[i-1];if(d.nodeType==1)return zn(d.getBoundingClientRect(),!1)}if(i=0)}if(i&&(r<0||i==Kt(o))){var v=o.childNodes[i-1],g=v.nodeType==3?ne(v,Kt(v)-(s?0:1)):v.nodeType==1&&(v.nodeName!="BR"||!v.nextSibling)?v:null;if(g)return or(ce(g,1),!1)}if(i=0)}function or(e,t){if(e.width==0)return e;var r=t?e.left:e.right;return{top:e.top,bottom:e.bottom,left:r,right:r}}function zn(e,t){if(e.height==0)return e;var r=t?e.top:e.bottom;return{top:r,bottom:r,left:e.left,right:e.right}}function Ki(e,t,r){var n=e.state,o=e.root.activeElement;n!=t&&e.updateState(t),o!=e.dom&&e.focus();try{return r()}finally{n!=t&&e.updateState(n),o!=e.dom&&o&&o.focus()}}function yl(e,t,r){var n=t.selection,o=r=="up"?n.$from:n.$to;return Ki(e,t,function(){for(var i=e.docView.domFromPos(o.pos,r=="up"?-1:1),s=i.node;;){var a=e.docView.nearestDesc(s,!0);if(!a)break;if(a.node.isBlock){s=a.dom;break}s=a.dom.parentNode}for(var c=Wi(e,o.pos,1),l=s.firstChild;l;l=l.nextSibling){var u=void 0;if(l.nodeType==1)u=l.getClientRects();else if(l.nodeType==3)u=ne(l,0,l.nodeValue.length).getClientRects();else continue;for(var f=0;fp.top&&(r=="up"?p.bottomc.bottom-1))return!1}}return!0})}var bl=/[\u0590-\u08ac]/;function kl(e,t,r){var n=t.selection,o=n.$head;if(!o.parent.isTextblock)return!1;var i=o.parentOffset,s=!i,a=i==o.parent.content.size,c=e.root.getSelection();return!bl.test(o.parent.textContent)||!c.modify?r=="left"||r=="backward"?s:a:Ki(e,t,function(){var l=c.getRangeAt(0),u=c.focusNode,f=c.focusOffset,p=c.caretBidiLevel;c.modify("move",r,"character");var d=o.depth?e.docView.domAfterPos(o.before()):e.dom,h=!d.contains(c.focusNode.nodeType==1?c.focusNode:c.focusNode.parentNode)||u==c.focusNode&&f==c.focusOffset;return c.removeAllRanges(),c.addRange(l),p!=null&&(c.caretBidiLevel=p),h})}var $i=null,Ui=null,Gi=!1;function Sl(e,t,r){return $i==t&&Ui==r?Gi:($i=t,Ui=r,Gi=r=="up"||r=="down"?yl(e,t,r):kl(e,t,r))}var Ft=0,Yi=1,ir=2,le=3,W=function(t,r,n,o){this.parent=t,this.children=r,this.dom=n,n.pmViewDesc=this,this.contentDOM=o,this.dirty=Ft},$t={beforePosition:{configurable:!0},size:{configurable:!0},border:{configurable:!0},posBefore:{configurable:!0},posAtStart:{configurable:!0},posAfter:{configurable:!0},posAtEnd:{configurable:!0},contentLost:{configurable:!0},domAtom:{configurable:!0}};W.prototype.matchesWidget=function(){return!1};W.prototype.matchesMark=function(){return!1};W.prototype.matchesNode=function(){return!1};W.prototype.matchesHack=function(t){return!1};$t.beforePosition.get=function(){return!1};W.prototype.parseRule=function(){return null};W.prototype.stopEvent=function(){return!1};$t.size.get=function(){for(var e=0,t=0;tPt(this.contentDOM);else if(this.contentDOM&&this.contentDOM!=this.dom&&this.dom.contains(this.contentDOM))c=t.compareDocumentPosition(this.contentDOM)&2;else if(this.dom.firstChild){if(r==0)for(var l=t;;l=l.parentNode){if(l==this.dom){c=!1;break}if(l.parentNode.firstChild!=l)break}if(c==null&&r==t.childNodes.length)for(var u=t;;u=u.parentNode){if(u==this.dom){c=!0;break}if(u.parentNode.lastChild!=u)break}}return(c==null?n>0:c)?this.posAtEnd:this.posAtStart};W.prototype.nearestDesc=function(t,r){for(var n=!0,o=t;o;o=o.parentNode){var i=this.getDesc(o);if(i&&(!r||i.node))if(n&&i.nodeDOM&&!(i.nodeDOM.nodeType==1?i.nodeDOM.contains(t.nodeType==1?t:t.parentNode):i.nodeDOM==t))n=!1;else return i}};W.prototype.getDesc=function(t){for(var r=t.pmViewDesc,n=r;n;n=n.parent)if(n==this)return r};W.prototype.posFromDOM=function(t,r,n){for(var o=t;o;o=o.parentNode){var i=this.getDesc(o);if(i)return i.localPosFromDOM(t,r,n)}return-1};W.prototype.descAt=function(t){for(var r=0,n=0;r=t:a>t)&&(a>t||o+1>=this.children.length||!this.children[o+1].beforePosition))return s.domFromPos(t-n-s.border,r);n=a}};W.prototype.parseRange=function(t,r,n){if(n===void 0&&(n=0),this.children.length==0)return{node:this.contentDOM,from:t,to:r,fromOffset:0,toOffset:this.contentDOM.childNodes.length};for(var o=-1,i=-1,s=n,a=0;;a++){var c=this.children[a],l=s+c.size;if(o==-1&&t<=l){var u=s+c.border;if(t>=u&&r<=l-c.border&&c.node&&c.contentDOM&&this.contentDOM.contains(c.contentDOM))return c.parseRange(t,r,u);t=s;for(var f=a;f>0;f--){var p=this.children[f-1];if(p.size&&p.dom.parentNode==this.contentDOM&&!p.emptyChildAt(1)){o=Pt(p.dom)+1;break}t-=p.size}o==-1&&(o=0)}if(o>-1&&(l>r||a==this.children.length-1)){r=l;for(var d=a+1;dc&&sr){var F=f;f=p,p=F}var J=document.createRange();J.setEnd(p.node,p.offset),J.setStart(f.node,f.offset),d.removeAllRanges(),d.addRange(J)}}};W.prototype.ignoreMutation=function(t){return!this.contentDOM&&t.type!="selection"};$t.contentLost.get=function(){return this.contentDOM&&this.contentDOM!=this.dom&&!this.dom.contains(this.contentDOM)};W.prototype.markDirty=function(t,r){for(var n=0,o=0;o=n:tn){var a=n+i.border,c=s-i.border;if(t>=a&&r<=c){this.dirty=t==n||r==s?ir:Yi,t==a&&r==c&&(i.contentLost||i.dom.parentNode!=this.contentDOM)?i.dirty=le:i.markDirty(t-a,r-a);return}else i.dirty=i.dom==i.contentDOM&&i.dom.parentNode==this.contentDOM?ir:le}n=s}this.dirty=ir};W.prototype.markParentsDirty=function(){for(var t=1,r=this.parent;r;r=r.parent,t++){var n=t==1?ir:Yi;r.dirty0&&(a=Hn(a,0,n,i));for(var l=0;l-1?l:null,f=l&&l.pos<0,p=new Bt(this,u&&u.node);_l(this.node,this.innerDeco,function(d,h,v){d.spec.marks?p.syncToMarks(d.spec.marks,a,o):d.type.side>=0&&!v&&p.syncToMarks(h==s.node.childCount?P.none:s.node.child(h).marks,a,o),p.placeWidget(d,o,c)},function(d,h,v,g){p.syncToMarks(d.marks,a,o);var M;p.findNodeMatch(d,h,v,g)||f&&o.state.selection.from>c&&o.state.selection.to-1&&p.updateNodeAt(d,h,v,M,o)||p.updateNextNode(d,h,v,o,g)||p.addNode(d,h,v,o,c),c+=d.nodeSize}),p.syncToMarks(ue,a,o),this.node.isTextblock&&p.addTextblockHacks(),p.destroyRest(),(p.changed||this.dirty==ir)&&(u&&this.protectLocalComposition(o,u),Zi(this.contentDOM,this.children,o),x.ios&&Nl(this.dom))},t.prototype.localCompositionInfo=function(o,i){var s=o.state.selection,a=s.from,c=s.to;if(!(!(o.state.selection instanceof H)||ai+this.node.content.size)){var l=o.root.getSelection(),u=El(l.focusNode,l.focusOffset);if(!(!u||!this.dom.contains(u.parentNode)))if(this.node.inlineContent){var f=u.nodeValue,p=Dl(this.node.content,f,a-i,c-i);return p<0?null:{node:u,pos:p,text:f}}else return{node:u,pos:-1}}},t.prototype.protectLocalComposition=function(o,i){var s=i.node,a=i.pos,c=i.text;if(!this.getDesc(s)){for(var l=s;l.parentNode!=this.contentDOM;l=l.parentNode){for(;l.previousSibling;)l.parentNode.removeChild(l.previousSibling);for(;l.nextSibling;)l.parentNode.removeChild(l.nextSibling);l.pmViewDesc&&(l.pmViewDesc=null)}var u=new xl(this,l,s,c);o.compositionNodes.push(u),this.children=Hn(this.children,a,a+c.length,o,u)}},t.prototype.update=function(o,i,s,a){return this.dirty==le||!o.sameMarkup(this.node)?!1:(this.updateInner(o,i,s,a),!0)},t.prototype.updateInner=function(o,i,s,a){this.updateOuterDeco(i),this.node=o,this.innerDeco=s,this.contentDOM&&this.updateChildren(a,this.posAtStart),this.dirty=Ft},t.prototype.updateOuterDeco=function(o){if(!Vn(o,this.outerDeco)){var i=this.nodeDOM.nodeType!=1,s=this.dom;this.dom=ts(this.dom,this.nodeDOM,Fn(this.outerDeco,this.node,i),Fn(o,this.node,i)),this.dom!=s&&(s.pmViewDesc=null,this.dom.pmViewDesc=this),this.outerDeco=o}},t.prototype.selectNode=function(){this.nodeDOM.classList.add("ProseMirror-selectednode"),(this.contentDOM||!this.node.type.spec.draggable)&&(this.dom.draggable=!0)},t.prototype.deselectNode=function(){this.nodeDOM.classList.remove("ProseMirror-selectednode"),(this.contentDOM||!this.node.type.spec.draggable)&&this.dom.removeAttribute("draggable")},r.domAtom.get=function(){return this.node.isAtom},Object.defineProperties(t.prototype,r),t}(W);function Xi(e,t,r,n,o){return es(n,t,e),new sr(null,e,t,r,n,n,n,o,0)}var Qi=function(e){function t(n,o,i,s,a,c,l){e.call(this,n,o,i,s,a,null,c,l)}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var r={domAtom:{configurable:!0}};return t.prototype.parseRule=function(){for(var o=this.nodeDOM.parentNode;o&&o!=this.dom&&!o.pmIsDeco;)o=o.parentNode;return{skip:o||!0}},t.prototype.update=function(o,i,s,a){return this.dirty==le||this.dirty!=Ft&&!this.inParent()||!o.sameMarkup(this.node)?!1:(this.updateOuterDeco(i),(this.dirty!=Ft||o.text!=this.node.text)&&o.text!=this.nodeDOM.nodeValue&&(this.nodeDOM.nodeValue=o.text,a.trackWrites==this.nodeDOM&&(a.trackWrites=null)),this.node=o,this.dirty=Ft,!0)},t.prototype.inParent=function(){for(var o=this.parent.contentDOM,i=this.nodeDOM;i;i=i.parentNode)if(i==o)return!0;return!1},t.prototype.domFromPos=function(o){return{node:this.nodeDOM,offset:o}},t.prototype.localPosFromDOM=function(o,i,s){return o==this.nodeDOM?this.posAtStart+Math.min(i,this.node.text.length):e.prototype.localPosFromDOM.call(this,o,i,s)},t.prototype.ignoreMutation=function(o){return o.type!="characterData"&&o.type!="selection"},t.prototype.slice=function(o,i,s){var a=this.node.cut(o,i),c=document.createTextNode(a.text);return new t(this.parent,a,this.outerDeco,this.innerDeco,c,c,s)},t.prototype.markDirty=function(o,i){e.prototype.markDirty.call(this,o,i),this.dom!=this.nodeDOM&&(o==0||i==this.nodeDOM.nodeValue.length)&&(this.dirty=le)},r.domAtom.get=function(){return!1},Object.defineProperties(t.prototype,r),t}(sr),Cl=function(e){function t(){e.apply(this,arguments)}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var r={domAtom:{configurable:!0}};return t.prototype.parseRule=function(){return{ignore:!0}},t.prototype.matchesHack=function(o){return this.dirty==Ft&&this.dom.nodeName==o},r.domAtom.get=function(){return!0},Object.defineProperties(t.prototype,r),t}(W),Ol=function(e){function t(r,n,o,i,s,a,c,l,u,f){e.call(this,r,n,o,i,s,a,c,u,f),this.spec=l}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.update=function(n,o,i,s){if(this.dirty==le)return!1;if(this.spec.update){var a=this.spec.update(n,o,i);return a&&this.updateInner(n,o,i,s),a}else return!this.contentDOM&&!n.isLeaf?!1:e.prototype.update.call(this,n,o,i,s)},t.prototype.selectNode=function(){this.spec.selectNode?this.spec.selectNode():e.prototype.selectNode.call(this)},t.prototype.deselectNode=function(){this.spec.deselectNode?this.spec.deselectNode():e.prototype.deselectNode.call(this)},t.prototype.setSelection=function(n,o,i,s){this.spec.setSelection?this.spec.setSelection(n,o,i):e.prototype.setSelection.call(this,n,o,i,s)},t.prototype.destroy=function(){this.spec.destroy&&this.spec.destroy(),e.prototype.destroy.call(this)},t.prototype.stopEvent=function(n){return this.spec.stopEvent?this.spec.stopEvent(n):!1},t.prototype.ignoreMutation=function(n){return this.spec.ignoreMutation?this.spec.ignoreMutation(n):e.prototype.ignoreMutation.call(this,n)},t}(sr);function Zi(e,t,r){for(var n=e.firstChild,o=!1,i=0;i>1,s=Math.min(i,t.length);o-1)a>this.index&&(this.changed=!0,this.destroyBetween(this.index,a)),this.top=this.top.children[this.index];else{var l=Ln.create(this.top,t[i],r,n);this.top.children.splice(this.index,0,l),this.top=l,this.changed=!0}this.index=0,i++}};Bt.prototype.findNodeMatch=function(t,r,n,o){var i=this.top.children,s=-1;if(o>=this.preMatch.index){for(var a=this.index;a0&&n>0;n--){var i=t[n-1],s=i.node;if(!!s){if(s!=e.child(r-1))break;--r,o.set(i,r)}}return{index:r,matched:o}}function Al(e,t){return e.type.side-t.type.side}function _l(e,t,r,n){var o=t.locals(e),i=0;if(o.length==0){for(var s=0;si;)l.push(o[c++]);var y=i+v.nodeSize;if(v.isText){var R=y;c0){if(e.childNodes.length>t&&e.childNodes[t].nodeType==3)return e.childNodes[t];e=e.childNodes[t-1],t=Kt(e)}else if(e.nodeType==1&&t=r){var u=c.lastIndexOf(t,n-a);if(u>=0&&u+t.length+a>=r)return a+u}}}return-1}function Hn(e,t,r,n,o){for(var i=[],s=0,a=0;s=r||u<=t?i.push(c):(lr&&i.push(c.slice(r-l,c.size,n)))}return i}function ns(e,t){var r=e.root.getSelection(),n=e.state.doc;if(!r.focusNode)return null;var o=e.docView.nearestDesc(r.focusNode),i=o&&o.size==0,s=e.docView.posFromDOM(r.focusNode,r.focusOffset);if(s<0)return null;var a=n.resolve(s),c,l;if(Pn(r)){for(c=a;o&&!o.node;)o=o.parent;if(o&&o.node.isAtom&&E.isSelectable(o.node)&&o.parent&&!(o.node.isInline&&il(r.focusNode,r.focusOffset,o.dom))){var u=o.posBefore;l=new E(s==u?a:n.resolve(u))}}else{var f=e.docView.posFromDOM(r.anchorNode,r.anchorOffset);if(f<0)return null;c=n.resolve(f)}if(!l){var p=t=="pointer"||e.state.selection.head0?n.max(o):n.min(o),s=i.parent.inlineContent?i.depth?e.doc.resolve(t>0?i.after():i.before()):null:i;return s&&D.findFrom(s,t)}function xe(e,t){return e.dispatch(e.state.tr.setSelection(t).scrollIntoView()),!0}function fs(e,t,r){var n=e.state.selection;if(n instanceof H){if(!n.empty||r.indexOf("s")>-1)return!1;if(e.endOfTextblock(t>0?"right":"left")){var o=Wn(e.state,t);return o&&o instanceof E?xe(e,o):!1}else if(!(x.mac&&r.indexOf("m")>-1)){var i=n.$head,s=i.textOffset?null:t<0?i.nodeBefore:i.nodeAfter,a;if(!s||s.isText)return!1;var c=t<0?i.pos-s.nodeSize:i.pos;return s.isAtom||(a=e.docView.descAt(c))&&!a.contentDOM?E.isSelectable(s)?xe(e,new E(t<0?e.state.doc.resolve(i.pos-s.nodeSize):i)):x.webkit?xe(e,new H(e.state.doc.resolve(t<0?c:c+s.nodeSize))):!1:!1}}else{if(n instanceof E&&n.node.isInline)return xe(e,new H(t>0?n.$to:n.$from));var l=Wn(e.state,t);return l?xe(e,l):!1}}function Br(e){return e.nodeType==3?e.nodeValue.length:e.childNodes.length}function cr(e){var t=e.pmViewDesc;return t&&t.size==0&&(e.nextSibling||e.nodeName!="BR")}function Kn(e){var t=e.root.getSelection(),r=t.focusNode,n=t.focusOffset;if(!!r){var o,i,s=!1;for(x.gecko&&r.nodeType==1&&n0){if(r.nodeType!=1)break;var a=r.childNodes[n-1];if(cr(a))o=r,i=--n;else if(a.nodeType==3)r=a,n=r.nodeValue.length;else break}else{if(ps(r))break;for(var c=r.previousSibling;c&&cr(c);)o=r.parentNode,i=Pt(c),c=c.previousSibling;if(c)r=c,n=Br(r);else{if(r=r.parentNode,r==e.dom)break;n=0}}s?Un(e,t,r,n):o&&Un(e,t,o,i)}}function $n(e){var t=e.root.getSelection(),r=t.focusNode,n=t.focusOffset;if(!!r){for(var o=Br(r),i,s;;)if(n-1||x.mac&&r.indexOf("m")>-1)return!1;var o=n.$from,i=n.$to;if(!o.parent.inlineContent||e.endOfTextblock(t<0?"up":"down")){var s=Wn(e.state,t);if(s&&s instanceof E)return xe(e,s)}if(!o.parent.inlineContent){var a=t<0?o:i,c=n instanceof re?D.near(a,t):D.findFrom(a,t);return c?xe(e,c):!1}return!1}function hs(e,t){if(!(e.state.selection instanceof H))return!0;var r=e.state.selection,n=r.$head,o=r.$anchor,i=r.empty;if(!n.sameParent(o))return!0;if(!i)return!1;if(e.endOfTextblock(t>0?"forward":"backward"))return!0;var s=!n.textOffset&&(t<0?n.nodeBefore:n.nodeAfter);if(s&&!s.isText){var a=e.state.tr;return t<0?a.delete(n.pos-s.nodeSize,n.pos):a.delete(n.pos,n.pos+s.nodeSize),e.dispatch(a),!0}return!1}function ms(e,t,r){e.domObserver.stop(),t.contentEditable=r,e.domObserver.start()}function zl(e){if(!(!x.safari||e.state.selection.$head.parentOffset>0)){var t=e.root.getSelection(),r=t.focusNode,n=t.focusOffset;if(r&&r.nodeType==1&&n==0&&r.firstChild&&r.firstChild.contentEditable=="false"){var o=r.firstChild;ms(e,o,!0),setTimeout(function(){return ms(e,o,!1)},20)}}}function Ll(e){var t="";return e.ctrlKey&&(t+="c"),e.metaKey&&(t+="m"),e.altKey&&(t+="a"),e.shiftKey&&(t+="s"),t}function Fl(e,t){var r=t.keyCode,n=Ll(t);return r==8||x.mac&&r==72&&n=="c"?hs(e,-1)||Kn(e):r==46||x.mac&&r==68&&n=="c"?hs(e,1)||$n(e):r==13||r==27?!0:r==37?fs(e,-1,n)||Kn(e):r==39?fs(e,1,n)||$n(e):r==38?ds(e,-1,n)||Kn(e):r==40?zl(e)||ds(e,1,n)||$n(e):n==(x.mac?"m":"c")&&(r==66||r==73||r==89||r==90)}function Vl(e,t,r){var n=e.docView.parseRange(t,r),o=n.node,i=n.fromOffset,s=n.toOffset,a=n.from,c=n.to,l=e.root.getSelection(),u=null,f=l.anchorNode;if(f&&e.dom.contains(f.nodeType==1?f:f.parentNode)&&(u=[{node:f,offset:l.anchorOffset}],Pn(l)||u.push({node:l.focusNode,offset:l.focusOffset})),x.chrome&&e.lastKeyCode===8)for(var p=s;p>i;p--){var d=o.childNodes[p-1],h=d.pmViewDesc;if(d.nodeName=="BR"&&!h){s=p;break}if(!h||h.size)break}var v=e.state.doc,g=e.someProp("domParser")||Lt.fromSchema(e.state.schema),M=v.resolve(a),y=null,R=g.parse(o,{topNode:M.parent,topMatch:M.parent.contentMatchAt(M.index()),topOpen:!0,from:i,to:s,preserveWhitespace:M.parent.type.spec.code?"full":!0,editableContent:!0,findPositions:u,ruleFromNode:Hl,context:M});if(u&&u[0].pos!=null){var m=u[0].pos,I=u[1]&&u[1].pos;I==null&&(I=m),y={anchor:m+a,head:I+a}}return{doc:R,sel:y,from:a,to:c}}function Hl(e){var t=e.pmViewDesc;if(t)return t.parseRule();if(e.nodeName=="BR"&&e.parentNode){if(x.safari&&/^(ul|ol)$/i.test(e.parentNode.nodeName)){var r=document.createElement("div");return r.appendChild(document.createElement("li")),{skip:r}}else if(e.parentNode.lastChild==e||x.safari&&/^(tr|table)$/i.test(e.parentNode.nodeName))return{ignore:!0}}else if(e.nodeName=="IMG"&&e.getAttribute("mark-placeholder"))return{ignore:!0}}function jl(e,t,r,n,o){if(t<0){var i=e.lastSelectionTime>Date.now()-50?e.lastSelectionOrigin:null,s=ns(e,i);if(s&&!e.state.selection.eq(s)){var a=e.state.tr.setSelection(s);i=="pointer"?a.setMeta("pointer",!0):i=="key"&&a.scrollIntoView(),e.dispatch(a)}return}var c=e.state.doc.resolve(t),l=c.sharedDepth(r);t=c.before(l+1),r=e.state.doc.resolve(r).after(l+1);var u=e.state.selection,f=Vl(e,t,r);if(x.chrome&&e.cursorWrapper&&f.sel&&f.sel.anchor==e.cursorWrapper.deco.from){var p=e.cursorWrapper.deco.type.toDOM.nextSibling,d=p&&p.nodeValue?p.nodeValue.length:1;f.sel={anchor:f.sel.anchor+d,head:f.sel.anchor+d}}var h=e.state.doc,v=h.slice(f.from,f.to),g,M;e.lastKeyCode===8&&Date.now()-100Date.now()-225||x.android)&&o.some(function(Y){return Y.nodeName=="DIV"||Y.nodeName=="P"})&&e.someProp("handleKeyDown",function(Y){return Y(e,ze(13,"Enter"))})){e.lastIOSEnter=0;return}else{if(f.sel){var R=vs(e,e.state.doc,f.sel);R&&!R.eq(e.state.selection)&&e.dispatch(e.state.tr.setSelection(R))}return}e.domChangeCount++,e.state.selection.frome.state.selection.from&&y.start<=e.state.selection.from+2?y.start=e.state.selection.from:y.endA=e.state.selection.to-2&&(y.endB+=e.state.selection.to-y.endA,y.endA=e.state.selection.to)),x.ie&&x.ie_version<=11&&y.endB==y.start+1&&y.endA==y.start&&y.start>f.from&&f.doc.textBetween(y.start-f.from-1,y.start-f.from+1)==" \xA0"&&(y.start--,y.endA--,y.endB--);var m=f.doc.resolveNoCache(y.start-f.from),I=f.doc.resolveNoCache(y.endB-f.from),O=m.sameParent(I)&&m.parent.inlineContent,F;if((x.ios&&e.lastIOSEnter>Date.now()-225&&(!O||o.some(function(Y){return Y.nodeName=="DIV"||Y.nodeName=="P"}))||!O&&m.posy.start&&Jl(h,y.start,y.endA,m,I)&&e.someProp("handleKeyDown",function(Y){return Y(e,ze(8,"Backspace"))})){x.android&&x.chrome&&e.domObserver.suppressSelectionUpdates();return}x.chrome&&x.android&&y.toB==y.from&&(e.lastAndroidDelete=Date.now()),x.android&&!O&&m.start()!=I.start()&&I.parentOffset==0&&m.depth==I.depth&&f.sel&&f.sel.anchor==f.sel.head&&f.sel.head==y.endA&&(y.endB-=2,I=f.doc.resolveNoCache(y.endB-f.from),setTimeout(function(){e.someProp("handleKeyDown",function(Y){return Y(e,ze(13,"Enter"))})},20));var J=y.start,U=y.endA,T,Qt,rt,ut;if(O){if(m.pos==I.pos)x.ie&&x.ie_version<=11&&m.parentOffset==0&&(e.domObserver.suppressSelectionUpdates(),setTimeout(function(){return fe(e)},20)),T=e.state.tr.delete(J,U),Qt=h.resolve(y.start).marksAcross(h.resolve(y.endA));else if(y.endA==y.endB&&(ut=h.resolve(y.start))&&(rt=ql(m.parent.content.cut(m.parentOffset,I.parentOffset),ut.parent.content.cut(ut.parentOffset,y.endA-ut.start()))))T=e.state.tr,rt.type=="add"?T.addMark(J,U,rt.mark):T.removeMark(J,U,rt.mark);else if(m.parent.child(m.index()).isText&&m.index()==I.index()-(I.textOffset?0:1)){var qt=m.parent.textBetween(m.parentOffset,I.parentOffset);if(e.someProp("handleTextInput",function(Y){return Y(e,J,U,qt)}))return;T=e.state.tr.insertText(qt,J,U)}}if(T||(T=e.state.tr.replace(J,U,f.doc.slice(y.start-f.from,y.endB-f.from))),f.sel){var G=vs(e,T.doc,f.sel);G&&!(x.chrome&&x.android&&e.composing&&G.empty&&(y.start!=y.endB||e.lastAndroidDeletet.content.size?null:qn(e,t.resolve(r.anchor),t.resolve(r.head))}function ql(e,t){for(var r=e.firstChild.marks,n=t.firstChild.marks,o=r,i=n,s,a,c,l=0;lr||Gn(s,!0,!1)0&&(t||e.indexAfter(n)==e.node(n).childCount);)n--,o++,t=!1;if(r)for(var i=e.node(n).maybeChild(e.indexAfter(n));i&&!i.isLeaf;)i=i.firstChild,o++;return o}function Wl(e,t,r,n,o){var i=e.findDiffStart(t,r);if(i==null)return null;var s=e.findDiffEnd(t,r+e.size,r+t.size),a=s.a,c=s.b;if(o=="end"){var l=Math.max(0,i-Math.min(a,c));n-=a+l-i}if(a=a?i-n:0;i-=u,c=i+(c-a),a=i}else if(c=c?i-n:0;i-=f,a=i+(a-c),c=i}return{start:i,endA:a,endB:c}}function gs(e,t){for(var r=[],n=t.content,o=t.openStart,i=t.openEnd;o>1&&i>1&&n.childCount==1&&n.firstChild.childCount==1;){o--,i--;var s=n.firstChild;r.push(s.type.name,s.attrs!=s.type.defaultAttrs?s.attrs:null),n=s.content}var a=e.someProp("clipboardSerializer")||ot.fromSchema(e.state.schema),c=Cs(),l=c.createElement("div");l.appendChild(a.serializeFragment(n,{document:c}));for(var u=l.firstChild,f;u&&u.nodeType==1&&(f=Ms[u.nodeName.toLowerCase()]);){for(var p=f.length-1;p>=0;p--){for(var d=c.createElement(f[p]);l.firstChild;)d.appendChild(l.firstChild);l.appendChild(d),f[p]!="tbody"&&(o++,i++)}u=l.firstChild}u&&u.nodeType==1&&u.setAttribute("data-pm-slice",o+" "+i+" "+JSON.stringify(r));var h=e.someProp("clipboardTextSerializer",function(v){return v(t)})||t.content.textBetween(0,t.content.size,` + +`);return{dom:l,text:h}}function ys(e,t,r,n,o){var i,s=o.parent.type.spec.code,a;if(!r&&!t)return null;var c=t&&(n||s||!r);if(c){if(e.someProp("transformPastedText",function(M){t=M(t,s||n)}),s)return new C(k.from(e.state.schema.text(t.replace(/\r\n?/g,` +`))),0,0);var l=e.someProp("clipboardTextParser",function(M){return M(t,o,n)});if(l)a=l;else{var u=o.marks(),f=e.state,p=f.schema,d=ot.fromSchema(p);i=document.createElement("div"),t.trim().split(/(?:\r\n?|\n)+/).forEach(function(M){i.appendChild(document.createElement("p")).appendChild(d.serializeNode(p.text(M,u)))})}}else e.someProp("transformPastedHTML",function(M){r=M(r)}),i=Ul(r),x.webkit&&Gl(i);var h=i&&i.querySelector("[data-pm-slice]"),v=h&&/^(\d+) (\d+) (.*)/.exec(h.getAttribute("data-pm-slice"));if(!a){var g=e.someProp("clipboardParser")||e.someProp("domParser")||Lt.fromSchema(e.state.schema);a=g.parseSlice(i,{preserveWhitespace:!!(c||v),context:o})}return v?a=Yl($l(a,+v[1],+v[2]),v[3]):a=C.maxOpen(Kl(a.content,o),!1),e.someProp("transformPasted",function(M){a=M(a)}),a}function Kl(e,t){if(e.childCount<2)return e;for(var r=function(i){var s=t.node(i),a=s.contentMatchAt(t.index(i)),c=void 0,l=[];if(e.forEach(function(u){if(!!l){var f=a.findWrapping(u.type),p;if(!f)return l=null;if(p=l.length&&c.length&&ks(f,c,u,l[l.length-1],0))l[l.length-1]=p;else{l.length&&(l[l.length-1]=Ss(l[l.length-1],c.length));var d=bs(u,f);l.push(d),a=a.matchType(d.type,d.attrs),c=f}}}),l)return{v:k.from(l)}},n=t.depth;n>=0;n--){var o=r(n);if(o)return o.v}return e}function bs(e,t,r){r===void 0&&(r=0);for(var n=t.length-1;n>=r;n--)e=t[n].create(null,k.from(e));return e}function ks(e,t,r,n,o){if(o=r&&(a=t<0?s.contentMatchAt(0).fillBefore(a,e.childCount>1||i<=o).append(a):a.append(s.contentMatchAt(s.childCount).fillBefore(k.empty,!0))),e.replaceChild(t<0?0:e.childCount-1,s.copy(a))}function $l(e,t,r){return t]*>)*/.exec(e);t&&(e=e.slice(t[0].length));var r=Cs().createElement("div"),n=/<([a-z][^>\s]+)/i.exec(e),o;if((o=n&&Ms[n[1].toLowerCase()])&&(e=o.map(function(s){return"<"+s+">"}).join("")+e+o.map(function(s){return""}).reverse().join("")),r.innerHTML=e,o)for(var i=0;i=0;a-=2){var c=r.nodes[n[a]];if(!c||c.hasRequiredAttrs())break;o=k.from(c.create(n[a+1],o)),i++,s++}return new C(o,i,s)}var Xl={childList:!0,characterData:!0,characterDataOldValue:!0,attributes:!0,attributeOldValue:!0,subtree:!0},Xn=x.ie&&x.ie_version<=11,Qn=function(){this.anchorNode=this.anchorOffset=this.focusNode=this.focusOffset=null};Qn.prototype.set=function(t){this.anchorNode=t.anchorNode,this.anchorOffset=t.anchorOffset,this.focusNode=t.focusNode,this.focusOffset=t.focusOffset};Qn.prototype.eq=function(t){return t.anchorNode==this.anchorNode&&t.anchorOffset==this.anchorOffset&&t.focusNode==this.focusNode&&t.focusOffset==this.focusOffset};var Nt=function(t,r){var n=this;this.view=t,this.handleDOMChange=r,this.queue=[],this.flushingSoon=-1,this.observer=window.MutationObserver&&new window.MutationObserver(function(o){for(var i=0;is.target.nodeValue.length})?n.flushSoon():n.flush()}),this.currentSelection=new Qn,Xn&&(this.onCharData=function(o){n.queue.push({target:o.target,type:"characterData",oldValue:o.prevValue}),n.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.suppressingSelectionUpdates=!1};Nt.prototype.flushSoon=function(){var t=this;this.flushingSoon<0&&(this.flushingSoon=window.setTimeout(function(){t.flushingSoon=-1,t.flush()},20))};Nt.prototype.forceFlush=function(){this.flushingSoon>-1&&(window.clearTimeout(this.flushingSoon),this.flushingSoon=-1,this.flush())};Nt.prototype.start=function(){this.observer&&this.observer.observe(this.view.dom,Xl),Xn&&this.view.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.connectSelection()};Nt.prototype.stop=function(){var t=this;if(this.observer){var r=this.observer.takeRecords();if(r.length){for(var n=0;n-1)){var t=this.observer?this.observer.takeRecords():[];this.queue.length&&(t=this.queue.concat(t),this.queue.length=0);var r=this.view.root.getSelection(),n=!this.suppressingSelectionUpdates&&!this.currentSelection.eq(r)&&Jn(this.view)&&!this.ignoreSelectionChange(r),o=-1,i=-1,s=!1,a=[];if(this.view.editable)for(var c=0;c1){var u=a.filter(function(d){return d.nodeName=="BR"});if(u.length==2){var f=u[0],p=u[1];f.parentNode&&f.parentNode.parentNode==p.parentNode?p.remove():f.remove()}}(o>-1||n)&&(o>-1&&(this.view.docView.markDirty(o,i),Ql(this.view)),this.handleDOMChange(o,i,s,a),this.view.docView.dirty?this.view.updateState(this.view.state):this.currentSelection.eq(r)||fe(this.view),this.currentSelection.set(r))}};Nt.prototype.registerMutation=function(t,r){if(r.indexOf(t.target)>-1)return null;var n=this.view.docView.nearestDesc(t.target);if(t.type=="attributes"&&(n==this.view.docView||t.attributeName=="contenteditable"||t.attributeName=="style"&&!t.oldValue&&!t.target.getAttribute("style"))||!n||n.ignoreMutation(t))return null;if(t.type=="childList"){for(var o=0;oi.depth?u(e,r,i.nodeAfter,i.before(l),o,!0):u(e,r,i.node(l),i.before(l),o,!1)}))return{v:!0}},a=i.depth+1;a>0;a--){var c=s(a);if(c)return c.v}return!1}function Fe(e,t,r){e.focused||e.focus();var n=e.state.tr.setSelection(t);r=="pointer"&&n.setMeta("pointer",!0),e.dispatch(n)}function ou(e,t){if(t==-1)return!1;var r=e.state.doc.resolve(t),n=r.nodeAfter;return n&&n.isAtom&&E.isSelectable(n)?(Fe(e,new E(r),"pointer"),!0):!1}function iu(e,t){if(t==-1)return!1;var r=e.state.selection,n,o;r instanceof E&&(n=r.node);for(var i=e.state.doc.resolve(t),s=i.depth+1;s>0;s--){var a=s>i.depth?i.nodeAfter:i.node(s);if(E.isSelectable(a)){n&&r.$from.depth>0&&s>=r.$from.depth&&i.before(r.$from.depth+1)==r.$from.pos?o=i.before(r.$from.depth):o=i.before(s);break}}return o!=null?(Fe(e,E.create(e.state.doc,o),"pointer"),!0):!1}function su(e,t,r,n,o){return eo(e,"handleClickOn",t,r,n)||e.someProp("handleClick",function(i){return i(e,t,n)})||(o?iu(e,r):ou(e,r))}function au(e,t,r,n){return eo(e,"handleDoubleClickOn",t,r,n)||e.someProp("handleDoubleClick",function(o){return o(e,t,n)})}function cu(e,t,r,n){return eo(e,"handleTripleClickOn",t,r,n)||e.someProp("handleTripleClick",function(o){return o(e,t,n)})||lu(e,r,n)}function lu(e,t,r){if(r.button!=0)return!1;var n=e.state.doc;if(t==-1)return n.inlineContent?(Fe(e,H.create(n,0,n.content.size),"pointer"),!0):!1;for(var o=n.resolve(t),i=o.depth+1;i>0;i--){var s=i>o.depth?o.nodeAfter:o.node(i),a=o.before(i);if(s.inlineContent)Fe(e,H.create(n,a+1,a+1+s.content.size),"pointer");else if(E.isSelectable(s))Fe(e,E.create(n,a),"pointer");else continue;return!0}}function ro(e){return Fr(e)}var ws=x.mac?"metaKey":"ctrlKey";Ct.mousedown=function(e,t){e.shiftKey=t.shiftKey;var r=ro(e),n=Date.now(),o="singleClick";n-e.lastClick.time<500&&nu(t,e.lastClick)&&!t[ws]&&(e.lastClick.type=="singleClick"?o="doubleClick":e.lastClick.type=="doubleClick"&&(o="tripleClick")),e.lastClick={time:n,x:t.clientX,y:t.clientY,type:o};var i=e.posAtCoords(zr(t));!i||(o=="singleClick"?(e.mouseDown&&e.mouseDown.done(),e.mouseDown=new Lr(e,i,t,r)):(o=="doubleClick"?au:cu)(e,i.pos,i.inside,t)?t.preventDefault():Ce(e,"pointer"))};var Lr=function(t,r,n,o){var i=this;this.view=t,this.startDoc=t.state.doc,this.pos=r,this.event=n,this.flushed=o,this.selectNode=n[ws],this.allowDefault=n.shiftKey,this.delayedSelectionSync=!1;var s,a;if(r.inside>-1)s=t.state.doc.nodeAt(r.inside),a=r.inside;else{var c=t.state.doc.resolve(r.pos);s=c.parent,a=c.depth?c.before():0}this.mightDrag=null;var l=o?null:n.target,u=l?t.docView.nearestDesc(l,!0):null;this.target=u?u.dom:null;var f=t.state,p=f.selection;(n.button==0&&s.type.spec.draggable&&s.type.spec.selectable!==!1||p instanceof E&&p.from<=a&&p.to>a)&&(this.mightDrag={node:s,pos:a,addAttr:this.target&&!this.target.draggable,setUneditable:this.target&&x.gecko&&!this.target.hasAttribute("contentEditable")}),this.target&&this.mightDrag&&(this.mightDrag.addAttr||this.mightDrag.setUneditable)&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&(this.target.draggable=!0),this.mightDrag.setUneditable&&setTimeout(function(){i.view.mouseDown==i&&i.target.setAttribute("contentEditable","false")},20),this.view.domObserver.start()),t.root.addEventListener("mouseup",this.up=this.up.bind(this)),t.root.addEventListener("mousemove",this.move=this.move.bind(this)),Ce(t,"pointer")};Lr.prototype.done=function(){var t=this;this.view.root.removeEventListener("mouseup",this.up),this.view.root.removeEventListener("mousemove",this.move),this.mightDrag&&this.target&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&this.target.removeAttribute("draggable"),this.mightDrag.setUneditable&&this.target.removeAttribute("contentEditable"),this.view.domObserver.start()),this.delayedSelectionSync&&setTimeout(function(){return fe(t.view)}),this.view.mouseDown=null};Lr.prototype.up=function(t){if(this.done(),!!this.view.dom.contains(t.target.nodeType==3?t.target.parentNode:t.target)){var r=this.pos;this.view.state.doc!=this.startDoc&&(r=this.view.posAtCoords(zr(t))),this.allowDefault||!r?Ce(this.view,"pointer"):su(this.view,r.pos,r.inside,t,this.selectNode)?t.preventDefault():t.button==0&&(this.flushed||x.safari&&this.mightDrag&&!this.mightDrag.node.isAtom||x.chrome&&!(this.view.state.selection instanceof H)&&Math.min(Math.abs(r.pos-this.view.state.selection.from),Math.abs(r.pos-this.view.state.selection.to))<=2)?(Fe(this.view,D.near(this.view.state.doc.resolve(r.pos)),"pointer"),t.preventDefault()):Ce(this.view,"pointer")}};Lr.prototype.move=function(t){!this.allowDefault&&(Math.abs(this.event.x-t.clientX)>4||Math.abs(this.event.y-t.clientY)>4)&&(this.allowDefault=!0),Ce(this.view,"pointer"),t.buttons==0&&this.done()};Ct.touchdown=function(e){ro(e),Ce(e,"pointer")};Ct.contextmenu=function(e){return ro(e)};function Ts(e,t){return e.composing?!0:x.safari&&Math.abs(t.timeStamp-e.compositionEndedAt)<500?(e.compositionEndedAt=-2e8,!0):!1}var uu=x.android?5e3:-1;kt.compositionstart=kt.compositionupdate=function(e){if(!e.composing){e.domObserver.flush();var t=e.state,r=t.selection.$from;if(t.selection.empty&&(t.storedMarks||!r.textOffset&&r.parentOffset&&r.nodeBefore.marks.some(function(a){return a.type.spec.inclusive===!1})))e.markCursor=e.state.storedMarks||r.marks(),Fr(e,!0),e.markCursor=null;else if(Fr(e),x.gecko&&t.selection.empty&&r.parentOffset&&!r.textOffset&&r.nodeBefore.marks.length)for(var n=e.root.getSelection(),o=n.focusNode,i=n.focusOffset;o&&o.nodeType==1&&i!=0;){var s=i<0?o.lastChild:o.childNodes[i-1];if(!s)break;if(s.nodeType==3){n.collapse(s,s.nodeValue.length);break}else o=s,i=-1}e.composing=!0}As(e,uu)};kt.compositionend=function(e,t){e.composing&&(e.composing=!1,e.compositionEndedAt=t.timeStamp,As(e,20))};function As(e,t){clearTimeout(e.composingTimeout),t>-1&&(e.composingTimeout=setTimeout(function(){return Fr(e)},t))}function _s(e){for(e.composing&&(e.composing=!1,e.compositionEndedAt=fu());e.compositionNodes.length>0;)e.compositionNodes.pop().markParentsDirty()}function fu(){var e=document.createEvent("Event");return e.initEvent("event",!0,!0),e.timeStamp}function Fr(e,t){if(e.domObserver.forceFlush(),_s(e),t||e.docView.dirty){var r=ns(e);return r&&!r.eq(e.state.selection)?e.dispatch(e.state.tr.setSelection(r)):e.updateState(e.state),!0}return!1}function pu(e,t){if(!!e.dom.parentNode){var r=e.dom.parentNode.appendChild(document.createElement("div"));r.appendChild(t),r.style.cssText="position: fixed; left: -10000px; top: 10px";var n=getSelection(),o=document.createRange();o.selectNodeContents(t),e.dom.blur(),n.removeAllRanges(),n.addRange(o),setTimeout(function(){r.parentNode&&r.parentNode.removeChild(r),e.focus()},50)}}var Ve=x.ie&&x.ie_version<15||x.ios&&x.webkit_version<604;Ct.copy=kt.cut=function(e,t){var r=e.state.selection,n=t.type=="cut";if(!r.empty){var o=Ve?null:t.clipboardData,i=r.content(),s=gs(e,i),a=s.dom,c=s.text;o?(t.preventDefault(),o.clearData(),o.setData("text/html",a.innerHTML),o.setData("text/plain",c)):pu(e,a),n&&e.dispatch(e.state.tr.deleteSelection().scrollIntoView().setMeta("uiEvent","cut"))}};function du(e){return e.openStart==0&&e.openEnd==0&&e.content.childCount==1?e.content.firstChild:null}function hu(e,t){if(!!e.dom.parentNode){var r=e.shiftKey||e.state.selection.$from.parent.type.spec.code,n=e.dom.parentNode.appendChild(document.createElement(r?"textarea":"div"));r||(n.contentEditable="true"),n.style.cssText="position: fixed; left: -10000px; top: 10px",n.focus(),setTimeout(function(){e.focus(),n.parentNode&&n.parentNode.removeChild(n),r?no(e,n.value,null,t):no(e,n.textContent,n.innerHTML,t)},50)}}function no(e,t,r,n){var o=ys(e,t,r,e.shiftKey,e.state.selection.$from);if(e.someProp("handlePaste",function(a){return a(e,n,o||C.empty)}))return!0;if(!o)return!1;var i=du(o),s=i?e.state.tr.replaceSelectionWith(i,e.shiftKey):e.state.tr.replaceSelection(o);return e.dispatch(s.scrollIntoView().setMeta("paste",!0).setMeta("uiEvent","paste")),!0}kt.paste=function(e,t){var r=Ve?null:t.clipboardData;r&&no(e,r.getData("text/plain"),r.getData("text/html"),t)?t.preventDefault():hu(e,t)};var mu=function(t,r){this.slice=t,this.move=r},Ns=x.mac?"altKey":"ctrlKey";Ct.dragstart=function(e,t){var r=e.mouseDown;if(r&&r.done(),!!t.dataTransfer){var n=e.state.selection,o=n.empty?null:e.posAtCoords(zr(t));if(!(o&&o.pos>=n.from&&o.pos<=(n instanceof E?n.to-1:n.to))){if(r&&r.mightDrag)e.dispatch(e.state.tr.setSelection(E.create(e.state.doc,r.mightDrag.pos)));else if(t.target&&t.target.nodeType==1){var i=e.docView.nearestDesc(t.target,!0);i&&i.node.type.spec.draggable&&i!=e.docView&&e.dispatch(e.state.tr.setSelection(E.create(e.state.doc,i.posBefore)))}}var s=e.state.selection.content(),a=gs(e,s),c=a.dom,l=a.text;t.dataTransfer.clearData(),t.dataTransfer.setData(Ve?"Text":"text/html",c.innerHTML),t.dataTransfer.effectAllowed="copyMove",Ve||t.dataTransfer.setData("text/plain",l),e.dragging=new mu(s,!t[Ns])}};Ct.dragend=function(e){var t=e.dragging;window.setTimeout(function(){e.dragging==t&&(e.dragging=null)},50)};kt.dragover=kt.dragenter=function(e,t){return t.preventDefault()};kt.drop=function(e,t){var r=e.dragging;if(e.dragging=null,!!t.dataTransfer){var n=e.posAtCoords(zr(t));if(!!n){var o=e.state.doc.resolve(n.pos);if(!!o){var i=r&&r.slice;i?e.someProp("transformPasted",function(h){i=h(i)}):i=ys(e,t.dataTransfer.getData(Ve?"Text":"text/plain"),Ve?null:t.dataTransfer.getData("text/html"),!1,o);var s=r&&!t[Ns];if(e.someProp("handleDrop",function(h){return h(e,t,i||C.empty,s)})){t.preventDefault();return}if(!!i){t.preventDefault();var a=i?Oi(e.state.doc,o.pos,i):o.pos;a==null&&(a=o.pos);var c=e.state.tr;s&&c.deleteSelection();var l=c.mapping.map(a),u=i.openStart==0&&i.openEnd==0&&i.content.childCount==1,f=c.doc;if(u?c.replaceRangeWith(l,l,i.content.firstChild):c.replaceRange(l,l,i),!c.doc.eq(f)){var p=c.doc.resolve(l);if(u&&E.isSelectable(i.content.firstChild)&&p.nodeAfter&&p.nodeAfter.sameMarkup(i.content.firstChild))c.setSelection(new E(p));else{var d=c.mapping.map(a);c.mapping.maps[c.mapping.maps.length-1].forEach(function(h,v,g,M){return d=M}),c.setSelection(qn(e,p,c.doc.resolve(d)))}e.focus(),e.dispatch(c.setMeta("uiEvent","drop"))}}}}}};Ct.focus=function(e){e.focused||(e.domObserver.stop(),e.dom.classList.add("ProseMirror-focused"),e.domObserver.start(),e.focused=!0,setTimeout(function(){e.docView&&e.hasFocus()&&!e.domObserver.currentSelection.eq(e.root.getSelection())&&fe(e)},20))};Ct.blur=function(e,t){e.focused&&(e.domObserver.stop(),e.dom.classList.remove("ProseMirror-focused"),e.domObserver.start(),t.relatedTarget&&e.dom.contains(t.relatedTarget)&&e.domObserver.currentSelection.set({}),e.focused=!1)};Ct.beforeinput=function(e,t){if(x.chrome&&x.android&&t.inputType=="deleteContentBackward"){var r=e.domChangeCount;setTimeout(function(){if(e.domChangeCount==r&&(e.dom.blur(),e.focus(),!e.someProp("handleKeyDown",function(i){return i(e,ze(8,"Backspace"))}))){var n=e.state.selection,o=n.$cursor;o&&o.pos>0&&e.dispatch(e.state.tr.delete(o.pos-1,o.pos).scrollIntoView())}},50)}};for(var Es in kt)Ct[Es]=kt[Es];function lr(e,t){if(e==t)return!0;for(var r in e)if(e[r]!==t[r])return!1;for(var n in t)if(!(n in e))return!1;return!0}var ur=function(t,r){this.spec=r||Oe,this.side=this.spec.side||0,this.toDOM=t};ur.prototype.map=function(t,r,n,o){var i=t.mapResult(r.from+o,this.side<0?-1:1),s=i.pos,a=i.deleted;return a?null:new nt(s-n,s-n,this)};ur.prototype.valid=function(){return!0};ur.prototype.eq=function(t){return this==t||t instanceof ur&&(this.spec.key&&this.spec.key==t.spec.key||this.toDOM==t.toDOM&&lr(this.spec,t.spec))};var Vt=function(t,r){this.spec=r||Oe,this.attrs=t};Vt.prototype.map=function(t,r,n,o){var i=t.map(r.from+o,this.spec.inclusiveStart?-1:1)-n,s=t.map(r.to+o,this.spec.inclusiveEnd?1:-1)-n;return i>=s?null:new nt(i,s,this)};Vt.prototype.valid=function(t,r){return r.from=t&&(!i||i(a.spec))&&n.push(a.copy(a.from+o,a.to+o))}for(var c=0;ct){var l=this.children[c]+1;this.children[c+2].findInner(t-l,r-l,n,o+l,i)}};q.prototype.map=function(t,r,n){return this==st||t.maps.length==0?this:this.mapInner(t,r,0,0,n||Oe)};q.prototype.mapInner=function(t,r,n,o,i){for(var s,a=0;aa&&u.to=t){this.children[i]==t&&(n=this.children[i+2]);break}for(var s=t+1,a=s+r.content.size,c=0;cs&&l.type instanceof Vt){var u=Math.max(s,l.from)-s,f=Math.min(a,l.to)-s;uY+i||(rt>=a[G]+i?a[G+1]=-1:ut>=o&&(oe=qt-ut-(rt-Qt))&&(a[G]+=oe,a[G+1]+=oe))}},l=0;l=n.content.size){u=!0;continue}var h=r.map(e[f+1]+i,-1),v=h-o,g=n.content.findIndex(d),M=g.index,y=g.offset,R=n.maybeChild(M);if(R&&y==d&&y+R.nodeSize==v){var m=a[f+2].mapInner(r,R,p+1,e[f]+i+1,s);m!=st?(a[f]=d,a[f+1]=v,a[f+2]=m):(a[f+1]=-2,u=!0)}else u=!0}if(u){var I=gu(a,e,t||[],r,o,i,s),O=Hr(I,n,0,s);t=O.local;for(var F=0;Fr&&s.to0;)t++;e.splice(t,0,r)}function so(e){var t=[];return e.someProp("decorations",function(r){var n=r(e.state);n&&n!=st&&t.push(n)}),e.cursorWrapper&&t.push(q.create(e.state.doc,[e.cursorWrapper.deco])),zt.from(t)}var Z=function(t,r){this._props=r,this.state=r.state,this.dispatch=this.dispatch.bind(this),this._root=null,this.focused=!1,this.trackWrites=null,this.dom=t&&t.mount||document.createElement("div"),t&&(t.appendChild?t.appendChild(this.dom):t.apply?t(this.dom):t.mount&&(this.mounted=!0)),this.editable=Ls(this),this.markCursor=null,this.cursorWrapper=null,zs(this),this.nodeViews=Fs(this),this.docView=Xi(this.state.doc,Bs(this),so(this),this.dom,this),this.lastSelectedViewDesc=null,this.dragging=null,Zl(this),this.pluginViews=[],this.updatePluginViews()},ao={props:{configurable:!0},root:{configurable:!0}};ao.props.get=function(){if(this._props.state!=this.state){var e=this._props;this._props={};for(var t in e)this._props[t]=e[t];this._props.state=this.state}return this._props};Z.prototype.update=function(t){t.handleDOMEvents!=this._props.handleDOMEvents&&Zn(this),this._props=t,this.updateStateInner(t.state,!0)};Z.prototype.setProps=function(t){var r={};for(var n in this._props)r[n]=this._props[n];r.state=this.state;for(var o in t)r[o]=t[o];this.update(r)};Z.prototype.updateState=function(t){this.updateStateInner(t,this.state.plugins!=t.plugins)};Z.prototype.updateStateInner=function(t,r){var n=this,o=this.state,i=!1,s=!1;if(t.storedMarks&&this.composing&&(_s(this),s=!0),this.state=t,r){var a=Fs(this);bu(a,this.nodeViews)&&(this.nodeViews=a,i=!0),Zn(this)}this.editable=Ls(this),zs(this);var c=so(this),l=Bs(this),u=r?"reset":t.scrollToSelection>o.scrollToSelection?"to selection":"preserve",f=i||!this.docView.matchesNode(t.doc,l,c);(f||!t.selection.eq(o.selection))&&(s=!0);var p=u=="preserve"&&s&&this.dom.style.overflowAnchor==null&&ll(this);if(s){this.domObserver.stop();var d=f&&(x.ie||x.chrome)&&!this.composing&&!o.selection.empty&&!t.selection.empty&&yu(o.selection,t.selection);if(f){var h=x.chrome?this.trackWrites=this.root.getSelection().focusNode:null;(i||!this.docView.update(t.doc,l,c,this))&&(this.docView.updateOuterDeco([]),this.docView.destroy(),this.docView=Xi(t.doc,l,c,this.dom,this)),h&&!this.trackWrites&&(d=!0)}d||!(this.mouseDown&&this.domObserver.currentSelection.eq(this.root.getSelection())&&Bl(this))?fe(this,d):(ls(this,t.selection),this.domObserver.setCurSelection()),this.domObserver.start()}if(this.updatePluginViews(o),u=="reset")this.dom.scrollTop=0;else if(u=="to selection"){var v=this.root.getSelection().focusNode;this.someProp("handleScrollToSelection",function(g){return g(n)})||(t.selection instanceof E?Vi(this,this.docView.domAfterPos(t.selection.from).getBoundingClientRect(),v):Vi(this,this.coordsAtPos(t.selection.head,1),v))}else p&&ul(p)};Z.prototype.destroyPluginViews=function(){for(var t;t=this.pluginViews.pop();)t.destroy&&t.destroy()};Z.prototype.updatePluginViews=function(t){if(!t||t.plugins!=this.state.plugins){this.destroyPluginViews();for(var r=0;r",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"',229:"Q"},Vs=typeof navigator!="undefined"&&/Chrome\/(\d+)/.exec(navigator.userAgent),ku=typeof navigator!="undefined"&&/Apple Computer/.test(navigator.vendor),Su=typeof navigator!="undefined"&&/Gecko\/\d+/.test(navigator.userAgent),Hs=typeof navigator!="undefined"&&/Mac/.test(navigator.platform),Mu=typeof navigator!="undefined"&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),xu=Vs&&(Hs||+Vs[1]<57)||Su&&Hs;for(var at=0;at<10;at++)pe[48+at]=pe[96+at]=String(at);for(var at=1;at<=24;at++)pe[at+111]="F"+at;for(var at=65;at<=90;at++)pe[at]=String.fromCharCode(at+32),jr[at]=String.fromCharCode(at);for(var co in pe)jr.hasOwnProperty(co)||(jr[co]=pe[co]);function Cu(e){var t=xu&&(e.ctrlKey||e.altKey||e.metaKey)||(ku||Mu)&&e.shiftKey&&e.key&&e.key.length==1,r=!t&&e.key||(e.shiftKey?jr:pe)[e.keyCode]||e.key||"Unidentified";return r=="Esc"&&(r="Escape"),r=="Del"&&(r="Delete"),r=="Left"&&(r="ArrowLeft"),r=="Up"&&(r="ArrowUp"),r=="Right"&&(r="ArrowRight"),r=="Down"&&(r="ArrowDown"),r}var Ou=typeof navigator!="undefined"?/Mac/.test(navigator.platform):!1;function wu(e){var t=e.split(/-(?!$)/),r=t[t.length-1];r=="Space"&&(r=" ");for(var n,o,i,s,a=0;a127)&&(s=pe[n.keyCode])&&s!=o){var c=t[lo(s,n,!0)];if(c&&c(r.state,r.dispatch,r))return!0}else if(i&&n.shiftKey){var l=t[lo(o,n,!0)];if(l&&l(r.state,r.dispatch,r))return!0}return!1}}var Ut=function(t,r){this.match=t,this.handler=typeof r=="string"?_u(r):r};function _u(e){return function(t,r,n,o){var i=e;if(r[1]){var s=r[0].lastIndexOf(r[1]);i+=r[0].slice(s+r[1].length),n+=s;var a=n-o;a>0&&(i=r[0].slice(s-a,s)+i,n=o)}return t.tr.insertText(i,n,o)}}var Nu=500;function Eu(e){var t=e.rules,r=new Rt({state:{init:function(){return null},apply:function(o,i){var s=o.getMeta(this);return s||(o.selectionSet||o.docChanged?null:i)}},props:{handleTextInput:function(o,i,s,a){return qs(o,i,s,a,t,r)},handleDOMEvents:{compositionend:function(n){setTimeout(function(){var o=n.state.selection,i=o.$cursor;i&&qs(n,i.pos,i.pos,"",t,r)})}}},isInputRules:!0});return r}function qs(e,t,r,n,o,i){if(e.composing)return!1;var s=e.state,a=s.doc.resolve(t);if(a.parent.type.spec.code)return!1;for(var c=a.parent.textBetween(Math.max(0,a.parentOffset-Nu),a.parentOffset,null,"\uFFFC")+n,l=0;l=0;c--)s.step(a.steps[c].invert(a.docs[c]));if(i.text){var l=s.doc.resolve(i.from).marks();s.replaceWith(i.from,i.to,e.schema.text(i.text,l))}else s.delete(i.from,i.to);t(s)}return!0}}return!1}new Ut(/--$/,"\u2014");new Ut(/\.\.\.$/,"\u2026");new Ut(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(")$/,"\u201C");new Ut(/"$/,"\u201D");new Ut(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(')$/,"\u2018");new Ut(/'$/,"\u2019");function uo(e,t,r,n){return new Ut(e,function(o,i,s,a){var c=r instanceof Function?r(i):r,l=o.tr.delete(s,a),u=l.doc.resolve(s),f=u.blockRange(),p=f&&Sn(f,t,c);if(!p)return null;l.wrap(f,p);var d=l.doc.resolve(s-1).nodeBefore;return d&&d.type==t&&Mn(l.doc,s-1)&&(!n||n(i,d))&&l.join(s-1),l})}function fo(e,t,r){return new Ut(e,function(n,o,i,s){var a=n.doc.resolve(i),c=r instanceof Function?r(o):r;return a.node(-1).canReplaceWith(a.index(-1),a.indexAfter(-1),t)?n.tr.delete(i,s).setBlockType(i,i,t,c):null})}function po(e,t){return e.selection.empty?!1:(t&&t(e.tr.deleteSelection().scrollIntoView()),!0)}function Js(e,t,r){var n=e.selection,o=n.$cursor;if(!o||(r?!r.endOfTextblock("backward",e):o.parentOffset>0))return!1;var i=Ks(o);if(!i){var s=o.blockRange(),a=s&&Pe(s);return a==null?!1:(t&&t(e.tr.lift(s,a).scrollIntoView()),!0)}var c=i.nodeBefore;if(!c.type.spec.isolating&&ra(e,i,t))return!0;if(o.parent.content.size==0&&(He(c,"end")||E.isSelectable(c))){if(t){var l=e.tr.deleteRange(o.before(),o.after());l.setSelection(He(c,"end")?D.findFrom(l.doc.resolve(l.mapping.map(i.pos,-1)),-1):E.create(l.doc,i.pos-c.nodeSize)),t(l.scrollIntoView())}return!0}return c.isAtom&&i.depth==o.depth-1?(t&&t(e.tr.delete(i.pos-c.nodeSize,i.pos).scrollIntoView()),!0):!1}function He(e,t,r){for(;e;e=t=="start"?e.firstChild:e.lastChild){if(e.isTextblock)return!0;if(r&&e.childCount!=1)return!1}return!1}function Ws(e,t,r){var n=e.selection,o=n.$head,i=n.empty,s=o;if(!i)return!1;if(o.parent.isTextblock){if(r?!r.endOfTextblock("backward",e):o.parentOffset>0)return!1;s=Ks(o)}var a=s&&s.nodeBefore;return!a||!E.isSelectable(a)?!1:(t&&t(e.tr.setSelection(E.create(e.doc,s.pos-a.nodeSize)).scrollIntoView()),!0)}function Ks(e){if(!e.parent.type.spec.isolating)for(var t=e.depth-1;t>=0;t--){if(e.index(t)>0)return e.doc.resolve(e.before(t+1));if(e.node(t).type.spec.isolating)break}return null}function $s(e,t,r){var n=e.selection,o=n.$cursor;if(!o||(r?!r.endOfTextblock("forward",e):o.parentOffset=0;t--){var r=e.node(t);if(e.index(t)+11&&n.after()!=n.end(-1)){var o=n.before();if(ee(e.doc,o))return t&&t(e.tr.split(o).scrollIntoView()),!0}var i=n.blockRange(),s=i&&Pe(i);return s==null?!1:(t&&t(e.tr.lift(i,s).scrollIntoView()),!0)}function Iu(e,t){var r=e.selection,n=r.$from,o=r.$to;if(e.selection instanceof E&&e.selection.node.isBlock)return!n.parentOffset||!ee(e.doc,n.pos)?!1:(t&&t(e.tr.split(n.pos).scrollIntoView()),!0);if(!n.parent.isBlock)return!1;if(t){var i=o.parentOffset==o.parent.content.size,s=e.tr;(e.selection instanceof H||e.selection instanceof re)&&s.deleteSelection();var a=n.depth==0?null:ho(n.node(-1).contentMatchAt(n.indexAfter(-1))),c=i&&a?[{type:a}]:null,l=ee(s.doc,s.mapping.map(n.pos),1,c);if(!c&&!l&&ee(s.doc,s.mapping.map(n.pos),1,a&&[{type:a}])&&(c=[{type:a}],l=!0),l&&(s.split(s.mapping.map(n.pos),1,c),!i&&!n.parentOffset&&n.parent.type!=a)){var u=s.mapping.map(n.before()),f=s.doc.resolve(u);n.node(-1).canReplaceWith(f.index(),f.index()+1,a)&&s.setNodeMarkup(s.mapping.map(n.before()),a)}t(s.scrollIntoView())}return!0}function Ru(e,t){var r=e.selection,n=r.$from,o=r.to,i,s=n.sharedDepth(o);return s==0?!1:(i=n.before(s),t&&t(e.tr.setSelection(E.create(e.doc,i))),!0)}function ea(e,t){return t&&t(e.tr.setSelection(new re(e.doc))),!0}function Pu(e,t,r){var n=t.nodeBefore,o=t.nodeAfter,i=t.index();return!n||!o||!n.type.compatibleContent(o.type)?!1:!n.content.size&&t.parent.canReplace(i-1,i)?(r&&r(e.tr.delete(t.pos-n.nodeSize,t.pos).scrollIntoView()),!0):!t.parent.canReplace(i,i+1)||!(o.isTextblock||Mn(e.doc,t.pos))?!1:(r&&r(e.tr.clearIncompatible(t.pos,n.type,n.contentMatchAt(n.childCount)).join(t.pos).scrollIntoView()),!0)}function ra(e,t,r){var n=t.nodeBefore,o=t.nodeAfter,i,s;if(n.type.spec.isolating||o.type.spec.isolating)return!1;if(Pu(e,t,r))return!0;var a=t.parent.canReplace(t.index(),t.index()+1);if(a&&(i=(s=n.contentMatchAt(n.childCount)).findWrapping(o.type))&&s.matchType(i[0]||o.type).validEnd){if(r){for(var c=t.pos+o.nodeSize,l=k.empty,u=i.length-1;u>=0;u--)l=k.from(i[u].create(null,l));l=k.from(n.copy(l));var f=e.tr.step(new xt(t.pos-1,c,t.pos,c,new C(l,1,0),i.length,!0)),p=c+2*i.length;Mn(f.doc,p)&&f.join(p),r(f.scrollIntoView())}return!0}var d=D.findFrom(t,1),h=d&&d.$from.blockRange(d.$to),v=h&&Pe(h);if(v!=null&&v>=t.depth)return r&&r(e.tr.lift(h,v).scrollIntoView()),!0;if(a&&He(o,"start",!0)&&He(n,"end")){for(var g=n,M=[];M.push(g),!g.isTextblock;)g=g.lastChild;for(var y=o,R=1;!y.isTextblock;y=y.firstChild)R++;if(g.canReplace(g.childCount,g.childCount,y.content)){if(r){for(var m=k.empty,I=M.length-1;I>=0;I--)m=k.from(M[I].copy(m));var O=e.tr.step(new xt(t.pos-M.length,t.pos+o.nodeSize,t.pos+R,t.pos+o.nodeSize-R,new C(m,M.length,0),0,!0));r(O.scrollIntoView())}return!0}}return!1}function na(e,t){return function(r,n){var o=r.selection,i=o.$from,s=o.$to,a=i.blockRange(s),c=a&&Sn(a,e,t);return c?(n&&n(r.tr.wrap(a,c).scrollIntoView()),!0):!1}}function Bu(e,t){return function(r,n){var o=r.selection,i=o.from,s=o.to,a=!1;return r.doc.nodesBetween(i,s,function(c,l){if(a)return!1;if(!(!c.isTextblock||c.hasMarkup(e,t)))if(c.type==e)a=!0;else{var u=r.doc.resolve(l),f=u.index();a=u.parent.canReplaceWith(f,f+1,e)}}),a?(n&&n(r.tr.setBlockType(i,s,e,t).scrollIntoView()),!0):!1}}function mo(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return function(r,n,o){for(var i=0;i=2&&i.node(a.depth-1).type.compatibleContent(e)&&a.startIndex==0){if(i.index(a.depth-1)==0)return!1;var u=r.doc.resolve(a.start-2);l=new Ge(u,u,a.depth),a.endIndex=0;s--)i=k.from(r[s].type.create(r[s].attrs,i));e.step(new xt(t.start-(n?2:0),t.end,t.start,t.end,new C(i,0,0),r.length,!0));for(var a=0,c=0;ca;s--)i-=o.child(s).nodeSize,n.delete(i-1,i+1);var c=n.doc.resolve(r.start),l=c.nodeAfter,u=r.startIndex==0,f=r.endIndex==o.childCount,p=c.node(-1),d=c.index(-1);if(!p.canReplace(d+(u?0:1),d+1,l.content.append(f?k.empty:k.from(o))))return!1;var h=c.pos,v=h+l.nodeSize;return n.step(new xt(h-(u?1:0),v+(f?1:0),h+1,v-1,new C((u?k.empty:k.from(o.copy(k.empty))).append(f?k.empty:k.from(o.copy(k.empty))),u?0:1,f?0:1),u?0:1)),t(n.scrollIntoView()),!0}function qu(e){return function(t,r){var n=t.selection,o=n.$from,i=n.$to,s=o.blockRange(i,function(v){return v.childCount&&v.firstChild.type==e});if(!s)return!1;var a=s.startIndex;if(a==0)return!1;var c=s.parent,l=c.child(a-1);if(l.type!=e)return!1;if(r){var u=l.lastChild&&l.lastChild.type==c.type,f=k.from(u?e.create():null),p=new C(k.from(e.create(null,k.from(c.type.create(null,f)))),u?3:1,0),d=s.start,h=s.end;r(t.tr.step(new xt(d-(u?3:1),h,d,h,p,1,!0)).scrollIntoView())}return!0}}function qr(e,t){return t.nodes[e]?"node":t.marks[e]?"mark":null}function tt(e,t){if(typeof e=="string"){if(!t.nodes[e])throw Error(`There is no node type named '${e}'. Maybe you forgot to add the extension?`);return t.nodes[e]}return e}function Ju(e,t){const r=tt(t,e.schema),{from:n,to:o}=e.selection;let i=[];e.doc.nodesBetween(n,o,a=>{i=[...i,a]});const s=i.reverse().find(a=>a.type.name===r.name);return s?S({},s.attrs):{}}function he(e,t){if(typeof e=="string"){if(!t.marks[e])throw Error(`There is no mark type named '${e}'. Maybe you forgot to add the extension?`);return t.marks[e]}return e}function aa(e,t){const r=he(t,e.schema),{from:n,to:o,empty:i}=e.selection;let s=[];i?s=e.selection.$head.marks():e.doc.nodesBetween(n,o,c=>{s=[...s,...c.marks]});const a=s.find(c=>c.type.name===r.name);return a?S({},a.attrs):{}}function Wu(e,t){const r=qr(typeof t=="string"?t:t.name,e.schema);return r==="node"?Ju(e,t):r==="mark"?aa(e,t):{}}function pr(e,t){const r=Object.keys(t);return r.length?!!r.filter(n=>t[n]===e[n]).length:!0}function je(e,t,r={}){const{from:n,to:o,empty:i}=e.selection,s=t?tt(t,e.schema):null;let a=[];if(e.doc.nodesBetween(n,o,(u,f)=>{if(!u.isText){const p=Math.max(n,f),d=Math.min(o,f+u.nodeSize);a=[...a,{node:u,from:p,to:d}]}}),i)return!!a.filter(u=>s?s.name===u.node.type.name:!0).find(u=>pr(u.node.attrs,r));const c=o-n;return a.filter(u=>s?s.name===u.node.type.name:!0).filter(u=>pr(u.node.attrs,r)).reduce((u,f)=>{const p=f.to-f.from;return u+p},0)>=c}function vo(e,t,r={}){const{from:n,to:o,empty:i}=e.selection,s=t?he(t,e.schema):null;if(i)return!!(e.storedMarks||e.selection.$from.marks()).filter(p=>s?s.name===p.type.name:!0).find(p=>pr(p.attrs,r));let a=0,c=[];if(e.doc.nodesBetween(n,o,(p,d)=>{if(p.isText){const h=Math.max(n,d),v=Math.min(o,d+p.nodeSize);a+=v-h,c=[...c,...p.marks.map(M=>({mark:M,from:h,to:v}))]}}),a===0)return!1;const l=c.filter(p=>s?s.name===p.mark.type.name:!0).filter(p=>pr(p.mark.attrs,r)).reduce((p,d)=>{const h=d.to-d.from;return p+h},0),u=c.filter(p=>s?p.mark.type!==s&&p.mark.type.excludes(s):!0).reduce((p,d)=>{const h=d.to-d.from;return p+h},0);return(l>0?l+u:l)>=a}function Ku(e,t,r={}){if(!t)return je(e,null,r)||vo(e,null,r);const n=qr(t,e.schema);return n==="node"?je(e,t,r):n==="mark"?vo(e,t,r):!1}function $u(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function ca(e){const t=`${e}`;return new window.DOMParser().parseFromString(t,"text/html").body}function Jr(e,t,r){if(r=S({slice:!0,parseOptions:{}},r),typeof e=="object"&&e!==null)try{return Array.isArray(e)?k.fromArray(e.map(n=>t.nodeFromJSON(n))):t.nodeFromJSON(e)}catch(n){return console.warn("[tiptap warn]: Invalid content.","Passed value:",e,"Error:",n),Jr("",t,r)}if(typeof e=="string"){const n=Lt.fromSchema(t);return r.slice?n.parseSlice(ca(e),r.parseOptions).content:n.parse(ca(e),r.parseOptions)}return Jr("",t,r)}function la(e,t,r={}){return Jr(e,t,{slice:!1,parseOptions:r})}function Uu(e,t){const r=ot.fromSchema(t).serializeFragment(e.content),o=document.implementation.createHTMLDocument().createElement("div");return o.appendChild(r),o.innerHTML}function Gu(e){var t;const r=(t=e.type.createAndFill())===null||t===void 0?void 0:t.toJSON(),n=e.toJSON();return JSON.stringify(r)===JSON.stringify(n)}function Yu(e){const t=document.querySelector("style[data-tiptap-style]");if(t!==null)return t;const r=document.createElement("style");return r.setAttribute("data-tiptap-style",""),r.innerHTML=e,document.getElementsByTagName("head")[0].appendChild(r),r}class Xu{constructor(t,r){this.editor=t,this.commands=r}createCommands(){const{commands:t,editor:r}=this,{state:n,view:o}=r,{tr:i}=n,s=this.buildProps(i);return Object.fromEntries(Object.entries(t).map(([a,c])=>[a,(...u)=>{const f=c(...u)(s);return i.getMeta("preventDispatch")||o.dispatch(i),f}]))}createChain(t,r=!0){const{commands:n,editor:o}=this,{state:i,view:s}=o,a=[],c=!!t,l=t||i.tr,u=()=>(!c&&r&&!l.getMeta("preventDispatch")&&s.dispatch(l),a.every(p=>p===!0)),f=Tt(S({},Object.fromEntries(Object.entries(n).map(([p,d])=>[p,(...v)=>{const g=this.buildProps(l,r),M=d(...v)(g);return a.push(M),f}]))),{run:u});return f}createCan(t){const{commands:r,editor:n}=this,{state:o}=n,i=void 0,s=t||o.tr,a=this.buildProps(s,i),c=Object.fromEntries(Object.entries(r).map(([l,u])=>[l,(...f)=>u(...f)(Tt(S({},a),{dispatch:i}))]));return Tt(S({},c),{chain:()=>this.createChain(s,i)})}buildProps(t,r=!0){const{editor:n,commands:o}=this,{state:i,view:s}=n;i.storedMarks&&t.setStoredMarks(i.storedMarks);const a={tr:t,editor:n,view:s,state:this.chainableState(t,i),dispatch:r?()=>{}:void 0,chain:()=>this.createChain(t),can:()=>this.createCan(t),get commands(){return Object.fromEntries(Object.entries(o).map(([c,l])=>[c,(...u)=>l(...u)(a)]))}};return a}chainableState(t,r){let{selection:n}=t,{doc:o}=t,{storedMarks:i}=t;return Tt(S({},r),{schema:r.schema,plugins:r.plugins,apply:r.apply.bind(r),applyTransaction:r.applyTransaction.bind(r),reconfigure:r.reconfigure.bind(r),toJSON:r.toJSON.bind(r),get storedMarks(){return i},get selection(){return n},get doc(){return o},get tr(){return n=t.selection,o=t.doc,i=t.storedMarks,t}})}}function _(e,t,r={}){return e.config[t]===void 0&&e.parent?_(e.parent,t,r):typeof e.config[t]=="function"?e.config[t].bind(Tt(S({},r),{parent:e.parent?_(e.parent,t,r):null})):e.config[t]}function dr(e){const t=e.filter(o=>o.type==="extension"),r=e.filter(o=>o.type==="node"),n=e.filter(o=>o.type==="mark");return{baseExtensions:t,nodeExtensions:r,markExtensions:n}}function ua(e){const t=[],{nodeExtensions:r,markExtensions:n}=dr(e),o=[...r,...n],i={default:null,rendered:!0,renderHTML:null,parseHTML:null,keepOnSplit:!0};return e.forEach(s=>{const a={name:s.name,options:s.options},c=_(s,"addGlobalAttributes",a);if(!c)return;c().forEach(u=>{u.types.forEach(f=>{Object.entries(u.attributes).forEach(([p,d])=>{t.push({type:f,name:p,attribute:S(S({},i),d)})})})})}),o.forEach(s=>{const a={name:s.name,options:s.options},c=_(s,"addAttributes",a);if(!c)return;const l=c();Object.entries(l).forEach(([u,f])=>{t.push({type:s.name,name:u,attribute:S(S({},i),f)})})}),t}function Ot(...e){return e.filter(t=>!!t).reduce((t,r)=>{const n=S({},t);return Object.entries(r).forEach(([o,i])=>{if(!n[o]){n[o]=i;return}o==="class"?n[o]=[n[o],i].join(" "):o==="style"?n[o]=[n[o],i].join("; "):n[o]=i}),n},{})}function go(e,t){return t.filter(r=>r.attribute.rendered).map(r=>r.attribute.renderHTML?r.attribute.renderHTML(e.attrs)||{}:{[r.name]:e.attrs[r.name]}).reduce((r,n)=>Ot(r,n),{})}function Qu(e={}){return Object.keys(e).length===0&&e.constructor===Object}function Zu(e){return typeof e!="string"?e:e.match(/^\d*(\.\d+)?$/)?Number(e):e==="true"?!0:e==="false"?!1:e}function fa(e,t){return e.style?e:Tt(S({},e),{getAttrs:r=>{const n=e.getAttrs?e.getAttrs(r):e.attrs;if(n===!1)return!1;const o=t.filter(i=>i.attribute.rendered).reduce((i,s)=>{const a=s.attribute.parseHTML?s.attribute.parseHTML(r)||{}:{[s.name]:Zu(r.getAttribute(s.name))},c=Object.fromEntries(Object.entries(a).filter(([,l])=>l!=null));return S(S({},i),c)},{});return S(S({},n),o)}})}function et(e,t=void 0,...r){return typeof e=="function"?t?e.bind(t)(...r):e(...r):e}function pa(e){return Object.fromEntries(Object.entries(e).filter(([t,r])=>t==="attrs"&&Qu(r)?!1:r!=null))}function tf(e){var t;const r=ua(e),{nodeExtensions:n,markExtensions:o}=dr(e),i=(t=n.find(c=>_(c,"topNode")))===null||t===void 0?void 0:t.name,s=Object.fromEntries(n.map(c=>{const l=r.filter(v=>v.type===c.name),u={name:c.name,options:c.options},f=e.reduce((v,g)=>{const M=_(g,"extendNodeSchema",u);return S(S({},v),M?M(c):{})},{}),p=pa(Tt(S({},f),{content:et(_(c,"content",u)),marks:et(_(c,"marks",u)),group:et(_(c,"group",u)),inline:et(_(c,"inline",u)),atom:et(_(c,"atom",u)),selectable:et(_(c,"selectable",u)),draggable:et(_(c,"draggable",u)),code:et(_(c,"code",u)),defining:et(_(c,"defining",u)),isolating:et(_(c,"isolating",u)),attrs:Object.fromEntries(l.map(v=>{var g;return[v.name,{default:(g=v==null?void 0:v.attribute)===null||g===void 0?void 0:g.default}]}))})),d=et(_(c,"parseHTML",u));d&&(p.parseDOM=d.map(v=>fa(v,l)));const h=_(c,"renderHTML",u);return h&&(p.toDOM=v=>h({node:v,HTMLAttributes:go(v,l)})),[c.name,p]})),a=Object.fromEntries(o.map(c=>{const l=r.filter(v=>v.type===c.name),u={name:c.name,options:c.options},f=e.reduce((v,g)=>{const M=_(g,"extendMarkSchema",u);return S(S({},v),M?M(c):{})},{}),p=pa(Tt(S({},f),{inclusive:et(_(c,"inclusive",u)),excludes:et(_(c,"excludes",u)),group:et(_(c,"group",u)),spanning:et(_(c,"spanning",u)),attrs:Object.fromEntries(l.map(v=>{var g;return[v.name,{default:(g=v==null?void 0:v.attribute)===null||g===void 0?void 0:g.default}]}))})),d=et(_(c,"parseHTML",u));d&&(p.parseDOM=d.map(v=>fa(v,l)));const h=_(c,"renderHTML",u);return h&&(p.toDOM=v=>h({mark:v,HTMLAttributes:go(v,l)})),[c.name,p]}));return new Se({topNode:i,nodes:s,marks:a})}function yo(e,t){return t.nodes[e]?t.nodes[e]:t.marks[e]?t.marks[e]:null}class hr{constructor(t,r){this.splittableMarks=[],this.editor=r,this.extensions=hr.resolve(t),this.schema=tf(this.extensions),this.extensions.forEach(n=>{var o;const i={name:n.name,options:n.options,editor:this.editor,type:yo(n.name,this.schema)};n.type==="mark"&&((o=et(_(n,"keepOnSplit",i)))!==null&&o!==void 0?o:!0)&&this.splittableMarks.push(n.name);const s=_(n,"onBeforeCreate",i);s&&this.editor.on("beforeCreate",s);const a=_(n,"onCreate",i);a&&this.editor.on("create",a);const c=_(n,"onUpdate",i);c&&this.editor.on("update",c);const l=_(n,"onSelectionUpdate",i);l&&this.editor.on("selectionUpdate",l);const u=_(n,"onTransaction",i);u&&this.editor.on("transaction",u);const f=_(n,"onFocus",i);f&&this.editor.on("focus",f);const p=_(n,"onBlur",i);p&&this.editor.on("blur",p);const d=_(n,"onDestroy",i);d&&this.editor.on("destroy",d)})}static resolve(t){return hr.sort(hr.flatten(t))}static flatten(t){return t.map(r=>{const n={name:r.name,options:r.options},o=_(r,"addExtensions",n);return o?[r,...this.flatten(o())]:r}).flat(10)}static sort(t){const r=100;return t.sort((n,o)=>{const i=_(n,"priority")||r,s=_(o,"priority")||r;return i>s?-1:i{const n={name:r.name,options:r.options,editor:this.editor,type:yo(r.name,this.schema)},o=_(r,"addCommands",n);return o?S(S({},t),o()):t},{})}get plugins(){return[...this.extensions].reverse().map(t=>{const r={name:t.name,options:t.options,editor:this.editor,type:yo(t.name,this.schema)},n=[],o=_(t,"addKeyboardShortcuts",r);if(o){const c=Object.fromEntries(Object.entries(o()).map(([u,f])=>[u,()=>f({editor:this.editor})])),l=Au(c);n.push(l)}const i=_(t,"addInputRules",r);if(this.editor.options.enableInputRules&&i){const c=i(),l=c.length?[Eu({rules:c})]:[];n.push(...l)}const s=_(t,"addPasteRules",r);if(this.editor.options.enablePasteRules&&s){const c=s();n.push(...c)}const a=_(t,"addProseMirrorPlugins",r);if(a){const c=a();n.push(...c)}return n}).flat()}get attributes(){return ua(this.extensions)}get nodeViews(){const{editor:t}=this,{nodeExtensions:r}=dr(this.extensions);return Object.fromEntries(r.filter(n=>!!_(n,"addNodeView")).map(n=>{const o=this.attributes.filter(c=>c.type===n.name),i={name:n.name,options:n.options,editor:t,type:tt(n.name,this.schema)},s=_(n,"addNodeView",i);if(!s)return[];const a=(c,l,u,f)=>{const p=go(c,o);return s()({editor:t,node:c,getPos:u,decorations:f,HTMLAttributes:p,extension:n})};return[n.name,a]}))}get textSerializers(){const{editor:t}=this,{nodeExtensions:r}=dr(this.extensions);return Object.fromEntries(r.filter(n=>!!_(n,"renderText")).map(n=>{const o={name:n.name,options:n.options,editor:t,type:tt(n.name,this.schema)},i=_(n,"renderText",o);if(!i)return[];const s=a=>i(a);return[n.name,s]}))}}class ef{constructor(){this.callbacks={}}on(t,r){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(r),this}emit(t,...r){const n=this.callbacks[t];return n&&n.forEach(o=>o.apply(this,r)),this}off(t,r){const n=this.callbacks[t];return n&&(r?this.callbacks[t]=n.filter(o=>o!==r):delete this.callbacks[t]),this}removeAllListeners(){this.callbacks={}}}function rf(e){return Object.prototype.toString.call(e).slice(8,-1)}function bo(e){return rf(e)!=="Object"?!1:e.constructor===Object&&Object.getPrototypeOf(e)===Object.prototype}function Wr(e,t){const r=S({},e);return bo(e)&&bo(t)&&Object.keys(t).forEach(n=>{bo(t[n])?n in e?r[n]=Wr(e[n],t[n]):Object.assign(r,{[n]:t[n]}):Object.assign(r,{[n]:t[n]})}),r}class St{constructor(t={}){this.type="extension",this.name="extension",this.parent=null,this.child=null,this.config={name:this.name,defaultOptions:{}},this.config=S(S({},this.config),t),this.name=this.config.name,this.options=this.config.defaultOptions}static create(t={}){return new St(t)}configure(t={}){const r=this.extend();return r.options=Wr(this.options,t),r}extend(t={}){const r=new St(t);return r.parent=this,this.child=r,r.name=t.name?t.name:r.parent.name,r.options=t.defaultOptions?t.defaultOptions:r.parent.options,r}}const nf=(e,t,r,n,o)=>{let i="",s=!0;return e.state.doc.nodesBetween(t,r,(a,c)=>{var l;const u=e.extensionManager.textSerializers[a.type.name];u?(i+=u({node:a}),s=!n):a.isText?(i+=(l=a==null?void 0:a.text)===null||l===void 0?void 0:l.slice(Math.max(t,c)-c,r-c),s=!n):a.isLeaf&&o?(i+=o,s=!n):!s&&a.isBlock&&(i+=n,s=!0)},0),i},of=St.create({name:"editable",addProseMirrorPlugins(){return[new Rt({key:new Wt("clipboardTextSerializer"),props:{clipboardTextSerializer:()=>{const{editor:e}=this,{from:t,to:r}=e.state.selection;return nf(e,t,r,` +`)}}})]}}),sf=()=>({editor:e,view:t})=>(requestAnimationFrame(()=>{e.isDestroyed||t.dom.blur()}),!0);var af=Object.freeze({__proto__:null,blur:sf});const cf=(e=!1)=>({commands:t})=>t.setContent("",e);var lf=Object.freeze({__proto__:null,clearContent:cf});const uf=()=>({state:e,tr:t,dispatch:r})=>{const{selection:n}=t,{ranges:o}=n;return o.forEach(i=>{e.doc.nodesBetween(i.$from.pos,i.$to.pos,(s,a)=>{if(s.type.isText)return;const c=t.doc.resolve(t.mapping.map(a)),l=t.doc.resolve(t.mapping.map(a+s.nodeSize)),u=c.blockRange(l);if(!u)return;const f=Pe(u);if(s.type.isTextblock&&r){const{defaultType:p}=c.parent.contentMatchAt(c.index());t.setNodeMarkup(u.start,p)}(f||f===0)&&r&&t.lift(u,f)})}),!0};var ff=Object.freeze({__proto__:null,clearNodes:uf});const pf=e=>t=>e(t);var df=Object.freeze({__proto__:null,command:pf});const hf=()=>({state:e,dispatch:t})=>Zs(e,t);var mf=Object.freeze({__proto__:null,createParagraphNear:hf});const vf=e=>({tr:t,state:r,dispatch:n})=>{const o=tt(e,r.schema),i=t.selection.$anchor;for(let s=i.depth;s>0;s-=1)if(i.node(s).type===o){if(n){const c=i.before(s),l=i.after(s);t.delete(c,l).scrollIntoView()}return!0}return!1};var gf=Object.freeze({__proto__:null,deleteNode:vf});const yf=e=>({tr:t,dispatch:r})=>{const{from:n,to:o}=e;return r&&t.delete(n,o),!0};var bf=Object.freeze({__proto__:null,deleteRange:yf});const kf=()=>({state:e,dispatch:t})=>po(e,t);var Sf=Object.freeze({__proto__:null,deleteSelection:kf});const Mf=()=>({commands:e})=>e.keyboardShortcut("Enter");var xf=Object.freeze({__proto__:null,enter:Mf});const Cf=()=>({state:e,dispatch:t})=>Qs(e,t);var Of=Object.freeze({__proto__:null,exitCode:Cf});function ko(e,t,r={}){return e.find(n=>n.type===t&&pr(n.attrs,r))}function wf(e,t,r={}){return!!ko(e,t,r)}function da(e,t,r={}){if(!e||!t)return;const n=e.parent.childAfter(e.parentOffset);if(!n.node)return;const o=ko(n.node.marks,t,r);if(!o)return;let i=e.index(),s=e.start()+n.offset,a=i+1,c=s+n.node.nodeSize;for(ko(n.node.marks,t,r);i>0&&o.isInSet(e.parent.child(i-1).marks);)i-=1,s-=e.parent.child(i).nodeSize;for(;a({tr:r,state:n,dispatch:o})=>{const i=he(e,n.schema),{doc:s,selection:a}=r,{$from:c,from:l,to:u}=a;if(o){const f=da(c,i,t);if(f&&f.from<=l&&f.to>=u){const p=H.create(s,f.from,f.to);r.setSelection(p)}}return!0};var Af=Object.freeze({__proto__:null,extendMarkRange:Tf});const _f=e=>t=>{const r=typeof e=="function"?e(t):e;for(let n=0;n({editor:t,view:r,tr:n,dispatch:o})=>{const i=()=>{requestAnimationFrame(()=>{t.isDestroyed||r.focus()})};if(r.hasFocus()&&e===null||e===!1)return!0;if(o&&e===null&&!ma(t.state.selection))return i(),!0;const{from:s,to:a}=Df(t.state,e)||t.state.selection,{doc:c,storedMarks:l}=n,u=D.atStart(c).from,f=D.atEnd(c).to,p=mr(s,u,f),d=mr(a,u,f),h=H.create(c,p,d),v=t.state.selection.eq(h);return o&&(v||n.setSelection(h),v&&l&&n.setStoredMarks(l),i()),!0};var Rf=Object.freeze({__proto__:null,focus:If});const Pf=(e,t)=>r=>e.every((n,o)=>t(n,Tt(S({},r),{index:o})));var Bf=Object.freeze({__proto__:null,forEach:Pf});const zf=(e,t)=>({tr:r,commands:n})=>n.insertContentAt({from:r.selection.from,to:r.selection.to},e,t);var Lf=Object.freeze({__proto__:null,insertContent:zf});function Ff(e,t,r){const n=e.steps.length-1;if(n{s===0&&(s=u)}),e.setSelection(D.near(e.doc.resolve(s),r))}const Vf=(e,t,r)=>({tr:n,dispatch:o,editor:i})=>{if(o){const s=Jr(t,i.schema,S({parseOptions:{preserveWhitespace:"full"}},r||{}));if(s.toString()==="<>")return!0;const{from:a,to:c}=typeof e=="number"?{from:e,to:e}:e;n.replaceWith(a,c,s),Ff(n,n.steps.length-1,1)}return!0};var Hf=Object.freeze({__proto__:null,insertContentAt:Vf});const jf=()=>({state:e,dispatch:t})=>Js(e,t);var qf=Object.freeze({__proto__:null,joinBackward:jf});const Jf=()=>({state:e,dispatch:t})=>$s(e,t);var Wf=Object.freeze({__proto__:null,joinForward:Jf});const Kf=typeof navigator!="undefined"?/Mac/.test(navigator.platform):!1;function $f(e){const t=e.split(/-(?!$)/);let r=t[t.length-1];r==="Space"&&(r=" ");let n,o,i,s;for(let a=0;a({editor:t,view:r,tr:n,dispatch:o})=>{const i=$f(e).split(/-(?!$)/),s=i.find(l=>!["Alt","Ctrl","Meta","Shift"].includes(l)),a=new KeyboardEvent("keydown",{key:s==="Space"?" ":s,altKey:i.includes("Alt"),ctrlKey:i.includes("Ctrl"),metaKey:i.includes("Meta"),shiftKey:i.includes("Shift"),bubbles:!0,cancelable:!0}),c=t.captureTransaction(()=>{r.someProp("handleKeyDown",l=>l(r,a))});return c==null||c.steps.forEach(l=>{const u=l.map(n.mapping);u&&o&&n.maybeStep(u)}),!0};var Gf=Object.freeze({__proto__:null,keyboardShortcut:Uf});const Yf=(e,t={})=>({state:r,dispatch:n})=>{const o=tt(e,r.schema);return je(r,o,t)?Ys(r,n):!1};var Xf=Object.freeze({__proto__:null,lift:Yf});const Qf=()=>({state:e,dispatch:t})=>ta(e,t);var Zf=Object.freeze({__proto__:null,liftEmptyBlock:Qf});const tp=e=>({state:t,dispatch:r})=>{const n=tt(e,t.schema);return Vu(n)(t,r)};var ep=Object.freeze({__proto__:null,liftListItem:tp});const rp=()=>({state:e,dispatch:t})=>Xs(e,t);var np=Object.freeze({__proto__:null,newlineInCode:rp});function va(e,t){const r=typeof t=="string"?[t]:t;return Object.keys(e).reduce((n,o)=>(r.includes(o)||(n[o]=e[o]),n),{})}const op=(e,t)=>({tr:r,state:n,dispatch:o})=>{let i=null,s=null;const a=qr(typeof e=="string"?e:e.name,n.schema);return a?(a==="node"&&(i=tt(e,n.schema)),a==="mark"&&(s=he(e,n.schema)),o&&r.selection.ranges.forEach(c=>{n.doc.nodesBetween(c.$from.pos,c.$to.pos,(l,u)=>{i&&i===l.type&&r.setNodeMarkup(u,void 0,va(l.attrs,t)),s&&l.marks.length&&l.marks.forEach(f=>{s===f.type&&r.addMark(u,u+l.nodeSize,s.create(va(f.attrs,t)))})})}),!0):!1};var ip=Object.freeze({__proto__:null,resetAttributes:op});const sp=()=>({tr:e,dispatch:t})=>(t&&e.scrollIntoView(),!0);var ap=Object.freeze({__proto__:null,scrollIntoView:sp});const cp=()=>({state:e,dispatch:t})=>ea(e,t);var lp=Object.freeze({__proto__:null,selectAll:cp});const up=()=>({state:e,dispatch:t})=>Ws(e,t);var fp=Object.freeze({__proto__:null,selectNodeBackward:up});const pp=()=>({state:e,dispatch:t})=>Us(e,t);var dp=Object.freeze({__proto__:null,selectNodeForward:pp});const hp=()=>({state:e,dispatch:t})=>Ru(e,t);var mp=Object.freeze({__proto__:null,selectParentNode:hp});const vp=(e,t=!1,r={})=>({tr:n,editor:o,dispatch:i})=>{const{doc:s}=n,a=la(e,o.schema,r),c=H.create(s,0,s.content.size);return i&&n.setSelection(c).replaceSelectionWith(a,!1).setMeta("preventUpdate",!t),!0};var gp=Object.freeze({__proto__:null,setContent:vp});const yp=(e,t={})=>({tr:r,state:n,dispatch:o})=>{const{selection:i}=r,{empty:s,ranges:a}=i,c=he(e,n.schema);if(o)if(s){const l=aa(n,c);r.addStoredMark(c.create(S(S({},l),t)))}else a.forEach(l=>{const u=l.$from.pos,f=l.$to.pos;n.doc.nodesBetween(u,f,(p,d)=>{const h=Math.max(d,u),v=Math.min(d+p.nodeSize,f);p.marks.find(M=>M.type===c)?p.marks.forEach(M=>{c===M.type&&r.addMark(h,v,c.create(S(S({},M.attrs),t)))}):r.addMark(h,v,c.create(t))})});return!0};var bp=Object.freeze({__proto__:null,setMark:yp});const kp=(e,t)=>({tr:r})=>(r.setMeta(e,t),!0);var Sp=Object.freeze({__proto__:null,setMeta:kp});const Mp=(e,t={})=>({state:r,dispatch:n})=>{const o=tt(e,r.schema);return Bu(o,t)(r,n)};var xp=Object.freeze({__proto__:null,setNode:Mp});const Cp=e=>({tr:t,dispatch:r})=>{if(r){const{doc:n}=t,o=D.atStart(n).from,i=D.atEnd(n).to,s=mr(e,o,i),a=E.create(n,s);t.setSelection(a)}return!0};var Op=Object.freeze({__proto__:null,setNodeSelection:Cp});const wp=e=>({tr:t,dispatch:r})=>{if(r){const{doc:n}=t,{from:o,to:i}=typeof e=="number"?{from:e,to:e}:e,s=D.atStart(n).from,a=D.atEnd(n).to,c=mr(o,s,a),l=mr(i,s,a),u=H.create(n,c,l);t.setSelection(u)}return!0};var Tp=Object.freeze({__proto__:null,setTextSelection:wp});const Ap=e=>({state:t,dispatch:r})=>{const n=tt(e,t.schema);return qu(n)(t,r)};var _p=Object.freeze({__proto__:null,sinkListItem:Ap});function Kr(e,t,r){return Object.fromEntries(Object.entries(r).filter(([n])=>{const o=e.find(i=>i.type===t&&i.name===n);return o?o.attribute.keepOnSplit:!1}))}function Np(e){for(let t=0;tt==null?void 0:t.includes(o.type.name));e.tr.ensureMarks(n)}}const Ep=({keepMarks:e=!0}={})=>({tr:t,state:r,dispatch:n,editor:o})=>{const{selection:i,doc:s}=t,{$from:a,$to:c}=i,l=o.extensionManager.attributes,u=Kr(l,a.node().type.name,a.node().attrs);if(i instanceof E&&i.node.isBlock)return!a.parentOffset||!ee(s,a.pos)?!1:(n&&(e&&ga(r,o.extensionManager.splittableMarks),t.split(a.pos).scrollIntoView()),!0);if(!a.parent.isBlock)return!1;if(n){const f=c.parentOffset===c.parent.content.size;i instanceof H&&t.deleteSelection();const p=a.depth===0?void 0:Np(a.node(-1).contentMatchAt(a.indexAfter(-1)));let d=f&&p?[{type:p,attrs:u}]:void 0,h=ee(t.doc,t.mapping.map(a.pos),1,d);if(!d&&!h&&ee(t.doc,t.mapping.map(a.pos),1,p?[{type:p}]:void 0)&&(h=!0,d=p?[{type:p,attrs:u}]:void 0),h&&(t.split(t.mapping.map(a.pos),1,d),p&&!f&&!a.parentOffset&&a.parent.type!==p)){const v=t.mapping.map(a.before()),g=t.doc.resolve(v);a.node(-1).canReplaceWith(g.index(),g.index()+1,p)&&t.setNodeMarkup(t.mapping.map(a.before()),p)}e&&ga(r,o.extensionManager.splittableMarks),t.scrollIntoView()}return!0};var Dp=Object.freeze({__proto__:null,splitBlock:Ep});const Ip=e=>({tr:t,state:r,dispatch:n,editor:o})=>{var i;const s=tt(e,r.schema),{$from:a,$to:c}=r.selection,l=r.selection.node;if(l&&l.isBlock||a.depth<2||!a.sameParent(c))return!1;const u=a.node(-1);if(u.type!==s)return!1;const f=o.extensionManager.attributes;if(a.parent.content.size===0&&a.node(-1).childCount===a.indexAfter(-1)){if(a.depth===2||a.node(-3).type!==s||a.index(-2)!==a.node(-2).childCount-1)return!1;if(n){let g=k.empty;const M=a.index(-1)?1:a.index(-2)?2:3;for(let F=a.depth-M;F>=a.depth-3;F-=1)g=k.from(a.node(F).copy(g));const y=a.indexAfter(-1){if(O>-1)return!1;F.isTextblock&&F.content.size===0&&(O=J+1)}),O>-1&&t.setSelection(H.near(t.doc.resolve(O))),t.scrollIntoView()}return!0}const p=c.pos===a.end()?u.contentMatchAt(0).defaultType:null,d=Kr(f,u.type.name,u.attrs),h=Kr(f,a.node().type.name,a.node().attrs);t.delete(a.pos,c.pos);const v=p?[{type:s,attrs:d},{type:p,attrs:h}]:[{type:s,attrs:d}];return ee(t.doc,a.pos,2)?(n&&t.split(a.pos,2,v).scrollIntoView(),!0):!1};var Rp=Object.freeze({__proto__:null,splitListItem:Ip});function Pp(e,t){for(let r=e.depth;r>0;r-=1){const n=e.node(r);if(t(n))return{pos:r>0?e.before(r):0,start:e.start(r),depth:r,node:n}}}function Bp(e){return t=>Pp(t.$from,e)}function ya(e,t){const{nodeExtensions:r}=dr(t),n=r.find(s=>s.name===e);if(!n)return!1;const o={name:n.name,options:n.options},i=et(_(n,"group",o));return typeof i!="string"?!1:i.split(" ").includes("list")}const zp=(e,t)=>({editor:r,tr:n,state:o,dispatch:i,chain:s,commands:a,can:c})=>{const{extensions:l}=r.extensionManager,u=tt(e,o.schema),f=tt(t,o.schema),{selection:p}=o,{$from:d,$to:h}=p,v=d.blockRange(h);if(!v)return!1;const g=Bp(y=>ya(y.type.name,l))(p);if(v.depth>=1&&g&&v.depth-g.depth<=1){if(g.node.type===u)return a.liftListItem(f);if(ya(g.node.type.name,l)&&u.validContent(g.node.content)&&i)return n.setNodeMarkup(g.pos,u),!0}return c().wrapInList(u)?a.wrapInList(u):s().clearNodes().wrapInList(u).run()};var Lp=Object.freeze({__proto__:null,toggleList:zp});const Fp=(e,t={})=>({state:r,commands:n})=>{const o=he(e,r.schema);return vo(r,o,t)?n.unsetMark(o):n.setMark(o,t)};var Vp=Object.freeze({__proto__:null,toggleMark:Fp});const Hp=(e,t,r={})=>({state:n,commands:o})=>{const i=tt(e,n.schema),s=tt(t,n.schema);return je(n,i,r)?o.setNode(s):o.setNode(i,r)};var jp=Object.freeze({__proto__:null,toggleNode:Hp});const qp=(e,t={})=>({state:r,dispatch:n})=>{const o=tt(e,r.schema);return je(r,o,t)?Ys(r,n):na(o,t)(r,n)};var Jp=Object.freeze({__proto__:null,toggleWrap:qp});const Wp=()=>({state:e,dispatch:t})=>Du(e,t);var Kp=Object.freeze({__proto__:null,undoInputRule:Wp});const $p=()=>({tr:e,state:t,dispatch:r})=>{const{selection:n}=e,{empty:o,ranges:i}=n;return o||r&&Object.entries(t.schema.marks).forEach(([,s])=>{i.forEach(a=>{e.removeMark(a.$from.pos,a.$to.pos,s)})}),!0};var Up=Object.freeze({__proto__:null,unsetAllMarks:$p});const Gp=e=>({tr:t,state:r,dispatch:n})=>{const{selection:o}=t,i=he(e,r.schema),{$from:s,empty:a,ranges:c}=o;if(n){if(a){let{from:l,to:u}=o;const f=da(s,i);f&&(l=f.from,u=f.to),t.removeMark(l,u,i)}else c.forEach(l=>{t.removeMark(l.$from.pos,l.$to.pos,i)});t.removeStoredMark(i)}return!0};var Yp=Object.freeze({__proto__:null,unsetMark:Gp});const Xp=(e,t={})=>({tr:r,state:n,dispatch:o})=>{let i=null,s=null;const a=qr(typeof e=="string"?e:e.name,n.schema);return a?(a==="node"&&(i=tt(e,n.schema)),a==="mark"&&(s=he(e,n.schema)),o&&r.selection.ranges.forEach(c=>{const l=c.$from.pos,u=c.$to.pos;n.doc.nodesBetween(l,u,(f,p)=>{i&&i===f.type&&r.setNodeMarkup(p,void 0,S(S({},f.attrs),t)),s&&f.marks.length&&f.marks.forEach(d=>{if(s===d.type){const h=Math.max(p,l),v=Math.min(p+f.nodeSize,u);r.addMark(h,v,s.create(S(S({},d.attrs),t)))}})})}),!0):!1};var Qp=Object.freeze({__proto__:null,updateAttributes:Xp});const Zp=(e,t={})=>({state:r,dispatch:n})=>{const o=tt(e,r.schema);return je(r,o,t)?!1:na(o,t)(r,n)};var td=Object.freeze({__proto__:null,wrapIn:Zp});const ed=(e,t={})=>({state:r,dispatch:n})=>{const o=tt(e,r.schema);return Lu(o,t)(r,n)};var rd=Object.freeze({__proto__:null,wrapInList:ed});const nd=St.create({name:"commands",addCommands(){return S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S(S({},af),lf),ff),df),mf),gf),bf),Sf),xf),Of),Af),Nf),Rf),Bf),Lf),Hf),qf),Wf),Gf),Xf),Zf),ep),np),ip),ap),lp),fp),dp),mp),gp),bp),Sp),xp),Op),Tp),_p),Dp),Rp),Lp),Vp),jp),Jp),Kp),Up),Yp),Qp),td),rd)}}),od=St.create({name:"editable",addProseMirrorPlugins(){return[new Rt({key:new Wt("editable"),props:{editable:()=>this.editor.options.editable}})]}}),id=St.create({name:"focusEvents",addProseMirrorPlugins(){const{editor:e}=this;return[new Rt({key:new Wt("focusEvents"),props:{attributes:{tabindex:"0"},handleDOMEvents:{focus:(t,r)=>{e.isFocused=!0;const n=e.state.tr.setMeta("focus",{event:r}).setMeta("addToHistory",!1);return t.dispatch(n),!1},blur:(t,r)=>{e.isFocused=!1;const n=e.state.tr.setMeta("blur",{event:r}).setMeta("addToHistory",!1);return t.dispatch(n),!1}}}})]}}),sd=St.create({name:"keymap",addKeyboardShortcuts(){const e=()=>this.editor.commands.first(({commands:r})=>[()=>r.undoInputRule(),()=>r.deleteSelection(),()=>r.joinBackward(),()=>r.selectNodeBackward()]),t=()=>this.editor.commands.first(({commands:r})=>[()=>r.deleteSelection(),()=>r.joinForward(),()=>r.selectNodeForward()]);return{Enter:()=>this.editor.commands.first(({commands:r})=>[()=>r.newlineInCode(),()=>r.createParagraphNear(),()=>r.liftEmptyBlock(),()=>r.splitBlock()]),"Mod-Enter":()=>this.editor.commands.exitCode(),Backspace:()=>e(),"Mod-Backspace":()=>e(),Delete:()=>t(),"Mod-Delete":()=>t()}}});var ad=Object.freeze({__proto__:null,ClipboardTextSerializer:of,Commands:nd,Editable:od,FocusEvents:id,Keymap:sd});const cd=`.ProseMirror { + position: relative; +} + +.ProseMirror { + word-wrap: break-word; + white-space: pre-wrap; + -webkit-font-variant-ligatures: none; + font-variant-ligatures: none; +} + +.ProseMirror [contenteditable="false"] { + white-space: normal; +} + +.ProseMirror [contenteditable="false"] [contenteditable="true"] { + white-space: pre-wrap; +} + +.ProseMirror pre { + white-space: pre-wrap; +} + +.ProseMirror-gapcursor { + display: none; + pointer-events: none; + position: absolute; +} + +.ProseMirror-gapcursor:after { + content: ""; + display: block; + position: absolute; + top: -2px; + width: 20px; + border-top: 1px solid black; + animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite; +} + +@keyframes ProseMirror-cursor-blink { + to { + visibility: hidden; + } +} + +.ProseMirror-hideselection *::selection { + background: transparent; +} + +.ProseMirror-hideselection *::-moz-selection { + background: transparent; +} + +.ProseMirror-hideselection * { + caret-color: transparent; +} + +.ProseMirror-focused .ProseMirror-gapcursor { + display: block; +} + +.tippy-box[data-animation=fade][data-state=hidden] { + opacity: 0 +}`;class ld extends ef{constructor(t={}){super();this.isFocused=!1,this.options={element:document.createElement("div"),content:"",injectCSS:!0,extensions:[],autofocus:!1,editable:!0,editorProps:{},parseOptions:{},enableInputRules:!0,enablePasteRules:!0,onBeforeCreate:()=>null,onCreate:()=>null,onUpdate:()=>null,onSelectionUpdate:()=>null,onTransaction:()=>null,onFocus:()=>null,onBlur:()=>null,onDestroy:()=>null},this.isCapturingTransaction=!1,this.capturedTransaction=null,this.setOptions(t),this.createExtensionManager(),this.createCommandManager(),this.createSchema(),this.on("beforeCreate",this.options.onBeforeCreate),this.emit("beforeCreate",{editor:this}),this.createView(),this.injectCSS(),this.on("create",this.options.onCreate),this.on("update",this.options.onUpdate),this.on("selectionUpdate",this.options.onSelectionUpdate),this.on("transaction",this.options.onTransaction),this.on("focus",this.options.onFocus),this.on("blur",this.options.onBlur),this.on("destroy",this.options.onDestroy),window.setTimeout(()=>{this.isDestroyed||(this.commands.focus(this.options.autofocus),this.emit("create",{editor:this}))},0)}get commands(){return this.commandManager.createCommands()}chain(){return this.commandManager.createChain()}can(){return this.commandManager.createCan()}injectCSS(){this.options.injectCSS&&document&&(this.css=Yu(cd))}setOptions(t={}){this.options=S(S({},this.options),t),!(!this.view||!this.state||this.isDestroyed)&&(this.options.editorProps&&this.view.setProps(this.options.editorProps),this.view.updateState(this.state))}setEditable(t){this.setOptions({editable:t})}get isEditable(){return this.options.editable&&this.view&&this.view.editable}get state(){return this.view.state}registerPlugin(t,r){const n=typeof r=="function"?r(t,this.state.plugins):[...this.state.plugins,t],o=this.state.reconfigure({plugins:n});this.view.updateState(o)}unregisterPlugin(t){if(this.isDestroyed)return;const r=typeof t=="string"?`${t}$`:t.key,n=this.state.reconfigure({plugins:this.state.plugins.filter(o=>!o.key.startsWith(r))});this.view.updateState(n)}createExtensionManager(){const r=[...Object.entries(ad).map(([,n])=>n),...this.options.extensions].filter(n=>["extension","node","mark"].includes(n==null?void 0:n.type));this.extensionManager=new hr(r,this)}createCommandManager(){this.commandManager=new Xu(this,this.extensionManager.commands)}createSchema(){this.schema=this.extensionManager.schema}createView(){this.view=new Z(this.options.element,Tt(S({},this.options.editorProps),{dispatchTransaction:this.dispatchTransaction.bind(this),state:mt.create({doc:la(this.options.content,this.schema,this.options.parseOptions)})}));const t=this.state.reconfigure({plugins:this.extensionManager.plugins});this.view.updateState(t),this.createNodeViews();const r=this.view.dom;r.editor=this}createNodeViews(){this.view.setProps({nodeViews:this.extensionManager.nodeViews})}captureTransaction(t){this.isCapturingTransaction=!0,t(),this.isCapturingTransaction=!1;const r=this.capturedTransaction;return this.capturedTransaction=null,r}dispatchTransaction(t){if(this.isCapturingTransaction){if(!this.capturedTransaction){this.capturedTransaction=t;return}t.steps.forEach(s=>{var a;return(a=this.capturedTransaction)===null||a===void 0?void 0:a.step(s)});return}const r=this.state.apply(t),n=!this.state.selection.eq(r.selection);this.view.updateState(r),this.emit("transaction",{editor:this,transaction:t}),n&&this.emit("selectionUpdate",{editor:this,transaction:t});const o=t.getMeta("focus"),i=t.getMeta("blur");o&&this.emit("focus",{editor:this,event:o.event,transaction:t}),i&&this.emit("blur",{editor:this,event:i.event,transaction:t}),!(!t.docChanged||t.getMeta("preventUpdate"))&&this.emit("update",{editor:this,transaction:t})}getAttributes(t){return Wu(this.state,t)}isActive(t,r){const n=typeof t=="string"?t:null,o=typeof t=="string"?r:t;return Ku(this.state,n,o)}getJSON(){return this.state.doc.toJSON()}getHTML(){return Uu(this.state.doc,this.schema)}get isEmpty(){return Gu(this.state.doc)}getCharacterCount(){return this.state.doc.content.size-2}destroy(){this.emit("destroy"),this.view&&this.view.destroy(),this.removeAllListeners(),$u(this.css)}get isDestroyed(){var t;return!((t=this.view)===null||t===void 0?void 0:t.docView)}}class wt{constructor(t={}){this.type="node",this.name="node",this.parent=null,this.child=null,this.config={name:this.name,defaultOptions:{}},this.config=S(S({},this.config),t),this.name=this.config.name,this.options=this.config.defaultOptions}static create(t={}){return new wt(t)}configure(t={}){const r=this.extend();return r.options=Wr(this.options,t),r}extend(t={}){const r=new wt(t);return r.parent=this,this.child=r,r.name=t.name?t.name:r.parent.name,r.options=t.defaultOptions?t.defaultOptions:r.parent.options,r}}class Te{constructor(t={}){this.type="mark",this.name="mark",this.parent=null,this.child=null,this.config={name:this.name,defaultOptions:{}},this.config=S(S({},this.config),t),this.name=this.config.name,this.options=this.config.defaultOptions}static create(t={}){return new Te(t)}configure(t={}){const r=this.extend();return r.options=Wr(this.options,t),r}extend(t={}){const r=new Te(t);return r.parent=this,this.child=r,r.name=t.name?t.name:r.parent.name,r.options=t.defaultOptions?t.defaultOptions:r.parent.options,r}}function ud(e,t,r){return new Ut(e,(n,o,i,s)=>{const a=r instanceof Function?r(o):r,{tr:c}=n;return o[0]&&c.replaceWith(i-1,s,t.create(a)),c})}function fd(e,t,r){let n=[];return r.doc.nodesBetween(e,t,(o,i)=>{n=[...n,...o.marks.map(s=>({from:i,to:i+o.nodeSize,mark:s}))]}),n}function qe(e,t,r){return new Ut(e,(n,o,i,s)=>{const a=r instanceof Function?r(o):r,{tr:c}=n,l=o[o.length-1],u=o[0];let f=s;if(l){const p=u.search(/\S/),d=i+u.indexOf(l),h=d+l.length;if(fd(i,s,n).filter(g=>{const{excluded:M}=g.mark.type;return M.find(y=>y.name===t.name)}).filter(g=>g.to>d).length)return null;hi&&c.delete(i+p,d),f=i+p+l.length,c.addMark(i+p,f,t.create(a)),c.removeStoredMark(t)}return c})}function Je(e,t,r){const n=(o,i)=>{const s=[];return o.forEach(a=>{if(a.isText&&a.text){const{text:c}=a;let l=0,u;for(;(u=e.exec(c))!==null;){const f=Math.max(u.length-2,0),p=Math.max(u.length-1,0);if(i==null?void 0:i.type.allowsMarkType(t)){const h=u.index+u[0].indexOf(u[f]),v=h+u[f].length,g=h+u[f].lastIndexOf(u[p]),M=g+u[p].length,y=r instanceof Function?r(u):r;if(!y&&y!==void 0)continue;h>0&&s.push(a.cut(l,h)),s.push(a.cut(g,M).mark(t.create(y).addToSet(a.marks))),l=v}}lnew C(n(o.content),o.openStart,o.openEnd)}})}function pd(e){return ha(e)&&e instanceof E}function ba(e,t,r){const n=e.coordsAtPos(t),o=e.coordsAtPos(r,-1),i=Math.min(n.top,o.top),s=Math.max(n.bottom,o.bottom),a=Math.min(n.left,o.left),c=Math.max(n.right,o.right),l=c-a,u=s-i,d={top:i,bottom:s,left:a,right:c,width:l,height:u,x:a,y:i};return Tt(S({},d),{toJSON:()=>d})}var dd="tippy-box",ka="tippy-content",hd="tippy-backdrop",Sa="tippy-arrow",Ma="tippy-svg-arrow",Ae={passive:!0,capture:!0};function So(e,t,r){if(Array.isArray(e)){var n=e[t];return n==null?Array.isArray(r)?r[t]:r:n}return e}function Mo(e,t){var r={}.toString.call(e);return r.indexOf("[object")===0&&r.indexOf(t+"]")>-1}function xa(e,t){return typeof e=="function"?e.apply(void 0,t):e}function Ca(e,t){if(t===0)return e;var r;return function(n){clearTimeout(r),r=setTimeout(function(){e(n)},t)}}function md(e){return e.split(/\s+/).filter(Boolean)}function vr(e){return[].concat(e)}function Oa(e,t){e.indexOf(t)===-1&&e.push(t)}function vd(e){return e.filter(function(t,r){return e.indexOf(t)===r})}function gd(e){return e.split("-")[0]}function $r(e){return[].slice.call(e)}function yd(e){return Object.keys(e).reduce(function(t,r){return e[r]!==void 0&&(t[r]=e[r]),t},{})}function gr(){return document.createElement("div")}function Ur(e){return["Element","Fragment"].some(function(t){return Mo(e,t)})}function bd(e){return Mo(e,"NodeList")}function kd(e){return Mo(e,"MouseEvent")}function Sd(e){return!!(e&&e._tippy&&e._tippy.reference===e)}function Md(e){return Ur(e)?[e]:bd(e)?$r(e):Array.isArray(e)?e:$r(document.querySelectorAll(e))}function xo(e,t){e.forEach(function(r){r&&(r.style.transitionDuration=t+"ms")})}function wa(e,t){e.forEach(function(r){r&&r.setAttribute("data-state",t)})}function xd(e){var t,r=vr(e),n=r[0];return(n==null||(t=n.ownerDocument)==null?void 0:t.body)?n.ownerDocument:document}function Cd(e,t){var r=t.clientX,n=t.clientY;return e.every(function(o){var i=o.popperRect,s=o.popperState,a=o.props,c=a.interactiveBorder,l=gd(s.placement),u=s.modifiersData.offset;if(!u)return!0;var f=l==="bottom"?u.top.y:0,p=l==="top"?u.bottom.y:0,d=l==="right"?u.left.x:0,h=l==="left"?u.right.x:0,v=i.top-n+f>c,g=n-i.bottom-p>c,M=i.left-r+d>c,y=r-i.right-h>c;return v||g||M||y})}function Co(e,t,r){var n=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach(function(o){e[n](o,r)})}var Gt={isTouch:!1},Ta=0;function Od(){Gt.isTouch||(Gt.isTouch=!0,window.performance&&document.addEventListener("mousemove",Aa))}function Aa(){var e=performance.now();e-Ta<20&&(Gt.isTouch=!1,document.removeEventListener("mousemove",Aa)),Ta=e}function wd(){var e=document.activeElement;if(Sd(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}function Td(){document.addEventListener("touchstart",Od,Ae),window.addEventListener("blur",wd)}var Ad=typeof window!="undefined"&&typeof document!="undefined",_d=Ad?navigator.userAgent:"",Nd=/MSIE |Trident\//.test(_d),Ed={animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},Dd={allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999},Ht=Object.assign({appendTo:function(){return document.body},aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},Ed,{},Dd),Id=Object.keys(Ht),Rd=function(t){var r=Object.keys(t);r.forEach(function(n){Ht[n]=t[n]})};function _a(e){var t=e.plugins||[],r=t.reduce(function(n,o){var i=o.name,s=o.defaultValue;return i&&(n[i]=e[i]!==void 0?e[i]:s),n},{});return Object.assign({},e,{},r)}function Pd(e,t){var r=t?Object.keys(_a(Object.assign({},Ht,{plugins:t}))):Id,n=r.reduce(function(o,i){var s=(e.getAttribute("data-tippy-"+i)||"").trim();if(!s)return o;if(i==="content")o[i]=s;else try{o[i]=JSON.parse(s)}catch{o[i]=s}return o},{});return n}function Na(e,t){var r=Object.assign({},t,{content:xa(t.content,[e])},t.ignoreAttributes?{}:Pd(e,t.plugins));return r.aria=Object.assign({},Ht.aria,{},r.aria),r.aria={expanded:r.aria.expanded==="auto"?t.interactive:r.aria.expanded,content:r.aria.content==="auto"?t.interactive?null:"describedby":r.aria.content},r}var Bd=function(){return"innerHTML"};function Oo(e,t){e[Bd()]=t}function Ea(e){var t=gr();return e===!0?t.className=Sa:(t.className=Ma,Ur(e)?t.appendChild(e):Oo(t,e)),t}function Da(e,t){Ur(t.content)?(Oo(e,""),e.appendChild(t.content)):typeof t.content!="function"&&(t.allowHTML?Oo(e,t.content):e.textContent=t.content)}function wo(e){var t=e.firstElementChild,r=$r(t.children);return{box:t,content:r.find(function(n){return n.classList.contains(ka)}),arrow:r.find(function(n){return n.classList.contains(Sa)||n.classList.contains(Ma)}),backdrop:r.find(function(n){return n.classList.contains(hd)})}}function Ia(e){var t=gr(),r=gr();r.className=dd,r.setAttribute("data-state","hidden"),r.setAttribute("tabindex","-1");var n=gr();n.className=ka,n.setAttribute("data-state","hidden"),Da(n,e.props),t.appendChild(r),r.appendChild(n),o(e.props,e.props);function o(i,s){var a=wo(t),c=a.box,l=a.content,u=a.arrow;s.theme?c.setAttribute("data-theme",s.theme):c.removeAttribute("data-theme"),typeof s.animation=="string"?c.setAttribute("data-animation",s.animation):c.removeAttribute("data-animation"),s.inertia?c.setAttribute("data-inertia",""):c.removeAttribute("data-inertia"),c.style.maxWidth=typeof s.maxWidth=="number"?s.maxWidth+"px":s.maxWidth,s.role?c.setAttribute("role",s.role):c.removeAttribute("role"),(i.content!==s.content||i.allowHTML!==s.allowHTML)&&Da(l,e.props),s.arrow?u?i.arrow!==s.arrow&&(c.removeChild(u),c.appendChild(Ea(s.arrow))):c.appendChild(Ea(s.arrow)):u&&c.removeChild(u)}return{popper:t,onUpdate:o}}Ia.$$tippy=!0;var zd=1,Gr=[],To=[];function Ld(e,t){var r=Na(e,Object.assign({},Ht,{},_a(yd(t)))),n,o,i,s=!1,a=!1,c=!1,l=!1,u,f,p,d=[],h=Ca(Lo,r.interactiveDebounce),v,g=zd++,M=null,y=vd(r.plugins),R={isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},m={id:g,reference:e,popper:gr(),popperInstance:M,props:r,state:R,plugins:y,clearDelayTimeouts:Ua,setProps:Ga,setContent:Ya,show:Xa,hide:Qa,hideWithInteractivity:Za,enable:Ka,disable:$a,unmount:tc,destroy:ec};if(!r.render)return m;var I=r.render(m),O=I.popper,F=I.onUpdate;O.setAttribute("data-tippy-root",""),O.id="tippy-"+m.id,m.popper=O,e._tippy=m,O._tippy=m;var J=y.map(function(b){return b.fn(m)}),U=e.hasAttribute("aria-expanded");return Po(),Ke(),oe(),Et("onCreate",[m]),r.showOnCreate&&Jo(),O.addEventListener("mouseenter",function(){m.props.interactive&&m.state.isVisible&&m.clearDelayTimeouts()}),O.addEventListener("mouseleave",function(b){m.props.interactive&&m.props.trigger.indexOf("mouseenter")>=0&&(qt().addEventListener("mousemove",h),h(b))}),m;function T(){var b=m.props.touch;return Array.isArray(b)?b:[b,0]}function Qt(){return T()[0]==="hold"}function rt(){var b;return!!((b=m.props.render)==null?void 0:b.$$tippy)}function ut(){return v||e}function qt(){var b=ut().parentNode;return b?xd(b):document}function G(){return wo(O)}function Y(b){return m.state.isMounted&&!m.state.isVisible||Gt.isTouch||u&&u.type==="focus"?0:So(m.props.delay,b?0:1,Ht.delay)}function oe(){O.style.pointerEvents=m.props.interactive&&m.state.isVisible?"":"none",O.style.zIndex=""+m.props.zIndex}function Et(b,w,N){if(N===void 0&&(N=!0),J.forEach(function(z){z[b]&&z[b].apply(void 0,w)}),N){var j;(j=m.props)[b].apply(j,w)}}function No(){var b=m.props.aria;if(!!b.content){var w="aria-"+b.content,N=O.id,j=vr(m.props.triggerTarget||e);j.forEach(function(z){var vt=z.getAttribute(w);if(m.state.isVisible)z.setAttribute(w,vt?vt+" "+N:N);else{var Dt=vt&&vt.replace(N,"").trim();Dt?z.setAttribute(w,Dt):z.removeAttribute(w)}})}}function Ke(){if(!(U||!m.props.aria.expanded)){var b=vr(m.props.triggerTarget||e);b.forEach(function(w){m.props.interactive?w.setAttribute("aria-expanded",m.state.isVisible&&w===ut()?"true":"false"):w.removeAttribute("aria-expanded")})}}function tn(){qt().removeEventListener("mousemove",h),Gr=Gr.filter(function(b){return b!==h})}function yr(b){if(!(Gt.isTouch&&(c||b.type==="mousedown"))&&!(m.props.interactive&&O.contains(b.target))){if(ut().contains(b.target)){if(Gt.isTouch||m.state.isVisible&&m.props.trigger.indexOf("click")>=0)return}else Et("onClickOutside",[m,b]);m.props.hideOnClick===!0&&(m.clearDelayTimeouts(),m.hide(),a=!0,setTimeout(function(){a=!1}),m.state.isMounted||en())}}function Eo(){c=!0}function Do(){c=!1}function Io(){var b=qt();b.addEventListener("mousedown",yr,!0),b.addEventListener("touchend",yr,Ae),b.addEventListener("touchstart",Do,Ae),b.addEventListener("touchmove",Eo,Ae)}function en(){var b=qt();b.removeEventListener("mousedown",yr,!0),b.removeEventListener("touchend",yr,Ae),b.removeEventListener("touchstart",Do,Ae),b.removeEventListener("touchmove",Eo,Ae)}function qa(b,w){Ro(b,function(){!m.state.isVisible&&O.parentNode&&O.parentNode.contains(O)&&w()})}function Ja(b,w){Ro(b,w)}function Ro(b,w){var N=G().box;function j(z){z.target===N&&(Co(N,"remove",j),w())}if(b===0)return w();Co(N,"remove",f),Co(N,"add",j),f=j}function _e(b,w,N){N===void 0&&(N=!1);var j=vr(m.props.triggerTarget||e);j.forEach(function(z){z.addEventListener(b,w,N),d.push({node:z,eventType:b,handler:w,options:N})})}function Po(){Qt()&&(_e("touchstart",zo,{passive:!0}),_e("touchend",Fo,{passive:!0})),md(m.props.trigger).forEach(function(b){if(b!=="manual")switch(_e(b,zo),b){case"mouseenter":_e("mouseleave",Fo);break;case"focus":_e(Nd?"focusout":"blur",Vo);break;case"focusin":_e("focusout",Vo);break}})}function Bo(){d.forEach(function(b){var w=b.node,N=b.eventType,j=b.handler,z=b.options;w.removeEventListener(N,j,z)}),d=[]}function zo(b){var w,N=!1;if(!(!m.state.isEnabled||Ho(b)||a)){var j=((w=u)==null?void 0:w.type)==="focus";u=b,v=b.currentTarget,Ke(),!m.state.isVisible&&kd(b)&&Gr.forEach(function(z){return z(b)}),b.type==="click"&&(m.props.trigger.indexOf("mouseenter")<0||s)&&m.props.hideOnClick!==!1&&m.state.isVisible?N=!0:Jo(b),b.type==="click"&&(s=!N),N&&!j&&br(b)}}function Lo(b){var w=b.target,N=ut().contains(w)||O.contains(w);if(!(b.type==="mousemove"&&N)){var j=rn().concat(O).map(function(z){var vt,Dt=z._tippy,Ne=(vt=Dt.popperInstance)==null?void 0:vt.state;return Ne?{popperRect:z.getBoundingClientRect(),popperState:Ne,props:r}:null}).filter(Boolean);Cd(j,b)&&(tn(),br(b))}}function Fo(b){var w=Ho(b)||m.props.trigger.indexOf("click")>=0&&s;if(!w){if(m.props.interactive){m.hideWithInteractivity(b);return}br(b)}}function Vo(b){m.props.trigger.indexOf("focusin")<0&&b.target!==ut()||m.props.interactive&&b.relatedTarget&&O.contains(b.relatedTarget)||br(b)}function Ho(b){return Gt.isTouch?Qt()!==b.type.indexOf("touch")>=0:!1}function jo(){qo();var b=m.props,w=b.popperOptions,N=b.placement,j=b.offset,z=b.getReferenceClientRect,vt=b.moveTransition,Dt=rt()?wo(O).arrow:null,Ne=z?{getBoundingClientRect:z,contextElement:z.contextElement||ut()}:e,Wo={name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(kr){var Ee=kr.state;if(rt()){var rc=G(),on=rc.box;["placement","reference-hidden","escaped"].forEach(function(Sr){Sr==="placement"?on.setAttribute("data-placement",Ee.placement):Ee.attributes.popper["data-popper-"+Sr]?on.setAttribute("data-"+Sr,""):on.removeAttribute("data-"+Sr)}),Ee.attributes.popper={}}}},ge=[{name:"offset",options:{offset:j}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!vt}},Wo];rt()&&Dt&&ge.push({name:"arrow",options:{element:Dt,padding:3}}),ge.push.apply(ge,(w==null?void 0:w.modifiers)||[]),m.popperInstance=ac(Ne,O,Object.assign({},w,{placement:N,onFirstUpdate:p,modifiers:ge}))}function qo(){m.popperInstance&&(m.popperInstance.destroy(),m.popperInstance=null)}function Wa(){var b=m.props.appendTo,w,N=ut();m.props.interactive&&b===Ht.appendTo||b==="parent"?w=N.parentNode:w=xa(b,[N]),w.contains(O)||w.appendChild(O),jo()}function rn(){return $r(O.querySelectorAll("[data-tippy-root]"))}function Jo(b){m.clearDelayTimeouts(),b&&Et("onTrigger",[m,b]),Io();var w=Y(!0),N=T(),j=N[0],z=N[1];Gt.isTouch&&j==="hold"&&z&&(w=z),w?n=setTimeout(function(){m.show()},w):m.show()}function br(b){if(m.clearDelayTimeouts(),Et("onUntrigger",[m,b]),!m.state.isVisible){en();return}if(!(m.props.trigger.indexOf("mouseenter")>=0&&m.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(b.type)>=0&&s)){var w=Y(!1);w?o=setTimeout(function(){m.state.isVisible&&m.hide()},w):i=requestAnimationFrame(function(){m.hide()})}}function Ka(){m.state.isEnabled=!0}function $a(){m.hide(),m.state.isEnabled=!1}function Ua(){clearTimeout(n),clearTimeout(o),cancelAnimationFrame(i)}function Ga(b){if(!m.state.isDestroyed){Et("onBeforeUpdate",[m,b]),Bo();var w=m.props,N=Na(e,Object.assign({},m.props,{},b,{ignoreAttributes:!0}));m.props=N,Po(),w.interactiveDebounce!==N.interactiveDebounce&&(tn(),h=Ca(Lo,N.interactiveDebounce)),w.triggerTarget&&!N.triggerTarget?vr(w.triggerTarget).forEach(function(j){j.removeAttribute("aria-expanded")}):N.triggerTarget&&e.removeAttribute("aria-expanded"),Ke(),oe(),F&&F(w,N),m.popperInstance&&(jo(),rn().forEach(function(j){requestAnimationFrame(j._tippy.popperInstance.forceUpdate)})),Et("onAfterUpdate",[m,b])}}function Ya(b){m.setProps({content:b})}function Xa(){var b=m.state.isVisible,w=m.state.isDestroyed,N=!m.state.isEnabled,j=Gt.isTouch&&!m.props.touch,z=So(m.props.duration,0,Ht.duration);if(!(b||w||N||j)&&!ut().hasAttribute("disabled")&&(Et("onShow",[m],!1),m.props.onShow(m)!==!1)){if(m.state.isVisible=!0,rt()&&(O.style.visibility="visible"),oe(),Io(),m.state.isMounted||(O.style.transition="none"),rt()){var vt=G(),Dt=vt.box,Ne=vt.content;xo([Dt,Ne],0)}p=function(){var ge;if(!(!m.state.isVisible||l)){if(l=!0,O.offsetHeight,O.style.transition=m.props.moveTransition,rt()&&m.props.animation){var nn=G(),kr=nn.box,Ee=nn.content;xo([kr,Ee],z),wa([kr,Ee],"visible")}No(),Ke(),Oa(To,m),(ge=m.popperInstance)==null||ge.forceUpdate(),m.state.isMounted=!0,Et("onMount",[m]),m.props.animation&&rt()&&Ja(z,function(){m.state.isShown=!0,Et("onShown",[m])})}},Wa()}}function Qa(){var b=!m.state.isVisible,w=m.state.isDestroyed,N=!m.state.isEnabled,j=So(m.props.duration,1,Ht.duration);if(!(b||w||N)&&(Et("onHide",[m],!1),m.props.onHide(m)!==!1)){if(m.state.isVisible=!1,m.state.isShown=!1,l=!1,s=!1,rt()&&(O.style.visibility="hidden"),tn(),en(),oe(),rt()){var z=G(),vt=z.box,Dt=z.content;m.props.animation&&(xo([vt,Dt],j),wa([vt,Dt],"hidden"))}No(),Ke(),m.props.animation?rt()&&qa(j,m.unmount):m.unmount()}}function Za(b){qt().addEventListener("mousemove",h),Oa(Gr,h),h(b)}function tc(){m.state.isVisible&&m.hide(),!!m.state.isMounted&&(qo(),rn().forEach(function(b){b._tippy.unmount()}),O.parentNode&&O.parentNode.removeChild(O),To=To.filter(function(b){return b!==m}),m.state.isMounted=!1,Et("onHidden",[m]))}function ec(){m.state.isDestroyed||(m.clearDelayTimeouts(),m.unmount(),Bo(),delete e._tippy,m.state.isDestroyed=!0,Et("onDestroy",[m]))}}function We(e,t){t===void 0&&(t={});var r=Ht.plugins.concat(t.plugins||[]);Td();var n=Object.assign({},t,{plugins:r}),o=Md(e),i=o.reduce(function(s,a){var c=a&&Ld(a,n);return c&&s.push(c),s},[]);return Ur(e)?i[0]:i}We.defaultProps=Ht;We.setDefaultProps=Rd;We.currentInput=Gt;Object.assign({},sc,{effect:function(t){var r=t.state,n={popper:{position:r.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(r.elements.popper.style,n.popper),r.styles=n,r.elements.arrow&&Object.assign(r.elements.arrow.style,n.arrow)}});We.setDefaultProps({render:Ia});class Fd{constructor({editor:t,element:r,view:n,tippyOptions:o,shouldShow:i}){this.preventHide=!1,this.shouldShow=({state:s,from:a,to:c})=>{const{doc:l,selection:u}=s,{empty:f}=u,p=!l.textBetween(a,c).length&&ma(s.selection);return!(f||p)},this.mousedownHandler=()=>{this.preventHide=!0},this.dragstartHandler=()=>{this.hide()},this.focusHandler=()=>{setTimeout(()=>this.update(this.editor.view))},this.blurHandler=({event:s})=>{var a;if(this.preventHide){this.preventHide=!1;return}(s==null?void 0:s.relatedTarget)&&((a=this.element.parentNode)===null||a===void 0?void 0:a.contains(s.relatedTarget))||this.hide()},this.editor=t,this.element=r,this.view=n,i&&(this.shouldShow=i),this.element.addEventListener("mousedown",this.mousedownHandler,{capture:!0}),this.view.dom.addEventListener("dragstart",this.dragstartHandler),this.editor.on("focus",this.focusHandler),this.editor.on("blur",this.blurHandler),this.element.style.visibility="visible",requestAnimationFrame(()=>{this.createTooltip(o)})}createTooltip(t={}){this.tippy=We(this.editor.options.element,S({duration:0,getReferenceClientRect:null,content:this.element,interactive:!0,trigger:"manual",placement:"top",hideOnClick:"toggle"},t))}update(t,r){var n;const{state:o,composing:i}=t,{doc:s,selection:a}=o,c=r&&r.doc.eq(s)&&r.selection.eq(a);if(i||c)return;const{ranges:l}=a,u=Math.min(...l.map(d=>d.$from.pos)),f=Math.max(...l.map(d=>d.$to.pos));if(!this.shouldShow({editor:this.editor,view:t,state:o,oldState:r,from:u,to:f})){this.hide();return}(n=this.tippy)===null||n===void 0||n.setProps({getReferenceClientRect:()=>{if(pd(o.selection)){const d=t.nodeDOM(u);if(d)return d.getBoundingClientRect()}return ba(t,u,f)}}),this.show()}show(){var t;(t=this.tippy)===null||t===void 0||t.show()}hide(){var t;(t=this.tippy)===null||t===void 0||t.hide()}destroy(){var t;(t=this.tippy)===null||t===void 0||t.destroy(),this.element.removeEventListener("mousedown",this.mousedownHandler),this.view.dom.removeEventListener("dragstart",this.dragstartHandler),this.editor.off("focus",this.focusHandler),this.editor.off("blur",this.blurHandler)}}const Ra=e=>new Rt({key:typeof e.pluginKey=="string"?new Wt(e.pluginKey):e.pluginKey,view:t=>new Fd(S({view:t},e))});St.create({name:"bubbleMenu",defaultOptions:{element:null,tippyOptions:{},pluginKey:"bubbleMenu",shouldShow:null},addProseMirrorPlugins(){return this.options.element?[Ra({pluginKey:this.options.pluginKey,editor:this.editor,element:this.options.element,tippyOptions:this.options.tippyOptions,shouldShow:this.options.shouldShow})]:[]}});class Vd{constructor({editor:t,element:r,view:n,tippyOptions:o,shouldShow:i}){this.preventHide=!1,this.shouldShow=({state:s})=>{const{selection:a}=s,{$anchor:c,empty:l}=a,u=c.depth===1,f=c.parent.isTextblock&&!c.parent.type.spec.code&&!c.parent.textContent;return!(!l||!u||!f)},this.mousedownHandler=()=>{this.preventHide=!0},this.focusHandler=()=>{setTimeout(()=>this.update(this.editor.view))},this.blurHandler=({event:s})=>{var a;if(this.preventHide){this.preventHide=!1;return}(s==null?void 0:s.relatedTarget)&&((a=this.element.parentNode)===null||a===void 0?void 0:a.contains(s.relatedTarget))||this.hide()},this.editor=t,this.element=r,this.view=n,i&&(this.shouldShow=i),this.element.addEventListener("mousedown",this.mousedownHandler,{capture:!0}),this.editor.on("focus",this.focusHandler),this.editor.on("blur",this.blurHandler),this.element.style.visibility="visible",requestAnimationFrame(()=>{this.createTooltip(o)})}createTooltip(t={}){this.tippy=We(this.editor.options.element,S({duration:0,getReferenceClientRect:null,content:this.element,interactive:!0,trigger:"manual",placement:"right",hideOnClick:"toggle"},t))}update(t,r){var n;const{state:o,composing:i}=t,{doc:s,selection:a}=o,{from:c,to:l}=a,u=r&&r.doc.eq(s)&&r.selection.eq(a);if(i||u)return;if(!this.shouldShow({editor:this.editor,view:t,state:o,oldState:r})){this.hide();return}(n=this.tippy)===null||n===void 0||n.setProps({getReferenceClientRect:()=>ba(t,c,l)}),this.show()}show(){var t;(t=this.tippy)===null||t===void 0||t.show()}hide(){var t;(t=this.tippy)===null||t===void 0||t.hide()}destroy(){var t;(t=this.tippy)===null||t===void 0||t.destroy(),this.element.removeEventListener("mousedown",this.mousedownHandler),this.editor.off("focus",this.focusHandler),this.editor.off("blur",this.blurHandler)}}const Pa=e=>new Rt({key:typeof e.pluginKey=="string"?new Wt(e.pluginKey):e.pluginKey,view:t=>new Vd(S({view:t},e))});St.create({name:"floatingMenu",defaultOptions:{element:null,tippyOptions:{},pluginKey:"floatingMenu",shouldShow:null},addProseMirrorPlugins(){return this.options.element?[Pa({pluginKey:this.options.pluginKey,editor:this.editor,element:this.options.element,tippyOptions:this.options.tippyOptions,shouldShow:this.options.shouldShow})]:[]}});$e({name:"BubbleMenu",props:{pluginKey:{type:[String,Object],default:"bubbleMenu"},editor:{type:Object,required:!0},tippyOptions:{type:Object,default:()=>({})},shouldShow:{type:Function,default:null}},setup(e,{slots:t}){const r=xr(null);return sn(()=>{const{pluginKey:n,editor:o,tippyOptions:i,shouldShow:s}=e;o.registerPlugin(Ra({pluginKey:n,editor:o,element:r.value,tippyOptions:i,shouldShow:s}))}),Cr(()=>{const{pluginKey:n,editor:o}=e;o.unregisterPlugin(n)}),()=>{var n;return ye("div",{ref:r},(n=t.default)===null||n===void 0?void 0:n.call(t))}}});function Hd(e){return hc((t,r)=>({get(){return t(),e},set(n){e=n,requestAnimationFrame(()=>{requestAnimationFrame(()=>{r()})})}}))}class jd extends ld{constructor(t={}){super(t);return this.vueRenderers=dc(new Map),this.contentComponent=null,this.reactiveState=Hd(this.view.state),this.on("transaction",()=>{this.reactiveState.value=this.view.state}),mc(this)}get state(){return this.reactiveState?this.reactiveState.value:this.view.state}registerPlugin(t,r){super.registerPlugin(t,r),this.reactiveState.value=this.view.state}unregisterPlugin(t){super.unregisterPlugin(t),this.reactiveState.value=this.view.state}}const qd=$e({name:"EditorContent",props:{editor:{default:null,type:Object}},setup(e){const t=xr(),r=cc();return lc(()=>{const n=e.editor;n&&n.options.element&&t.value&&uc(()=>{if(!t.value||!n.options.element.firstChild)return;const o=fc(t.value);t.value.append(...n.options.element.childNodes),n.contentComponent=r.ctx._,n.setOptions({element:o}),n.createNodeViews()})}),Cr(()=>{const n=e.editor;if(!n||(n.isDestroyed||n.view.setProps({nodeViews:{}}),n.contentComponent=null,!n.options.element.firstChild))return;const o=document.createElement("div");o.append(...n.options.element.childNodes),n.setOptions({element:o})}),{rootEl:t}},render(){const e=[];return this.editor&&this.editor.vueRenderers.forEach(t=>{const r=ye(pc,{to:t.teleportElement,key:t.id},ye(t.component,S({ref:t.id},t.props)));e.push(r)}),ye("div",{ref:t=>{this.rootEl=t}},...e)}});$e({name:"FloatingMenu",props:{pluginKey:{type:[String,Object],default:"floatingMenu"},editor:{type:Object,required:!0},tippyOptions:{type:Object,default:()=>({})},shouldShow:{type:Function,default:null}},setup(e,{slots:t}){const r=xr(null);return sn(()=>{const{pluginKey:n,editor:o,tippyOptions:i,shouldShow:s}=e;o.registerPlugin(Pa({pluginKey:n,editor:o,element:r.value,tippyOptions:i,shouldShow:s}))}),Cr(()=>{const{pluginKey:n,editor:o}=e;o.unregisterPlugin(n)}),()=>{var n;return ye("div",{ref:r},(n=t.default)===null||n===void 0?void 0:n.call(t))}}});const Jd=(e={})=>{const t=xr();return sn(()=>{t.value=new jd(e)}),Cr(()=>{var r;(r=t.value)===null||r===void 0||r.destroy()}),t};$e({props:{as:{type:String,default:"div"}},inject:["onDragStart","decorationClasses"],render(){var e,t;return ye(this.as,{class:this.decorationClasses.value,style:{whiteSpace:"normal"},"data-node-view-wrapper":"",onDragStart:this.onDragStart},(t=(e=this.$slots).default)===null||t===void 0?void 0:t.call(e))}});$e({props:{as:{type:String,default:"div"}},render(){return ye(this.as,{style:{whiteSpace:"pre-wrap"},"data-node-view-content":""})}});const Wd=/^\s*>\s$/gm,Kd=wt.create({name:"blockquote",defaultOptions:{HTMLAttributes:{}},content:"block*",group:"block",defining:!0,parseHTML(){return[{tag:"blockquote"}]},renderHTML({HTMLAttributes:e}){return["blockquote",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setBlockquote:()=>({commands:e})=>e.wrapIn("blockquote"),toggleBlockquote:()=>({commands:e})=>e.toggleWrap("blockquote"),unsetBlockquote:()=>({commands:e})=>e.lift("blockquote")}},addKeyboardShortcuts(){return{"Mod-Shift-b":()=>this.editor.commands.toggleBlockquote()}},addInputRules(){return[uo(Wd,this.type)]}}),$d=/(?:^|\s)((?:\*\*)((?:[^*]+))(?:\*\*))$/gm,Ud=/(?:^|\s)((?:\*\*)((?:[^*]+))(?:\*\*))/gm,Gd=/(?:^|\s)((?:__)((?:[^__]+))(?:__))$/gm,Yd=/(?:^|\s)((?:__)((?:[^__]+))(?:__))/gm,Xd=Te.create({name:"bold",defaultOptions:{HTMLAttributes:{}},parseHTML(){return[{tag:"strong"},{tag:"b",getAttrs:e=>e.style.fontWeight!=="normal"&&null},{style:"font-weight",getAttrs:e=>/^(bold(er)?|[5-9]\d{2,})$/.test(e)&&null}]},renderHTML({HTMLAttributes:e}){return["strong",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setBold:()=>({commands:e})=>e.setMark("bold"),toggleBold:()=>({commands:e})=>e.toggleMark("bold"),unsetBold:()=>({commands:e})=>e.unsetMark("bold")}},addKeyboardShortcuts(){return{"Mod-b":()=>this.editor.commands.toggleBold()}},addInputRules(){return[qe($d,this.type),qe(Gd,this.type)]},addPasteRules(){return[Je(Ud,this.type),Je(Yd,this.type)]}}),Qd=/^\s*([-+*])\s$/,Zd=wt.create({name:"bulletList",defaultOptions:{HTMLAttributes:{}},group:"block list",content:"listItem+",parseHTML(){return[{tag:"ul"}]},renderHTML({HTMLAttributes:e}){return["ul",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{toggleBulletList:()=>({commands:e})=>e.toggleList("bulletList","listItem")}},addKeyboardShortcuts(){return{"Mod-Shift-8":()=>this.editor.commands.toggleBulletList()}},addInputRules(){return[uo(Qd,this.type)]}}),th=/(?:^|\s)((?:`)((?:[^`]+))(?:`))$/gm,eh=/(?:^|\s)((?:`)((?:[^`]+))(?:`))/gm,rh=Te.create({name:"code",defaultOptions:{HTMLAttributes:{}},excludes:"_",parseHTML(){return[{tag:"code"}]},renderHTML({HTMLAttributes:e}){return["code",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setCode:()=>({commands:e})=>e.setMark("code"),toggleCode:()=>({commands:e})=>e.toggleMark("code"),unsetCode:()=>({commands:e})=>e.unsetMark("code")}},addKeyboardShortcuts(){return{"Mod-e":()=>this.editor.commands.toggleCode()}},addInputRules(){return[qe(th,this.type)]},addPasteRules(){return[Je(eh,this.type)]}}),nh=/^```(?[a-z]*)? $/,oh=/^~~~(?[a-z]*)? $/,ih=wt.create({name:"codeBlock",defaultOptions:{languageClassPrefix:"language-",HTMLAttributes:{}},content:"text*",marks:"",group:"block",code:!0,defining:!0,addAttributes(){return{language:{default:null,parseHTML:e=>{var t;const r=(t=e.firstElementChild)===null||t===void 0?void 0:t.getAttribute("class");if(!r)return null;const n=new RegExp(`^(${this.options.languageClassPrefix})`);return{language:r.replace(n,"")}},renderHTML:e=>e.language?{class:this.options.languageClassPrefix+e.language}:null}}},parseHTML(){return[{tag:"pre",preserveWhitespace:"full"}]},renderHTML({HTMLAttributes:e}){return["pre",this.options.HTMLAttributes,["code",e,0]]},addCommands(){return{setCodeBlock:e=>({commands:t})=>t.setNode("codeBlock",e),toggleCodeBlock:e=>({commands:t})=>t.toggleNode("codeBlock","paragraph",e)}},addKeyboardShortcuts(){return{"Mod-Alt-c":()=>this.editor.commands.toggleCodeBlock(),Backspace:()=>{const{empty:e,$anchor:t}=this.editor.state.selection,r=t.pos===1;return!e||t.parent.type.name!==this.name?!1:r||!t.parent.textContent.length?this.editor.commands.clearNodes():!1}}},addInputRules(){return[fo(nh,this.type,({groups:e})=>e),fo(oh,this.type,({groups:e})=>e)]}}),sh=wt.create({name:"doc",topNode:!0,content:"block+"});function ah(e){return e===void 0&&(e={}),new Rt({view:function(r){return new Yt(r,e)}})}var Yt=function(t,r){var n=this;this.editorView=t,this.width=r.width||1,this.color=r.color||"black",this.class=r.class,this.cursorPos=null,this.element=null,this.timeout=null,this.handlers=["dragover","dragend","drop","dragleave"].map(function(o){var i=function(s){return n[o](s)};return t.dom.addEventListener(o,i),{name:o,handler:i}})};Yt.prototype.destroy=function(){var t=this;this.handlers.forEach(function(r){var n=r.name,o=r.handler;return t.editorView.dom.removeEventListener(n,o)})};Yt.prototype.update=function(t,r){this.cursorPos!=null&&r.doc!=t.state.doc&&(this.cursorPos>t.state.doc.content.size?this.setCursor(null):this.updateOverlay())};Yt.prototype.setCursor=function(t){t!=this.cursorPos&&(this.cursorPos=t,t==null?(this.element.parentNode.removeChild(this.element),this.element=null):this.updateOverlay())};Yt.prototype.updateOverlay=function(){var t=this.editorView.state.doc.resolve(this.cursorPos),r;if(!t.parent.inlineContent){var n=t.nodeBefore,o=t.nodeAfter;if(n||o){var i=this.editorView.nodeDOM(this.cursorPos-(n?n.nodeSize:0)).getBoundingClientRect(),s=n?i.bottom:i.top;n&&o&&(s=(s+this.editorView.nodeDOM(this.cursorPos).getBoundingClientRect().top)/2),r={left:i.left,right:i.right,top:s-this.width/2,bottom:s+this.width/2}}}if(!r){var a=this.editorView.coordsAtPos(this.cursorPos);r={left:a.left-this.width/2,right:a.left+this.width/2,top:a.top,bottom:a.bottom}}var c=this.editorView.dom.offsetParent;this.element||(this.element=c.appendChild(document.createElement("div")),this.class&&(this.element.className=this.class),this.element.style.cssText="position: absolute; z-index: 50; pointer-events: none; background-color: "+this.color);var l,u;if(!c||c==document.body&&getComputedStyle(c).position=="static")l=-pageXOffset,u=-pageYOffset;else{var f=c.getBoundingClientRect();l=f.left-c.scrollLeft,u=f.top-c.scrollTop}this.element.style.left=r.left-l+"px",this.element.style.top=r.top-u+"px",this.element.style.width=r.right-r.left+"px",this.element.style.height=r.bottom-r.top+"px"};Yt.prototype.scheduleRemoval=function(t){var r=this;clearTimeout(this.timeout),this.timeout=setTimeout(function(){return r.setCursor(null)},t)};Yt.prototype.dragover=function(t){if(!!this.editorView.editable){var r=this.editorView.posAtCoords({left:t.clientX,top:t.clientY});if(r){var n=r.pos;if(this.editorView.dragging&&this.editorView.dragging.slice&&(n=Oi(this.editorView.state.doc,n,this.editorView.dragging.slice),n==null))return this.setCursor(null);this.setCursor(n),this.scheduleRemoval(5e3)}}};Yt.prototype.dragend=function(){this.scheduleRemoval(20)};Yt.prototype.drop=function(){this.scheduleRemoval(20)};Yt.prototype.dragleave=function(t){(t.target==this.editorView.dom||!this.editorView.dom.contains(t.relatedTarget))&&this.setCursor(null)};const ch=St.create({name:"dropCursor",defaultOptions:{color:"currentColor",width:1,class:null},addProseMirrorPlugins(){return[ah(this.options)]}});var jt=function(e){function t(r){e.call(this,r,r)}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.map=function(n,o){var i=n.resolve(o.map(this.head));return t.valid(i)?new t(i):e.near(i)},t.prototype.content=function(){return C.empty},t.prototype.eq=function(n){return n instanceof t&&n.head==this.head},t.prototype.toJSON=function(){return{type:"gapcursor",pos:this.head}},t.fromJSON=function(n,o){if(typeof o.pos!="number")throw new RangeError("Invalid input for GapCursor.fromJSON");return new t(n.resolve(o.pos))},t.prototype.getBookmark=function(){return new Yr(this.anchor)},t.valid=function(n){var o=n.parent;if(o.isTextblock||!lh(n)||!uh(n))return!1;var i=o.type.spec.allowGapCursor;if(i!=null)return i;var s=o.contentMatchAt(n.index()).defaultType;return s&&s.isTextblock},t.findFrom=function(n,o,i){t:for(;;){if(!i&&t.valid(n))return n;for(var s=n.pos,a=null,c=n.depth;;c--){var l=n.node(c);if(o>0?n.indexAfter(c)0){a=l.child(o>0?n.indexAfter(c):n.index(c)-1);break}else if(c==0)return null;s+=o;var u=n.doc.resolve(s);if(t.valid(u))return u}for(;;){var f=o>0?a.firstChild:a.lastChild;if(!f){if(a.isAtom&&!a.isText&&!E.isSelectable(a)){n=n.doc.resolve(s+a.nodeSize*o),i=!1;continue t}break}a=f,s+=o;var p=n.doc.resolve(s);if(t.valid(p))return p}return null}},t}(D);jt.prototype.visible=!1;D.jsonID("gapcursor",jt);var Yr=function(t){this.pos=t};Yr.prototype.map=function(t){return new Yr(t.map(this.pos))};Yr.prototype.resolve=function(t){var r=t.resolve(this.pos);return jt.valid(r)?new jt(r):D.near(r)};function lh(e){for(var t=e.depth;t>=0;t--){var r=e.index(t);if(r!=0)for(var n=e.node(t).child(r-1);;n=n.lastChild){if(n.childCount==0&&!n.inlineContent||n.isAtom||n.type.spec.isolating)return!0;if(n.inlineContent)return!1}}return!0}function uh(e){for(var t=e.depth;t>=0;t--){var r=e.indexAfter(t),n=e.node(t);if(r!=n.childCount)for(var o=n.child(r);;o=o.firstChild){if(o.childCount==0&&!o.inlineContent||o.isAtom||o.type.spec.isolating)return!0;if(o.inlineContent)return!1}}return!0}var fh=function(){return new Rt({props:{decorations:hh,createSelectionBetween:function(t,r,n){if(r.pos==n.pos&&jt.valid(n))return new jt(n)},handleClick:dh,handleKeyDown:ph}})},ph=js({ArrowLeft:Xr("horiz",-1),ArrowRight:Xr("horiz",1),ArrowUp:Xr("vert",-1),ArrowDown:Xr("vert",1)});function Xr(e,t){var r=e=="vert"?t>0?"down":"up":t>0?"right":"left";return function(n,o,i){var s=n.selection,a=t>0?s.$to:s.$from,c=s.empty;if(s instanceof H){if(!i.endOfTextblock(r)||a.depth==0)return!1;c=!1,a=n.doc.resolve(t>0?a.after():a.before())}var l=jt.findFrom(a,t,c);return l?(o&&o(n.tr.setSelection(new jt(l))),!0):!1}}function dh(e,t,r){if(!e.editable)return!1;var n=e.state.doc.resolve(t);if(!jt.valid(n))return!1;var o=e.posAtCoords({left:r.clientX,top:r.clientY}),i=o.inside;return i>-1&&E.isSelectable(e.state.doc.nodeAt(i))?!1:(e.dispatch(e.state.tr.setSelection(new jt(n))),!0)}function hh(e){if(!(e.selection instanceof jt))return null;var t=document.createElement("div");return t.className="ProseMirror-gapcursor",q.create(e.doc,[nt.widget(e.selection.head,t,{key:"gapcursor"})])}const mh=St.create({name:"gapCursor",addProseMirrorPlugins(){return[fh()]},extendNodeSchema(e){var t;const r={name:e.name,options:e.options};return{allowGapCursor:(t=et(_(e,"allowGapCursor",r)))!==null&&t!==void 0?t:null}}}),vh=wt.create({name:"hardBreak",defaultOptions:{HTMLAttributes:{}},inline:!0,group:"inline",selectable:!1,parseHTML(){return[{tag:"br"}]},renderHTML({HTMLAttributes:e}){return["br",Ot(this.options.HTMLAttributes,e)]},addCommands(){return{setHardBreak:()=>({commands:e})=>e.first([()=>e.exitCode(),()=>e.insertContent({type:this.name})])}},addKeyboardShortcuts(){return{"Mod-Enter":()=>this.editor.commands.setHardBreak(),"Shift-Enter":()=>this.editor.commands.setHardBreak()}}}),gh=wt.create({name:"heading",defaultOptions:{levels:[1,2,3,4,5,6],HTMLAttributes:{}},content:"inline*",group:"block",defining:!0,addAttributes(){return{level:{default:1,rendered:!1}}},parseHTML(){return this.options.levels.map(e=>({tag:`h${e}`,attrs:{level:e}}))},renderHTML({node:e,HTMLAttributes:t}){return[`h${this.options.levels.includes(e.attrs.level)?e.attrs.level:this.options.levels[0]}`,Ot(this.options.HTMLAttributes,t),0]},addCommands(){return{setHeading:e=>({commands:t})=>this.options.levels.includes(e.level)?t.setNode("heading",e):!1,toggleHeading:e=>({commands:t})=>this.options.levels.includes(e.level)?t.toggleNode("heading","paragraph",e):!1}},addKeyboardShortcuts(){return this.options.levels.reduce((e,t)=>Tt(S({},e),{[`Mod-Alt-${t}`]:()=>this.editor.commands.toggleHeading({level:t})}),{})},addInputRules(){return this.options.levels.map(e=>fo(new RegExp(`^(#{1,${e}})\\s$`),this.type,{level:e}))}});var Qr=200,ct=function(){};ct.prototype.append=function(t){return t.length?(t=ct.from(t),!this.length&&t||t.length=r?ct.empty:this.sliceInner(Math.max(0,t),Math.min(this.length,r))};ct.prototype.get=function(t){if(!(t<0||t>=this.length))return this.getInner(t)};ct.prototype.forEach=function(t,r,n){r===void 0&&(r=0),n===void 0&&(n=this.length),r<=n?this.forEachInner(t,r,n,0):this.forEachInvertedInner(t,r,n,0)};ct.prototype.map=function(t,r,n){r===void 0&&(r=0),n===void 0&&(n=this.length);var o=[];return this.forEach(function(i,s){return o.push(t(i,s))},r,n),o};ct.from=function(t){return t instanceof ct?t:t&&t.length?new Ba(t):ct.empty};var Ba=function(e){function t(n){e.call(this),this.values=n}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var r={length:{configurable:!0},depth:{configurable:!0}};return t.prototype.flatten=function(){return this.values},t.prototype.sliceInner=function(o,i){return o==0&&i==this.length?this:new t(this.values.slice(o,i))},t.prototype.getInner=function(o){return this.values[o]},t.prototype.forEachInner=function(o,i,s,a){for(var c=i;c=s;c--)if(o(this.values[c],a+c)===!1)return!1},t.prototype.leafAppend=function(o){if(this.length+o.length<=Qr)return new t(this.values.concat(o.flatten()))},t.prototype.leafPrepend=function(o){if(this.length+o.length<=Qr)return new t(o.flatten().concat(this.values))},r.length.get=function(){return this.values.length},r.depth.get=function(){return 0},Object.defineProperties(t.prototype,r),t}(ct);ct.empty=new Ba([]);var yh=function(e){function t(r,n){e.call(this),this.left=r,this.right=n,this.length=r.length+n.length,this.depth=Math.max(r.depth,n.depth)+1}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.flatten=function(){return this.left.flatten().concat(this.right.flatten())},t.prototype.getInner=function(n){return na&&this.right.forEachInner(n,Math.max(o-a,0),Math.min(this.length,i)-a,s+a)===!1)return!1},t.prototype.forEachInvertedInner=function(n,o,i,s){var a=this.left.length;if(o>a&&this.right.forEachInvertedInner(n,o-a,Math.max(i,a)-a,s+a)===!1||i=i?this.right.slice(n-i,o-i):this.left.slice(n,i).append(this.right.slice(0,o-i))},t.prototype.leafAppend=function(n){var o=this.right.leafAppend(n);if(o)return new t(this.left,o)},t.prototype.leafPrepend=function(n){var o=this.left.leafPrepend(n);if(o)return new t(o,this.right)},t.prototype.appendInner=function(n){return this.left.depth>=Math.max(this.right.depth,n.depth)+1?new t(this.left,new t(this.right,n)):new t(this,n)},t}(ct),za=ct,bh=500,lt=function(t,r){this.items=t,this.eventCount=r};lt.prototype.popEvent=function(t,r){var n=this;if(this.eventCount==0)return null;for(var o=this.items.length;;o--){var i=this.items.get(o-1);if(i.selection){--o;break}}var s,a;r&&(s=this.remapping(o,this.items.length),a=s.maps.length);var c=t.tr,l,u,f=[],p=[];return this.items.forEach(function(d,h){if(!d.step){s||(s=n.remapping(o,h+1),a=s.maps.length),a--,p.push(d);return}if(s){p.push(new Xt(d.map));var v=d.step.map(s.slice(a)),g;v&&c.maybeStep(v).doc&&(g=c.mapping.maps[c.mapping.maps.length-1],f.push(new Xt(g,null,null,f.length+p.length))),a--,g&&s.appendMap(g,a)}else c.maybeStep(d.step);if(d.selection)return l=s?d.selection.map(s.slice(a)):d.selection,u=new lt(n.items.slice(0,o).append(p.reverse().concat(f)),n.eventCount-1),!1},this.items.length,0),{remaining:u,transform:c,selection:l}};lt.prototype.addTransform=function(t,r,n,o){for(var i=[],s=this.eventCount,a=this.items,c=!o&&a.length?a.get(a.length-1):null,l=0;lSh&&(a=kh(a,d),s-=d),new lt(a.append(i),s)};lt.prototype.remapping=function(t,r){var n=new dt;return this.items.forEach(function(o,i){var s=o.mirrorOffset!=null&&i-o.mirrorOffset>=t?n.maps.length-o.mirrorOffset:null;n.appendMap(o.map,s)},t,r),n};lt.prototype.addMaps=function(t){return this.eventCount==0?this:new lt(this.items.append(t.map(function(r){return new Xt(r)})),this.eventCount)};lt.prototype.rebased=function(t,r){if(!this.eventCount)return this;var n=[],o=Math.max(0,this.items.length-r),i=t.mapping,s=t.steps.length,a=this.eventCount;this.items.forEach(function(d){d.selection&&a--},o);var c=r;this.items.forEach(function(d){var h=i.getMirror(--c);if(h!=null){s=Math.min(s,h);var v=i.maps[h];if(d.step){var g=t.steps[h].invert(t.docs[h]),M=d.selection&&d.selection.map(i.slice(c+1,h));M&&a++,n.push(new Xt(v,g,M))}else n.push(new Xt(v))}},o);for(var l=[],u=r;ubh&&(p=p.compress(this.items.length-n.length)),p};lt.prototype.emptyItemCount=function(){var t=0;return this.items.forEach(function(r){r.step||t++}),t};lt.prototype.compress=function(t){t===void 0&&(t=this.items.length);var r=this.remapping(0,t),n=r.maps.length,o=[],i=0;return this.items.forEach(function(s,a){if(a>=t)o.push(s),s.selection&&i++;else if(s.step){var c=s.step.map(r.slice(n)),l=c&&c.getMap();if(n--,l&&r.appendMap(l,n),c){var u=s.selection&&s.selection.map(r.slice(n));u&&i++;var f=new Xt(l.invert(),c,u),p,d=o.length-1;(p=o.length&&o[d].merge(f))?o[d]=p:o.push(f)}}else s.map&&n--},this.items.length,0),new lt(za.from(o.reverse()),i)};lt.empty=new lt(za.empty,0);function kh(e,t){var r;return e.forEach(function(n,o){if(n.selection&&t--==0)return r=o,!1}),e.slice(r)}var Xt=function(t,r,n,o){this.map=t,this.step=r,this.selection=n,this.mirrorOffset=o};Xt.prototype.merge=function(t){if(this.step&&t.step&&!t.selection){var r=t.step.merge(this.step);if(r)return new Xt(r.getMap().invert(),r,this.selection)}};var me=function(t,r,n,o){this.done=t,this.undone=r,this.prevRanges=n,this.prevTime=o},Sh=20;function Mh(e,t,r,n){var o=r.getMeta(ve),i;if(o)return o.historyState;r.getMeta(Ch)&&(e=new me(e.done,e.undone,null,0));var s=r.getMeta("appendedTransaction");if(r.steps.length==0)return e;if(s&&s.getMeta(ve))return s.getMeta(ve).redo?new me(e.done.addTransform(r,null,n,Zr(t)),e.undone,La(r.mapping.maps[r.steps.length-1]),e.prevTime):new me(e.done,e.undone.addTransform(r,null,n,Zr(t)),null,e.prevTime);if(r.getMeta("addToHistory")!==!1&&!(s&&s.getMeta("addToHistory")===!1)){var a=e.prevTime==0||!s&&(e.prevTime<(r.time||0)-n.newGroupDelay||!xh(r,e.prevRanges)),c=s?Ao(e.prevRanges,r.mapping):La(r.mapping.maps[r.steps.length-1]);return new me(e.done.addTransform(r,a?t.selection.getBookmark():null,n,Zr(t)),lt.empty,c,r.time)}else return(i=r.getMeta("rebased"))?new me(e.done.rebased(r,i),e.undone.rebased(r,i),Ao(e.prevRanges,r.mapping),e.prevTime):new me(e.done.addMaps(r.mapping.maps),e.undone.addMaps(r.mapping.maps),Ao(e.prevRanges,r.mapping),e.prevTime)}function xh(e,t){if(!t)return!1;if(!e.docChanged)return!0;var r=!1;return e.mapping.maps[0].forEach(function(n,o){for(var i=0;i=t[i]&&(r=!0)}),r}function La(e){var t=[];return e.forEach(function(r,n,o,i){return t.push(o,i)}),t}function Ao(e,t){if(!e)return null;for(var r=[],n=0;n({state:e,dispatch:t})=>Ha(e,t),redo:()=>({state:e,dispatch:t})=>ja(e,t)}},addProseMirrorPlugins(){return[Oh(this.options)]},addKeyboardShortcuts(){return{"Mod-z":()=>this.editor.commands.undo(),"Mod-y":()=>this.editor.commands.redo(),"Shift-Mod-z":()=>this.editor.commands.redo(),"Mod-\u044F":()=>this.editor.commands.undo(),"Shift-Mod-\u044F":()=>this.editor.commands.redo()}}}),Th=wt.create({name:"horizontalRule",defaultOptions:{HTMLAttributes:{}},group:"block",parseHTML(){return[{tag:"hr"}]},renderHTML({HTMLAttributes:e}){return["hr",Ot(this.options.HTMLAttributes,e)]},addCommands(){return{setHorizontalRule:()=>({chain:e})=>e().command(({tr:t,dispatch:r})=>{const{selection:n}=t,{empty:o,$anchor:i}=n,s=i.parent.isTextblock&&!i.parent.type.spec.code&&!i.parent.textContent;if(!o||!s||!r)return!0;const a=i.before();return t.deleteRange(a,a+1),!0}).insertContent({type:this.name}).command(({tr:t,dispatch:r})=>{var n;if(r){const{parent:o,pos:i}=t.selection.$from,s=i+1;if(!t.doc.nodeAt(s)){const c=(n=o.type.contentMatch.defaultType)===null||n===void 0?void 0:n.create();c&&(t.insert(s,c),t.setSelection(H.create(t.doc,s)))}t.scrollIntoView()}return!0}).run()}},addInputRules(){return[ud(/^(?:---|—-|___\s|\*\*\*\s)$/,this.type)]}}),Ah=/(?:^|\s)((?:\*)((?:[^*]+))(?:\*))$/gm,_h=/(?:^|\s)((?:\*)((?:[^*]+))(?:\*))/gm,Nh=/(?:^|\s)((?:_)((?:[^_]+))(?:_))$/gm,Eh=/(?:^|\s)((?:_)((?:[^_]+))(?:_))/gm,Dh=Te.create({name:"italic",defaultOptions:{HTMLAttributes:{}},parseHTML(){return[{tag:"em"},{tag:"i",getAttrs:e=>e.style.fontStyle!=="normal"&&null},{style:"font-style=italic"}]},renderHTML({HTMLAttributes:e}){return["em",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setItalic:()=>({commands:e})=>e.setMark("italic"),toggleItalic:()=>({commands:e})=>e.toggleMark("italic"),unsetItalic:()=>({commands:e})=>e.unsetMark("italic")}},addKeyboardShortcuts(){return{"Mod-i":()=>this.editor.commands.toggleItalic()}},addInputRules(){return[qe(Ah,this.type),qe(Nh,this.type)]},addPasteRules(){return[Je(_h,this.type),Je(Eh,this.type)]}}),Ih=wt.create({name:"listItem",defaultOptions:{HTMLAttributes:{}},content:"paragraph block*",defining:!0,parseHTML(){return[{tag:"li"}]},renderHTML({HTMLAttributes:e}){return["li",Ot(this.options.HTMLAttributes,e),0]},addKeyboardShortcuts(){return{Enter:()=>this.editor.commands.splitListItem("listItem"),Tab:()=>this.editor.commands.sinkListItem("listItem"),"Shift-Tab":()=>this.editor.commands.liftListItem("listItem")}}}),Rh=/^(\d+)\.\s$/,Ph=wt.create({name:"orderedList",defaultOptions:{HTMLAttributes:{}},group:"block list",content:"listItem+",addAttributes(){return{start:{default:1,parseHTML:e=>({start:e.hasAttribute("start")?parseInt(e.getAttribute("start")||"",10):1})}}},parseHTML(){return[{tag:"ol"}]},renderHTML({HTMLAttributes:e}){const n=e,{start:t}=n,r=Go(n,["start"]);return t===1?["ol",Ot(this.options.HTMLAttributes,r),0]:["ol",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{toggleOrderedList:()=>({commands:e})=>e.toggleList("orderedList","listItem")}},addKeyboardShortcuts(){return{"Mod-Shift-7":()=>this.editor.commands.toggleOrderedList()}},addInputRules(){return[uo(Rh,this.type,e=>({start:+e[1]}),(e,t)=>t.childCount+t.attrs.start===+e[1])]}}),Bh=wt.create({name:"paragraph",priority:1e3,defaultOptions:{HTMLAttributes:{}},group:"block",content:"inline*",parseHTML(){return[{tag:"p"}]},renderHTML({HTMLAttributes:e}){return["p",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setParagraph:()=>({commands:e})=>e.setNode("paragraph")}},addKeyboardShortcuts(){return{"Mod-Alt-0":()=>this.editor.commands.setParagraph()}}}),zh=/(?:^|\s)((?:~~)((?:[^~]+))(?:~~))$/gm,Lh=/(?:^|\s)((?:~~)((?:[^~]+))(?:~~))/gm,Fh=Te.create({name:"strike",defaultOptions:{HTMLAttributes:{}},parseHTML(){return[{tag:"s"},{tag:"del"},{tag:"strike"},{style:"text-decoration",consuming:!1,getAttrs:e=>e.includes("line-through")?{}:!1}]},renderHTML({HTMLAttributes:e}){return["s",Ot(this.options.HTMLAttributes,e),0]},addCommands(){return{setStrike:()=>({commands:e})=>e.setMark("strike"),toggleStrike:()=>({commands:e})=>e.toggleMark("strike"),unsetStrike:()=>({commands:e})=>e.unsetMark("strike")}},addKeyboardShortcuts(){return{"Mod-Shift-x":()=>this.editor.commands.toggleStrike()}},addInputRules(){return[qe(zh,this.type)]},addPasteRules(){return[Je(Lh,this.type)]}}),Vh=wt.create({name:"text",group:"inline"}),Hh=St.create({name:"starterKit",addExtensions(){var e,t,r,n,o,i,s,a,c,l,u,f,p,d,h,v,g,M;const y=[];return this.options.blockquote!==!1&&y.push(Kd.configure((e=this.options)===null||e===void 0?void 0:e.blockquote)),this.options.bold!==!1&&y.push(Xd.configure((t=this.options)===null||t===void 0?void 0:t.bold)),this.options.bulletList!==!1&&y.push(Zd.configure((r=this.options)===null||r===void 0?void 0:r.bulletList)),this.options.code!==!1&&y.push(rh.configure((n=this.options)===null||n===void 0?void 0:n.code)),this.options.codeBlock!==!1&&y.push(ih.configure((o=this.options)===null||o===void 0?void 0:o.codeBlock)),this.options.document!==!1&&y.push(sh.configure((i=this.options)===null||i===void 0?void 0:i.document)),this.options.dropcursor!==!1&&y.push(ch.configure((s=this.options)===null||s===void 0?void 0:s.dropcursor)),this.options.gapcursor!==!1&&y.push(mh.configure((a=this.options)===null||a===void 0?void 0:a.gapcursor)),this.options.hardBreak!==!1&&y.push(vh.configure((c=this.options)===null||c===void 0?void 0:c.hardBreak)),this.options.heading!==!1&&y.push(gh.configure((l=this.options)===null||l===void 0?void 0:l.heading)),this.options.history!==!1&&y.push(wh.configure((u=this.options)===null||u===void 0?void 0:u.history)),this.options.horizontalRule!==!1&&y.push(Th.configure((f=this.options)===null||f===void 0?void 0:f.horizontalRule)),this.options.italic!==!1&&y.push(Dh.configure((p=this.options)===null||p===void 0?void 0:p.italic)),this.options.listItem!==!1&&y.push(Ih.configure((d=this.options)===null||d===void 0?void 0:d.listItem)),this.options.orderedList!==!1&&y.push(Ph.configure((h=this.options)===null||h===void 0?void 0:h.orderedList)),this.options.paragraph!==!1&&y.push(Bh.configure((v=this.options)===null||v===void 0?void 0:v.paragraph)),this.options.strike!==!1&&y.push(Fh.configure((g=this.options)===null||g===void 0?void 0:g.strike)),this.options.text!==!1&&y.push(Vh.configure((M=this.options)===null||M===void 0?void 0:M.text)),y}}),jh=St.create({name:"textAlign",addOptions(){return{types:[],alignments:["left","center","right","justify"],defaultAlignment:"left"}},addGlobalAttributes(){return[{types:this.options.types,attributes:{textAlign:{default:this.options.defaultAlignment,parseHTML:e=>e.style.textAlign||this.options.defaultAlignment,renderHTML:e=>e.textAlign===this.options.defaultAlignment?{}:{style:`text-align: ${e.textAlign}`}}}}]},addCommands(){return{setTextAlign:e=>({commands:t})=>this.options.alignments.includes(e)?this.options.types.every(r=>t.updateAttributes(r,{textAlign:e})):!1,unsetTextAlign:()=>({commands:e})=>this.options.types.every(t=>e.resetAttributes(t,"textAlign"))}},addKeyboardShortcuts(){return{"Mod-Shift-l":()=>this.editor.commands.setTextAlign("left"),"Mod-Shift-e":()=>this.editor.commands.setTextAlign("center"),"Mod-Shift-r":()=>this.editor.commands.setTextAlign("right"),"Mod-Shift-j":()=>this.editor.commands.setTextAlign("justify")}}}),qh={},Jh={viewBox:"0 0 24 24"},Wh=A("path",{d:"M17.194 10.962A6.271 6.271 0 0012.844.248H4.3a1.25 1.25 0 000 2.5h1.013a.25.25 0 01.25.25V21a.25.25 0 01-.25.25H4.3a1.25 1.25 0 100 2.5h9.963a6.742 6.742 0 002.93-12.786zm-4.35-8.214a3.762 3.762 0 010 7.523H8.313a.25.25 0 01-.25-.25V3a.25.25 0 01.25-.25zm1.42 18.5H8.313a.25.25 0 01-.25-.25v-7.977a.25.25 0 01.25-.25h5.951a4.239 4.239 0 010 8.477z"},null,-1),Kh=[Wh];function $h(e,t){return gt(),Mt("svg",Jh,Kh)}var Uh=At(qh,[["render",$h]]);const Gh={},Yh={viewBox:"0 0 24 24"},Xh=A("path",{d:"M9.147 21.552a1.244 1.244 0 01-.895-.378L.84 13.561a2.257 2.257 0 010-3.125l7.412-7.613a1.25 1.25 0 011.791 1.744l-6.9 7.083a.5.5 0 000 .7l6.9 7.082a1.25 1.25 0 01-.9 2.122zm5.707 0a1.25 1.25 0 01-.9-2.122l6.9-7.083a.5.5 0 000-.7l-6.9-7.082a1.25 1.25 0 011.791-1.744l7.411 7.612a2.257 2.257 0 010 3.125l-7.412 7.614a1.244 1.244 0 01-.89.38zm6.514-9.373z"},null,-1),Qh=[Xh];function Zh(e,t){return gt(),Mt("svg",Yh,Qh)}var tm=At(Gh,[["render",Zh]]);const em={},rm={viewBox:"0 0 24 24"},nm=A("path",{d:"M22.5.248h-7.637a1.25 1.25 0 000 2.5h1.086a.25.25 0 01.211.384L4.78 21.017a.5.5 0 01-.422.231H1.5a1.25 1.25 0 000 2.5h7.637a1.25 1.25 0 000-2.5H8.051a.25.25 0 01-.211-.384L19.22 2.98a.5.5 0 01.422-.232H22.5a1.25 1.25 0 000-2.5z"},null,-1),om=[nm];function im(e,t){return gt(),Mt("svg",rm,om)}var sm=At(em,[["render",im]]);const am={},cm={viewBox:"0 0 24 24"},lm=A("path",{d:"M7.75 4.5h15a1 1 0 000-2h-15a1 1 0 000 2zm15 6.5h-15a1 1 0 100 2h15a1 1 0 000-2zm0 8.5h-15a1 1 0 000 2h15a1 1 0 000-2zM2.212 17.248a2 2 0 00-1.933 1.484.75.75 0 101.45.386.5.5 0 11.483.63.75.75 0 100 1.5.5.5 0 11-.482.635.75.75 0 10-1.445.4 2 2 0 103.589-1.648.251.251 0 010-.278 2 2 0 00-1.662-3.111zm2.038-6.5a2 2 0 00-4 0 .75.75 0 001.5 0 .5.5 0 011 0 1.031 1.031 0 01-.227.645L.414 14.029A.75.75 0 001 15.248h2.5a.75.75 0 000-1.5h-.419a.249.249 0 01-.195-.406L3.7 12.33a2.544 2.544 0 00.55-1.582zM4 5.248h-.25A.25.25 0 013.5 5V1.623A1.377 1.377 0 002.125.248H1.5a.75.75 0 000 1.5h.25A.25.25 0 012 2v3a.25.25 0 01-.25.25H1.5a.75.75 0 000 1.5H4a.75.75 0 000-1.5z"},null,-1),um=[lm];function fm(e,t){return gt(),Mt("svg",cm,um)}var pm=At(am,[["render",fm]]);const dm={},hm={viewBox:"0 0 24 24"},mm=vc('',6),vm=[mm];function gm(e,t){return gt(),Mt("svg",hm,vm)}var ym=At(dm,[["render",gm]]);const bm={},km={viewBox:"0 0 24 24"},Sm=A("path",{d:"M22.5.248H7.228a6.977 6.977 0 100 13.954h2.318a.25.25 0 01.25.25V22.5a1.25 1.25 0 002.5 0V3a.25.25 0 01.25-.25h3.682a.25.25 0 01.25.25v19.5a1.25 1.25 0 002.5 0V3a.249.249 0 01.25-.25H22.5a1.25 1.25 0 000-2.5zM9.8 11.452a.25.25 0 01-.25.25H7.228a4.477 4.477 0 110-8.954h2.318A.25.25 0 019.8 3z"},null,-1),Mm=[Sm];function xm(e,t){return gt(),Mt("svg",km,Mm)}var Cm=At(bm,[["render",xm]]);const Om={},wm={viewBox:"0 0 24 24"},Tm=A("path",{d:"M18.559 3.932a4.942 4.942 0 100 9.883 4.609 4.609 0 001.115-.141.25.25 0 01.276.368 6.83 6.83 0 01-5.878 3.523 1.25 1.25 0 000 2.5 9.71 9.71 0 009.428-9.95V8.873a4.947 4.947 0 00-4.941-4.941zm-12.323 0a4.942 4.942 0 000 9.883 4.6 4.6 0 001.115-.141.25.25 0 01.277.368 6.83 6.83 0 01-5.878 3.523 1.25 1.25 0 000 2.5 9.711 9.711 0 009.428-9.95V8.873a4.947 4.947 0 00-4.942-4.941z"},null,-1),Am=[Tm];function _m(e,t){return gt(),Mt("svg",wm,Am)}var Nm=At(Om,[["render",_m]]);const Em={},Dm={viewBox:"0 0 24 24"},Im=A("path",{d:"M23.75 12.952A1.25 1.25 0 0022.5 11.7h-8.936a.492.492 0 01-.282-.09c-.722-.513-1.482-.981-2.218-1.432-2.8-1.715-4.5-2.9-4.5-4.863 0-2.235 2.207-2.569 3.523-2.569a4.54 4.54 0 013.081.764 2.662 2.662 0 01.447 1.99v.3a1.25 1.25 0 102.5 0v-.268a4.887 4.887 0 00-1.165-3.777C13.949.741 12.359.248 10.091.248c-3.658 0-6.023 1.989-6.023 5.069 0 2.773 1.892 4.512 4 5.927a.25.25 0 01-.139.458H1.5a1.25 1.25 0 000 2.5h10.977a.251.251 0 01.159.058 4.339 4.339 0 011.932 3.466c0 3.268-3.426 3.522-4.477 3.522-1.814 0-3.139-.405-3.834-1.173a3.394 3.394 0 01-.65-2.7 1.25 1.25 0 00-2.488-.246A5.76 5.76 0 004.4 21.753c1.2 1.324 3.114 2 5.688 2 4.174 0 6.977-2.42 6.977-6.022a6.059 6.059 0 00-.849-3.147.25.25 0 01.216-.377H22.5a1.25 1.25 0 001.25-1.255z"},null,-1),Rm=[Im];function Pm(e,t){return gt(),Mt("svg",Dm,Rm)}var Bm=At(Em,[["render",Pm]]);const zm={},Lm={viewBox:"0 0 24 24"},Fm=A("path",{d:"M17.786 3.77a12.542 12.542 0 00-12.965-.865.249.249 0 01-.292-.045L1.937.269A.507.507 0 001.392.16a.5.5 0 00-.308.462v6.7a.5.5 0 00.5.5h6.7a.5.5 0 00.354-.854L6.783 5.115a.253.253 0 01-.068-.228.249.249 0 01.152-.181 10 10 0 019.466 1.1 9.759 9.759 0 01.094 15.809 1.25 1.25 0 001.473 2.016 12.122 12.122 0 005.013-9.961 12.125 12.125 0 00-5.127-9.9z"},null,-1),Vm=[Fm];function Hm(e,t){return gt(),Mt("svg",Lm,Vm)}var jm=At(zm,[["render",Hm]]);const qm={},Jm={viewBox:"0 0 24 24"},Wm=A("path",{d:"M22.608.161a.5.5 0 00-.545.108L19.472 2.86a.25.25 0 01-.292.045 12.537 12.537 0 00-12.966.865A12.259 12.259 0 006.1 23.632a1.25 1.25 0 001.476-2.018 9.759 9.759 0 01.091-15.809 10 10 0 019.466-1.1.25.25 0 01.084.409l-1.85 1.85a.5.5 0 00.354.853h6.7a.5.5 0 00.5-.5V.623a.5.5 0 00-.313-.462z"},null,-1),Km=[Wm];function $m(e,t){return gt(),Mt("svg",Jm,Km)}var Um=At(qm,[["render",$m]]);const Gm={},Ym={viewBox:"0 0 24 24"},Xm=A("path",{d:"M9.147 21.552a1.244 1.244 0 01-.895-.378L.84 13.561a2.257 2.257 0 010-3.125l7.412-7.613a1.25 1.25 0 011.791 1.744l-6.9 7.083a.5.5 0 000 .7l6.9 7.082a1.25 1.25 0 01-.9 2.122zm5.707 0a1.25 1.25 0 01-.9-2.122l6.9-7.083a.5.5 0 000-.7l-6.9-7.082a1.25 1.25 0 011.791-1.744l7.411 7.612a2.257 2.257 0 010 3.125l-7.412 7.614a1.244 1.244 0 01-.89.38zm6.514-9.373z"},null,-1),Qm=[Xm];function Zm(e,t){return gt(),Mt("svg",Ym,Qm)}var tv=At(Gm,[["render",Zm]]);const ev={},rv={viewBox:"0 0 24 24"},nv=A("path",{fill:"currentColor","fill-rule":"evenodd",d:"M3.75 5.25h16.5a.75.75 0 1 1 0 1.5H3.75a.75.75 0 0 1 0-1.5zm4 4h8.5a.75.75 0 1 1 0 1.5h-8.5a.75.75 0 1 1 0-1.5zm-4 4h16.5a.75.75 0 1 1 0 1.5H3.75a.75.75 0 1 1 0-1.5zm4 4h8.5a.75.75 0 1 1 0 1.5h-8.5a.75.75 0 1 1 0-1.5z"},null,-1),ov=[nv];function iv(e,t){return gt(),Mt("svg",rv,ov)}var sv=At(ev,[["render",iv]]);const av={components:{EditorContent:qd,BoldIcon:Uh,CodingIcon:tm,ItalicIcon:sm,ListIcon:pm,ListUlIcon:ym,ParagraphIcon:Cm,QuoteIcon:Nm,StrikethroughIcon:Bm,UndoIcon:jm,RedoIcon:Um,CodeBlockIcon:tv,DotsVerticalIcon:gc,MenuCenterIcon:sv,MenuAlt2Icon:yc,MenuAlt3Icon:bc,MenuIcon:kc},props:{modelValue:{type:String,default:""},contentLoading:{type:Boolean,default:!1}},emits:["update:modelValue"],setup(e,{emit:t}){const r=Jd({content:e.modelValue,extensions:[Hh,jh.configure({types:["heading","paragraph"],alignments:["left","right","center","justify"]})],onUpdate:()=>{t("update:modelValue",r.value.getHTML())}});return Sc(()=>e.modelValue,n=>{r.value.getHTML()!==n&&r.value.commands.setContent(e.modelValue,!1)}),Mc(()=>{setTimeout(()=>{r.value.destroy()},500)}),{editor:r}}},cv={key:1,class:"box-border w-full text-sm leading-8 text-left bg-white border border-gray-200 rounded-md min-h-[200px] overflow-hidden"},lv={key:0,class:"editor-content"},uv={class:"flex justify-end p-2 border-b border-gray-200 md:hidden"},fv={class:"flex items-center justify-center w-6 h-6 ml-2 text-sm text-black bg-white rounded-sm md:h-9 md:w-9"},pv={class:"flex flex-wrap space-x-1"},dv={class:"hidden p-2 border-b border-gray-200 md:flex"},hv={class:"flex flex-wrap space-x-1"};function mv(e,t,r,n,o,i){const s=Q("BaseContentPlaceholdersBox"),a=Q("BaseContentPlaceholders"),c=Q("dots-vertical-icon"),l=Q("bold-icon"),u=Q("italic-icon"),f=Q("strikethrough-icon"),p=Q("coding-icon"),d=Q("paragraph-icon"),h=Q("list-ul-icon"),v=Q("list-icon"),g=Q("quote-icon"),M=Q("code-block-icon"),y=Q("undo-icon"),R=Q("redo-icon"),m=Q("BaseDropdown"),I=Q("menu-alt2-icon"),O=Q("menu-alt3-icon"),F=Q("menu-icon"),J=Q("menu-center-icon"),U=Q("editor-content");return r.contentLoading?(gt(),xc(a,{key:0},{default:an(()=>[V(s,{rounded:!0,class:"w-full",style:{height:"200px"}})]),_:1})):(gt(),Mt("div",cv,[n.editor?(gt(),Mt("div",lv,[A("div",uv,[V(m,{"width-class":"w-48"},{activator:an(()=>[A("div",fv,[V(c,{class:"w-6 h-6 text-gray-600"})])]),default:an(()=>[A("div",pv,[A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("bold")}]),onClick:t[0]||(t[0]=T=>n.editor.chain().focus().toggleBold().run())},[V(l,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("italic")}]),onClick:t[1]||(t[1]=T=>n.editor.chain().focus().toggleItalic().run())},[V(u,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("strike")}]),onClick:t[2]||(t[2]=T=>n.editor.chain().focus().toggleStrike().run())},[V(f,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("code")}]),onClick:t[3]||(t[3]=T=>n.editor.chain().focus().toggleCode().run())},[V(p,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("paragraph")}]),onClick:t[4]||(t[4]=T=>n.editor.chain().focus().setParagraph().run())},[V(d,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:1})}]),onClick:t[5]||(t[5]=T=>n.editor.chain().focus().toggleHeading({level:1}).run())}," H1 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:2})}]),onClick:t[6]||(t[6]=T=>n.editor.chain().focus().toggleHeading({level:2}).run())}," H2 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:3})}]),onClick:t[7]||(t[7]=T=>n.editor.chain().focus().toggleHeading({level:3}).run())}," H3 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("bulletList")}]),onClick:t[8]||(t[8]=T=>n.editor.chain().focus().toggleBulletList().run())},[V(h,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("orderedList")}]),onClick:t[9]||(t[9]=T=>n.editor.chain().focus().toggleOrderedList().run())},[V(v,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("blockquote")}]),onClick:t[10]||(t[10]=T=>n.editor.chain().focus().toggleBlockquote().run())},[V(g,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("codeBlock")}]),onClick:t[11]||(t[11]=T=>n.editor.chain().focus().toggleCodeBlock().run())},[V(M,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("undo")}]),onClick:t[12]||(t[12]=T=>n.editor.chain().focus().undo().run())},[V(y,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("redo")}]),onClick:t[13]||(t[13]=T=>n.editor.chain().focus().redo().run())},[V(R,{class:"h-3 cursor-pointer fill-current"})],2)])]),_:1})]),A("div",dv,[A("div",hv,[A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("bold")}]),onClick:t[14]||(t[14]=T=>n.editor.chain().focus().toggleBold().run())},[V(l,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("italic")}]),onClick:t[15]||(t[15]=T=>n.editor.chain().focus().toggleItalic().run())},[V(u,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("strike")}]),onClick:t[16]||(t[16]=T=>n.editor.chain().focus().toggleStrike().run())},[V(f,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("code")}]),onClick:t[17]||(t[17]=T=>n.editor.chain().focus().toggleCode().run())},[V(p,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("paragraph")}]),onClick:t[18]||(t[18]=T=>n.editor.chain().focus().setParagraph().run())},[V(d,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:1})}]),onClick:t[19]||(t[19]=T=>n.editor.chain().focus().toggleHeading({level:1}).run())}," H1 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:2})}]),onClick:t[20]||(t[20]=T=>n.editor.chain().focus().toggleHeading({level:2}).run())}," H2 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("heading",{level:3})}]),onClick:t[21]||(t[21]=T=>n.editor.chain().focus().toggleHeading({level:3}).run())}," H3 ",2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("bulletList")}]),onClick:t[22]||(t[22]=T=>n.editor.chain().focus().toggleBulletList().run())},[V(h,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("orderedList")}]),onClick:t[23]||(t[23]=T=>n.editor.chain().focus().toggleOrderedList().run())},[V(v,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("blockquote")}]),onClick:t[24]||(t[24]=T=>n.editor.chain().focus().toggleBlockquote().run())},[V(g,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("codeBlock")}]),onClick:t[25]||(t[25]=T=>n.editor.chain().focus().toggleCodeBlock().run())},[V(M,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("undo")}]),onClick:t[26]||(t[26]=T=>n.editor.chain().focus().undo().run())},[V(y,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive("redo")}]),onClick:t[27]||(t[27]=T=>n.editor.chain().focus().redo().run())},[V(R,{class:"h-3 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive({textAlign:"left"})}]),onClick:t[28]||(t[28]=T=>n.editor.chain().focus().setTextAlign("left").run())},[V(I,{class:"h-5 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive({textAlign:"right"})}]),onClick:t[29]||(t[29]=T=>n.editor.chain().focus().setTextAlign("right").run())},[V(O,{class:"h-5 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive({textAlign:"justify"})}]),onClick:t[30]||(t[30]=T=>n.editor.chain().focus().setTextAlign("justify").run())},[V(F,{class:"h-5 cursor-pointer fill-current"})],2),A("span",{class:L(["flex items-center justify-center w-6 h-6 rounded-sm cursor-pointer hover:bg-gray-100",{"bg-gray-200":n.editor.isActive({textAlign:"center"})}]),onClick:t[31]||(t[31]=T=>n.editor.chain().focus().setTextAlign("center").run())},[V(J,{class:"h-5 cursor-pointer fill-current"})],2)])]),V(U,{editor:n.editor,class:"box-border relative w-full text-sm leading-8 text-left editor__content"},null,8,["editor"])])):Cc("",!0)]))}var bv=At(av,[["render",mv]]);export{bv as default}; diff --git a/crater/public/build/assets/BaseListItem.3b6ffe7a.js b/crater/public/build/assets/BaseListItem.3b6ffe7a.js new file mode 100644 index 0000000..ef37c6a --- /dev/null +++ b/crater/public/build/assets/BaseListItem.3b6ffe7a.js @@ -0,0 +1 @@ +import{_ as o}from"./main.465728e1.js";import{o as n,e as i,g as l,k as c,r as d,l as m,w as _,j as f,h as $,t as h,s as B}from"./vendor.d12b5734.js";const k={name:"List"},v={class:"list-none"};function x(e,r,t,s,a,p){return n(),i("div",v,[l(e.$slots,"default")])}var L=o(k,[["render",x]]);const y={name:"ListItem",props:{title:{type:String,required:!1,default:""},active:{type:Boolean,required:!0},index:{type:Number,default:null}},setup(e,{slots:r}){const t="cursor-pointer pb-2 pr-0 text-sm font-medium leading-5 flex items-center";let s=c(()=>!!r.icon),a=c(()=>e.active?`${t} text-primary-500`:`${t} text-gray-500`);return{hasIconSlot:s,containerClass:a}}},g={key:0,class:"mr-3"};function C(e,r,t,s,a,p){const u=d("router-link");return n(),m(u,B(e.$attrs,{class:s.containerClass}),{default:_(()=>[s.hasIconSlot?(n(),i("span",g,[l(e.$slots,"icon")])):f("",!0),$("span",null,h(t.title),1)]),_:3},16,["class"])}var b=o(y,[["render",C]]);export{b as B,L as a}; diff --git a/crater/public/build/assets/BaseMultiselect.2950bd7a.js b/crater/public/build/assets/BaseMultiselect.2950bd7a.js new file mode 100644 index 0000000..db798b1 --- /dev/null +++ b/crater/public/build/assets/BaseMultiselect.2950bd7a.js @@ -0,0 +1 @@ +var Xe=Object.defineProperty,Ye=Object.defineProperties;var Ze=Object.getOwnPropertyDescriptors;var Be=Object.getOwnPropertySymbols;var $e=Object.prototype.hasOwnProperty,_e=Object.prototype.propertyIsEnumerable;var qe=(e,n,a)=>n in e?Xe(e,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[n]=a,G=(e,n)=>{for(var a in n||(n={}))$e.call(n,a)&&qe(e,a,n[a]);if(Be)for(var a of Be(n))_e.call(n,a)&&qe(e,a,n[a]);return e},Ce=(e,n)=>Ye(e,Ze(n));import{bd as x,B as N,k as w,C as re,be as Te,r as De,o as I,l as el,w as ll,f as al,e as B,m as O,U as ve,j as E,F as ae,y as se,g as T,i as tl,t as J,h as P}from"./vendor.d12b5734.js";import{_ as nl}from"./main.465728e1.js";function F(e){return[null,void 0,!1].indexOf(e)!==-1}function rl(e,n,a){const{object:i,valueProp:o,mode:v}=x(e),f=a.iv,g=p=>{f.value=c(p);const b=t(p);n.emit("change",b),n.emit("input",b),n.emit("update:modelValue",b)},t=p=>i.value||F(p)?p:Array.isArray(p)?p.map(b=>b[o.value]):p[o.value],c=p=>F(p)?v.value==="single"?{}:[]:p;return{update:g}}function sl(e,n){const{value:a,modelValue:i,mode:o,valueProp:v}=x(e),f=N(o.value!=="single"?[]:{}),g=n.expose!==void 0?i:a,t=w(()=>o.value==="single"?f.value[v.value]:f.value.map(p=>p[v.value])),c=w(()=>o.value!=="single"?f.value.map(p=>p[v.value]).join(","):f.value[v.value]);return{iv:f,internalValue:f,ev:g,externalValue:g,textValue:c,plainValue:t}}function ul(e,n,a){const{preserveSearch:i}=x(e),o=N(e.initialSearch)||N(null),v=N(null),f=()=>{i.value||(o.value="")},g=c=>{o.value=c.target.value},t=c=>{n.emit("paste",c)};return re(o,c=>{n.emit("search-change",c)}),{search:o,input:v,clearSearch:f,handleSearchInput:g,handlePaste:t}}function ol(e,n,a){const{groupSelect:i,mode:o,groups:v}=x(e),f=N(null),g=c=>{c===void 0||c!==null&&c.disabled||v.value&&c&&c.group&&(o.value==="single"||!i.value)||(f.value=c)};return{pointer:f,setPointer:g,clearPointer:()=>{g(null)}}}function Ee(e,n=!0){return n?String(e).toLowerCase().trim():String(e).normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase().trim()}function il(e){return Object.prototype.toString.call(e)==="[object Object]"}function cl(e,n){const a=n.slice().sort();return e.length===n.length&&e.slice().sort().every(function(i,o){return i===a[o]})}function dl(e,n,a){const{options:i,mode:o,trackBy:v,limit:f,hideSelected:g,createTag:t,label:c,appendNewTag:p,multipleLabel:b,object:q,loading:V,delay:D,resolveOnLoad:m,minChars:s,filterResults:A,clearOnSearch:Z,clearOnSelect:k,valueProp:d,canDeselect:j,max:L,strict:Q,closeOnSelect:X,groups:$,groupLabel:ue,groupOptions:M,groupHideEmpty:pe,groupSelect:fe}=x(e),S=a.iv,z=a.ev,C=a.search,_=a.clearSearch,ee=a.update,ge=a.pointer,oe=a.clearPointer,W=a.blur,te=a.deactivate,r=N([]),h=N([]),R=N(!1),H=w(()=>{if($.value){let l=h.value||[],u=[];return l.forEach(y=>{ke(y[M.value]).forEach(U=>{u.push(Object.assign({},U,y.disabled?{disabled:!0}:{}))})}),u}else{let l=ke(h.value||[]);return r.value.length&&(l=l.concat(r.value)),l}}),Oe=w(()=>$.value?Ue((h.value||[]).map(l=>{const u=ke(l[M.value]);return Ce(G({},l),{group:!0,[M.value]:Se(u,!1).map(y=>Object.assign({},y,l.disabled?{disabled:!0}:{})),__VISIBLE__:Se(u).map(y=>Object.assign({},y,l.disabled?{disabled:!0}:{}))})})):[]),ie=w(()=>{let l=H.value;return me.value.length&&(l=me.value.concat(l)),l=Se(l),f.value>0&&(l=l.slice(0,f.value)),l}),be=w(()=>{switch(o.value){case"single":return!F(S.value[d.value]);case"multiple":case"tags":return!F(S.value)&&S.value.length>0}}),Ve=w(()=>b!==void 0&&b.value!==void 0?b.value(S.value):S.value&&S.value.length>1?`${S.value.length} options selected`:"1 option selected"),je=w(()=>!H.value.length&&!R.value&&!me.value.length),Re=w(()=>H.value.length>0&&ie.value.length==0&&(C.value&&$.value||!$.value)),me=w(()=>t.value===!1||!C.value?[]:ze(C.value)!==-1?[]:[{[d.value]:C.value,[c.value]:C.value,[v.value]:C.value}]),Ge=w(()=>{switch(o.value){case"single":return null;case"multiple":case"tags":return[]}}),Ae=w(()=>V.value||R.value),ne=l=>{switch(typeof l!="object"&&(l=K(l)),o.value){case"single":ee(l);break;case"multiple":case"tags":ee(S.value.concat(l));break}n.emit("select",Le(l),l)},le=l=>{switch(typeof l!="object"&&(l=K(l)),o.value){case"single":Ie();break;case"tags":case"multiple":ee(Array.isArray(l)?S.value.filter(u=>l.map(y=>y[d.value]).indexOf(u[d.value])===-1):S.value.filter(u=>u[d.value]!=l[d.value]));break}n.emit("deselect",Le(l),l)},Le=l=>q.value?l:l[d.value],Pe=l=>{le(l)},Me=(l,u)=>{if(u.button!==0){u.preventDefault();return}Pe(l)},Ie=()=>{n.emit("clear"),ee(Ge.value)},Y=l=>{if(l.group!==void 0)return o.value==="single"?!1:Fe(l[M.value])&&l[M.value].length;switch(o.value){case"single":return!F(S.value)&&S.value[d.value]==l[d.value];case"tags":case"multiple":return!F(S.value)&&S.value.map(u=>u[d.value]).indexOf(l[d.value])!==-1}},he=l=>l.disabled===!0,ye=()=>L===void 0||L.value===-1||!be.value&&L.value>0?!1:S.value.length>=L.value,Ne=l=>{if(!he(l)){switch(o.value){case"single":if(Y(l)){j.value&&le(l);return}W(),ne(l);break;case"multiple":if(Y(l)){le(l);return}if(ye())return;ne(l),k.value&&_(),g.value&&oe(),X.value&&W();break;case"tags":if(Y(l)){le(l);return}if(ye())return;K(l[d.value])===void 0&&t.value&&(n.emit("tag",l[d.value]),p.value&&We(l),_()),k.value&&_(),ne(l),g.value&&oe(),X.value&&W();break}X.value&&te()}},He=l=>{if(!(he(l)||o.value==="single"||!fe.value)){switch(o.value){case"multiple":case"tags":xe(l[M.value])?le(l[M.value]):ne(l[M.value].filter(u=>S.value.map(y=>y[d.value]).indexOf(u[d.value])===-1).filter(u=>!u.disabled).filter((u,y)=>S.value.length+1+y<=L.value||L.value===-1));break}X.value&&te()}},xe=l=>l.find(u=>!Y(u)&&!u.disabled)===void 0,Fe=l=>l.find(u=>!Y(u))===void 0,K=l=>H.value[H.value.map(u=>String(u[d.value])).indexOf(String(l))],ze=(l,u=!0)=>H.value.map(y=>y[v.value]).indexOf(l),Ke=l=>["tags","multiple"].indexOf(o.value)!==-1&&g.value&&Y(l),We=l=>{r.value.push(l)},Ue=l=>pe.value?l.filter(u=>C.value?u.__VISIBLE__.length:u[M.value].length):l.filter(u=>C.value?u.__VISIBLE__.length:!0),Se=(l,u=!0)=>{let y=l;return C.value&&A.value&&(y=y.filter(U=>Ee(U[v.value],Q.value).indexOf(Ee(C.value,Q.value))!==-1)),g.value&&u&&(y=y.filter(U=>!Ke(U))),y},ke=l=>{let u=l;return il(u)&&(u=Object.keys(u).map(y=>{let U=u[y];return{[d.value]:y,[v.value]:U,[c.value]:U}})),u=u.map(y=>typeof y=="object"?y:{[d.value]:y,[v.value]:y,[c.value]:y}),u},ce=()=>{F(z.value)||(S.value=de(z.value))},we=l=>{R.value=!0,i.value(C.value).then(u=>{h.value=u,typeof l=="function"&&l(u),R.value=!1})},Je=()=>{if(!!be.value)if(o.value==="single"){let l=K(S.value[d.value])[c.value];S.value[c.value]=l,q.value&&(z.value[c.value]=l)}else S.value.forEach((l,u)=>{let y=K(S.value[u][d.value])[c.value];S.value[u][c.value]=y,q.value&&(z.value[u][c.value]=y)})},Qe=l=>{we(l)},de=l=>F(l)?o.value==="single"?{}:[]:q.value?l:o.value==="single"?K(l)||{}:l.filter(u=>!!K(u)).map(u=>K(u));if(o.value!=="single"&&!F(z.value)&&!Array.isArray(z.value))throw new Error(`v-model must be an array when using "${o.value}" mode`);return i&&typeof i.value=="function"?m.value?we(ce):q.value==!0&&ce():(h.value=i.value,ce()),D.value>-1&&re(C,l=>{l.length{l==C.value&&i.value(C.value).then(u=>{l==C.value&&(h.value=u,ge.value=ie.value.filter(y=>y.disabled!==!0)[0]||null,R.value=!1)})},D.value))},{flush:"sync"}),re(z,l=>{if(F(l)){S.value=de(l);return}switch(o.value){case"single":(q.value?l[d.value]!=S.value[d.value]:l!=S.value[d.value])&&(S.value=de(l));break;case"multiple":case"tags":cl(q.value?l.map(u=>u[d.value]):l,S.value.map(u=>u[d.value]))||(S.value=de(l));break}},{deep:!0}),typeof e.options!="function"&&re(i,(l,u)=>{h.value=e.options,Object.keys(S.value).length||ce(),Je()}),{fo:ie,filteredOptions:ie,hasSelected:be,multipleLabelText:Ve,eo:H,extendedOptions:H,fg:Oe,filteredGroups:Oe,noOptions:je,noResults:Re,resolving:R,busy:Ae,select:ne,deselect:le,remove:Pe,clear:Ie,isSelected:Y,isDisabled:he,isMax:ye,getOption:K,handleOptionClick:Ne,handleGroupClick:He,handleTagRemove:Me,refreshOptions:Qe,resolveOptions:we}}function vl(e,n,a){const{valueProp:i,showOptions:o,searchable:v,groupLabel:f,groups:g,mode:t,groupSelect:c}=x(e),p=a.fo,b=a.fg,q=a.handleOptionClick,V=a.handleGroupClick,D=a.search,m=a.pointer,s=a.setPointer,A=a.clearPointer,Z=a.multiselect,k=w(()=>p.value.filter(r=>!r.disabled)),d=w(()=>b.value.filter(r=>!r.disabled)),j=w(()=>t.value!=="single"&&c.value),L=w(()=>m.value&&m.value.group),Q=w(()=>W(m.value)),X=w(()=>{const r=L.value?m.value:W(m.value),h=d.value.map(H=>H[f.value]).indexOf(r[f.value]);let R=d.value[h-1];return R===void 0&&(R=ue.value),R}),$=w(()=>{let r=d.value.map(h=>h.label).indexOf(L.value?m.value[f.value]:W(m.value)[f.value])+1;return d.value.length<=r&&(r=0),d.value[r]}),ue=w(()=>[...d.value].slice(-1)[0]),M=w(()=>m.value.__VISIBLE__.filter(r=>!r.disabled)[0]),pe=w(()=>{const r=Q.value.__VISIBLE__.filter(h=>!h.disabled);return r[r.map(h=>h[i.value]).indexOf(m.value[i.value])-1]}),fe=w(()=>{const r=W(m.value).__VISIBLE__.filter(h=>!h.disabled);return r[r.map(h=>h[i.value]).indexOf(m.value[i.value])+1]}),S=w(()=>[...X.value.__VISIBLE__.filter(r=>!r.disabled)].slice(-1)[0]),z=w(()=>[...ue.value.__VISIBLE__.filter(r=>!r.disabled)].slice(-1)[0]),C=r=>{if(!!m.value)return r.group?m.value[f.value]==r[f.value]:m.value[i.value]==r[i.value]},_=()=>{s(k.value[0]||null)},ee=()=>{!m.value||m.value.disabled===!0||(L.value?V(m.value):q(m.value))},ge=()=>{if(m.value===null)s((g.value&&j.value?d.value[0]:k.value[0])||null);else if(g.value&&j.value){let r=L.value?M.value:fe.value;r===void 0&&(r=$.value),s(r||null)}else{let r=k.value.map(h=>h[i.value]).indexOf(m.value[i.value])+1;k.value.length<=r&&(r=0),s(k.value[r]||null)}Te(()=>{te()})},oe=()=>{if(m.value===null){let r=k.value[k.value.length-1];g.value&&j.value&&(r=z.value,r===void 0&&(r=ue.value)),s(r||null)}else if(g.value&&j.value){let r=L.value?S.value:pe.value;r===void 0&&(r=L.value?X.value:Q.value),s(r||null)}else{let r=k.value.map(h=>h[i.value]).indexOf(m.value[i.value])-1;r<0&&(r=k.value.length-1),s(k.value[r]||null)}Te(()=>{te()})},W=r=>d.value.find(h=>h.__VISIBLE__.map(R=>R[i.value]).indexOf(r[i.value])!==-1),te=()=>{let r=Z.value.querySelector("[data-pointed]");if(!r)return;let h=r.parentElement.parentElement;g.value&&(h=L.value?r.parentElement.parentElement.parentElement:r.parentElement.parentElement.parentElement.parentElement),r.offsetTop+r.offsetHeight>h.clientHeight+h.scrollTop&&(h.scrollTop=r.offsetTop+r.offsetHeight-h.clientHeight),r.offsetTop{v.value&&(r.length&&o.value?_():A())}),{pointer:m,canPointGroups:j,isPointed:C,setPointerFirst:_,selectPointer:ee,forwardPointer:ge,backwardPointer:oe}}function pl(e,n,a){const{disabled:i}=x(e),o=N(!1);return{isOpen:o,open:()=>{o.value||i.value||(o.value=!0,n.emit("open"))},close:()=>{!o.value||(o.value=!1,n.emit("close"))}}}function fl(e,n,a){const{searchable:i,disabled:o}=x(e),v=a.input,f=a.open,g=a.close,t=a.clearSearch,c=N(null),p=N(!1),b=w(()=>i.value||o.value?-1:0),q=()=>{i.value&&v.value.blur(),c.value.blur()},V=()=>{i.value&&!o.value&&v.value.focus()},D=()=>{o.value||(p.value=!0,f())},m=()=>{p.value=!1,setTimeout(()=>{p.value||(g(),t())},1)};return{multiselect:c,tabindex:b,isActive:p,blur:q,handleFocus:V,activate:D,deactivate:m,handleCaretClick:()=>{p.value?(m(),q()):D()}}}function gl(e,n,a){const{mode:i,addTagOn:o,createTag:v,openDirection:f,searchable:g,showOptions:t,valueProp:c,groups:p}=x(e),b=a.iv,q=a.update,V=a.search,D=a.setPointer,m=a.selectPointer,s=a.backwardPointer,A=a.forwardPointer,Z=a.blur,k=a.fo,d=()=>{i.value==="tags"&&!t.value&&v.value&&g.value&&!p.value&&D(k.value[k.value.map(L=>L[c.value]).indexOf(V.value)])};return{handleKeydown:L=>{switch(L.keyCode){case 8:if(i.value==="single"||g.value&&[null,""].indexOf(V.value)===-1||b.value.length===0)return;q([...b.value].slice(0,-1));break;case 13:if(L.preventDefault(),i.value==="tags"&&o.value.indexOf("enter")===-1&&v.value)return;d(),m();break;case 32:if(g.value&&i.value!=="tags"&&!v.value||i.value==="tags"&&(o.value.indexOf("space")===-1&&v.value||!v.value))return;L.preventDefault(),d(),m();break;case 9:case 186:case 188:if(i.value!=="tags")return;const Q={9:"tab",186:";",188:","};if(o.value.indexOf(Q[L.keyCode])===-1||!v.value)return;d(),m(),L.preventDefault();break;case 27:Z();break;case 38:if(L.preventDefault(),!t.value)return;f.value==="top"?A():s();break;case 40:if(L.preventDefault(),!t.value)return;f.value==="top"?s():A();break}},preparePointer:d}}function bl(e,n,a){const i=x(e),{disabled:o,openDirection:v,showOptions:f,invalid:g}=i,t=a.isOpen,c=a.isPointed,p=a.isSelected,b=a.isDisabled,q=a.isActive,V=a.canPointGroups,D=a.resolving,m=a.fo,s=G({container:"multiselect",containerDisabled:"is-disabled",containerOpen:"is-open",containerOpenTop:"is-open-top",containerActive:"is-active",containerInvalid:"is-invalid",containerInvalidActive:"is-invalid-active",singleLabel:"multiselect-single-label",multipleLabel:"multiselect-multiple-label",search:"multiselect-search",tags:"multiselect-tags",tag:"multiselect-tag",tagDisabled:"is-disabled",tagRemove:"multiselect-tag-remove",tagRemoveIcon:"multiselect-tag-remove-icon",tagsSearchWrapper:"multiselect-tags-search-wrapper",tagsSearch:"multiselect-tags-search",tagsSearchCopy:"multiselect-tags-search-copy",placeholder:"multiselect-placeholder",caret:"multiselect-caret",caretOpen:"is-open",clear:"multiselect-clear",clearIcon:"multiselect-clear-icon",spinner:"multiselect-spinner",dropdown:"multiselect-dropdown",dropdownTop:"is-top",dropdownHidden:"is-hidden",options:"multiselect-options",optionsTop:"is-top",group:"multiselect-group",groupLabel:"multiselect-group-label",groupLabelPointable:"is-pointable",groupLabelPointed:"is-pointed",groupLabelSelected:"is-selected",groupLabelDisabled:"is-disabled",groupLabelSelectedPointed:"is-selected is-pointed",groupLabelSelectedDisabled:"is-selected is-disabled",groupOptions:"multiselect-group-options",option:"multiselect-option",optionPointed:"is-pointed",optionSelected:"is-selected",optionDisabled:"is-disabled",optionSelectedPointed:"is-selected is-pointed",optionSelectedDisabled:"is-selected is-disabled",noOptions:"multiselect-no-options",noResults:"multiselect-no-results",fakeInput:"multiselect-fake-input",spacer:"multiselect-spacer"},i.classes.value),A=w(()=>!!(t.value&&f.value&&(!D.value||D.value&&m.value.length)));return{classList:w(()=>({container:[s.container].concat(o.value?s.containerDisabled:[]).concat(A.value&&v.value==="top"?s.containerOpenTop:[]).concat(A.value&&v.value!=="top"?s.containerOpen:[]).concat(q.value?s.containerActive:[]).concat(g.value?s.containerInvalid:[]),spacer:s.spacer,singleLabel:s.singleLabel,multipleLabel:s.multipleLabel,search:s.search,tags:s.tags,tag:[s.tag].concat(o.value?s.tagDisabled:[]),tagRemove:s.tagRemove,tagRemoveIcon:s.tagRemoveIcon,tagsSearchWrapper:s.tagsSearchWrapper,tagsSearch:s.tagsSearch,tagsSearchCopy:s.tagsSearchCopy,placeholder:s.placeholder,caret:[s.caret].concat(t.value?s.caretOpen:[]),clear:s.clear,clearIcon:s.clearIcon,spinner:s.spinner,dropdown:[s.dropdown].concat(v.value==="top"?s.dropdownTop:[]).concat(!t.value||!f.value||!A.value?s.dropdownHidden:[]),options:[s.options].concat(v.value==="top"?s.optionsTop:[]),group:s.group,groupLabel:k=>{let d=[s.groupLabel];return c(k)?d.push(p(k)?s.groupLabelSelectedPointed:s.groupLabelPointed):p(k)&&V.value?d.push(b(k)?s.groupLabelSelectedDisabled:s.groupLabelSelected):b(k)&&d.push(s.groupLabelDisabled),V.value&&d.push(s.groupLabelPointable),d},groupOptions:s.groupOptions,option:(k,d)=>{let j=[s.option];return c(k)?j.push(p(k)?s.optionSelectedPointed:s.optionPointed):p(k)?j.push(b(k)?s.optionSelectedDisabled:s.optionSelected):(b(k)||d&&b(d))&&j.push(s.optionDisabled),j},noOptions:s.noOptions,noResults:s.noResults,fakeInput:s.fakeInput})),showDropdown:A}}const ml={name:"BaseMultiselect",props:{preserveSearch:{type:Boolean,default:!1},initialSearch:{type:String,default:null},contentLoading:{type:Boolean,default:!1},value:{required:!1},modelValue:{required:!1},options:{type:[Array,Object,Function],required:!1,default:()=>[]},id:{type:[String,Number],required:!1},name:{type:[String,Number],required:!1,default:"multiselect"},disabled:{type:Boolean,required:!1,default:!1},label:{type:String,required:!1,default:"label"},trackBy:{type:String,required:!1,default:"label"},valueProp:{type:String,required:!1,default:"value"},placeholder:{type:String,required:!1,default:null},mode:{type:String,required:!1,default:"single"},searchable:{type:Boolean,required:!1,default:!1},limit:{type:Number,required:!1,default:-1},hideSelected:{type:Boolean,required:!1,default:!0},createTag:{type:Boolean,required:!1,default:!1},appendNewTag:{type:Boolean,required:!1,default:!0},caret:{type:Boolean,required:!1,default:!0},loading:{type:Boolean,required:!1,default:!1},noOptionsText:{type:String,required:!1,default:"The list is empty"},noResultsText:{type:String,required:!1,default:"No results found"},multipleLabel:{type:Function,required:!1},object:{type:Boolean,required:!1,default:!1},delay:{type:Number,required:!1,default:-1},minChars:{type:Number,required:!1,default:0},resolveOnLoad:{type:Boolean,required:!1,default:!0},filterResults:{type:Boolean,required:!1,default:!0},clearOnSearch:{type:Boolean,required:!1,default:!1},clearOnSelect:{type:Boolean,required:!1,default:!0},canDeselect:{type:Boolean,required:!1,default:!0},canClear:{type:Boolean,required:!1,default:!1},max:{type:Number,required:!1,default:-1},showOptions:{type:Boolean,required:!1,default:!0},addTagOn:{type:Array,required:!1,default:()=>["enter"]},required:{type:Boolean,required:!1,default:!1},openDirection:{type:String,required:!1,default:"bottom"},nativeSupport:{type:Boolean,required:!1,default:!1},invalid:{type:Boolean,required:!1,default:!1},classes:{type:Object,required:!1,default:()=>({container:"p-0 relative mx-auto w-full flex items-center justify-end box-border cursor-pointer border border-gray-200 rounded-md bg-white text-sm leading-snug outline-none max-h-10",containerDisabled:"cursor-default bg-gray-200 bg-opacity-50 !text-gray-400",containerOpen:"",containerOpenTop:"",containerActive:"ring-1 ring-primary-400 border-primary-400",containerInvalid:"border-red-400 ring-red-400 focus:ring-red-400 focus:border-red-400",containerInvalidActive:"ring-1 border-red-400 ring-red-400",singleLabel:"flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5",multipleLabel:"flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5",search:"w-full absolute inset-0 outline-none appearance-none box-border border-0 text-sm font-sans bg-white rounded-md pl-3.5",tags:"grow shrink flex flex-wrap mt-1 pl-2",tag:"bg-primary-500 text-white text-sm font-semibold py-0.5 pl-2 rounded mr-1 mb-1 flex items-center whitespace-nowrap",tagDisabled:"pr-2 !bg-gray-400 text-white",tagRemove:"flex items-center justify-center p-1 mx-0.5 rounded-sm hover:bg-black hover:bg-opacity-10 group",tagRemoveIcon:"bg-multiselect-remove text-white bg-center bg-no-repeat opacity-30 inline-block w-3 h-3 group-hover:opacity-60",tagsSearchWrapper:"inline-block relative mx-1 mb-1 grow shrink h-full",tagsSearch:"absolute inset-0 border-0 focus:outline-none !shadow-none !focus:shadow-none appearance-none p-0 text-sm font-sans box-border w-full",tagsSearchCopy:"invisible whitespace-pre-wrap inline-block h-px",placeholder:"flex items-center h-full absolute left-0 top-0 pointer-events-none bg-transparent leading-snug pl-3.5 text-gray-400 text-sm",caret:"bg-multiselect-caret bg-center bg-no-repeat w-5 h-5 py-px box-content z-5 relative mr-1 opacity-40 shrink-0 grow-0 transition-transform",caretOpen:"rotate-180 pointer-events-auto",clear:"pr-3.5 relative z-10 opacity-40 transition duration-300 shrink-0 grow-0 flex hover:opacity-80",clearIcon:"bg-multiselect-remove bg-center bg-no-repeat w-2.5 h-4 py-px box-content inline-block",spinner:"bg-multiselect-spinner bg-center bg-no-repeat w-4 h-4 z-10 mr-3.5 animate-spin shrink-0 grow-0",dropdown:"max-h-60 shadow-lg absolute -left-px -right-px -bottom-1 translate-y-full border border-gray-300 mt-1 overflow-y-auto z-50 bg-white flex flex-col rounded-md",dropdownTop:"-translate-y-full -top-2 bottom-auto flex-col-reverse rounded-md",dropdownHidden:"hidden",options:"flex flex-col p-0 m-0 list-none",optionsTop:"flex-col-reverse",group:"p-0 m-0",groupLabel:"flex text-sm box-border items-center justify-start text-left py-1 px-3 font-semibold bg-gray-200 cursor-default leading-normal",groupLabelPointable:"cursor-pointer",groupLabelPointed:"bg-gray-300 text-gray-700",groupLabelSelected:"bg-primary-600 text-white",groupLabelDisabled:"bg-gray-100 text-gray-300 cursor-not-allowed",groupLabelSelectedPointed:"bg-primary-600 text-white opacity-90",groupLabelSelectedDisabled:"text-primary-100 bg-primary-600 bg-opacity-50 cursor-not-allowed",groupOptions:"p-0 m-0",option:"flex items-center justify-start box-border text-left cursor-pointer text-sm leading-snug py-2 px-3",optionPointed:"text-gray-800 bg-gray-100",optionSelected:"text-white bg-primary-500",optionDisabled:"text-gray-300 cursor-not-allowed",optionSelectedPointed:"text-white bg-primary-500 opacity-90",optionSelectedDisabled:"text-primary-100 bg-primary-500 bg-opacity-50 cursor-not-allowed",noOptions:"py-2 px-3 text-gray-600 bg-white",noResults:"py-2 px-3 text-gray-600 bg-white",fakeInput:"bg-transparent absolute left-0 right-0 -bottom-px w-full h-px border-0 p-0 appearance-none outline-none text-transparent",spacer:"h-9 py-px box-content"})},strict:{type:Boolean,required:!1,default:!0},closeOnSelect:{type:Boolean,required:!1,default:!0},autocomplete:{type:String,required:!1},groups:{type:Boolean,required:!1,default:!1},groupLabel:{type:String,required:!1,default:"label"},groupOptions:{type:String,required:!1,default:"options"},groupHideEmpty:{type:Boolean,required:!1,default:!1},groupSelect:{type:Boolean,required:!1,default:!0},inputType:{type:String,required:!1,default:"text"}},emits:["open","close","select","deselect","input","search-change","tag","update:modelValue","change","clear"],setup(e,n){const a=sl(e,n),i=ol(e),o=pl(e,n),v=ul(e,n),f=rl(e,n,{iv:a.iv}),g=fl(e,n,{input:v.input,open:o.open,close:o.close,clearSearch:v.clearSearch}),t=dl(e,n,{ev:a.ev,iv:a.iv,search:v.search,clearSearch:v.clearSearch,update:f.update,pointer:i.pointer,clearPointer:i.clearPointer,blur:g.blur,deactivate:g.deactivate}),c=vl(e,n,{fo:t.fo,fg:t.fg,handleOptionClick:t.handleOptionClick,handleGroupClick:t.handleGroupClick,search:v.search,pointer:i.pointer,setPointer:i.setPointer,clearPointer:i.clearPointer,multiselect:g.multiselect}),p=gl(e,n,{iv:a.iv,update:f.update,search:v.search,setPointer:i.setPointer,selectPointer:c.selectPointer,backwardPointer:c.backwardPointer,forwardPointer:c.forwardPointer,blur:g.blur,fo:t.fo}),b=bl(e,n,{isOpen:o.isOpen,isPointed:c.isPointed,canPointGroups:c.canPointGroups,isSelected:t.isSelected,isDisabled:t.isDisabled,isActive:g.isActive,resolving:t.resolving,fo:t.fo});return G(G(G(G(G(G(G(G(G(G({},a),o),g),i),f),v),t),c),p),b)}},hl=["id","tabindex"],yl=["type","modelValue","value","autocomplete"],Sl=["onMousedown"],kl=["type","modelValue","value","autocomplete"],wl={class:"w-full overflow-y-auto"},Ol=["data-pointed","onMouseenter","onClick"],Ll=["data-pointed","onMouseenter","onClick"],Pl=["data-pointed","onMouseenter","onClick"],Il=["innerHTML"],Bl=["innerHTML"],ql=["value"],Cl=["name","value"],Tl=["name","value"];function Dl(e,n,a,i,o,v){const f=De("BaseContentPlaceholdersBox"),g=De("BaseContentPlaceholders");return a.contentLoading?(I(),el(g,{key:0},{default:ll(()=>[al(f,{rounded:!0,class:"w-full",style:{height:"40px"}})]),_:1})):(I(),B("div",{key:1,id:a.id,ref:"multiselect",tabindex:e.tabindex,class:O(e.classList.container),onFocusin:n[6]||(n[6]=(...t)=>e.activate&&e.activate(...t)),onFocusout:n[7]||(n[7]=(...t)=>e.deactivate&&e.deactivate(...t)),onKeydown:n[8]||(n[8]=(...t)=>e.handleKeydown&&e.handleKeydown(...t)),onFocus:n[9]||(n[9]=(...t)=>e.handleFocus&&e.handleFocus(...t))},[a.mode!=="tags"&&a.searchable&&!a.disabled?(I(),B("input",{key:0,ref:"input",type:a.inputType,modelValue:e.search,value:e.search,class:O(e.classList.search),autocomplete:a.autocomplete,onInput:n[0]||(n[0]=(...t)=>e.handleSearchInput&&e.handleSearchInput(...t)),onPaste:n[1]||(n[1]=ve((...t)=>e.handlePaste&&e.handlePaste(...t),["stop"]))},null,42,yl)):E("",!0),a.mode=="tags"?(I(),B("div",{key:1,class:O(e.classList.tags)},[(I(!0),B(ae,null,se(e.iv,(t,c,p)=>T(e.$slots,"tag",{option:t,handleTagRemove:e.handleTagRemove,disabled:a.disabled},()=>[(I(),B("span",{key:p,class:O(e.classList.tag)},[tl(J(t[a.label])+" ",1),a.disabled?E("",!0):(I(),B("span",{key:0,class:O(e.classList.tagRemove),onMousedown:ve(b=>e.handleTagRemove(t,b),["stop"])},[P("span",{class:O(e.classList.tagRemoveIcon)},null,2)],42,Sl))],2))])),256)),P("div",{class:O(e.classList.tagsSearchWrapper)},[P("span",{class:O(e.classList.tagsSearchCopy)},J(e.search),3),a.searchable&&!a.disabled?(I(),B("input",{key:0,ref:"input",type:a.inputType,modelValue:e.search,value:e.search,class:O(e.classList.tagsSearch),autocomplete:a.autocomplete,style:{"box-shadow":"none !important"},onInput:n[2]||(n[2]=(...t)=>e.handleSearchInput&&e.handleSearchInput(...t)),onPaste:n[3]||(n[3]=ve((...t)=>e.handlePaste&&e.handlePaste(...t),["stop"]))},null,42,kl)):E("",!0)],2)],2)):E("",!0),a.mode=="single"&&e.hasSelected&&!e.search&&e.iv?T(e.$slots,"singlelabel",{key:2,value:e.iv},()=>[P("div",{class:O(e.classList.singleLabel)},J(e.iv[a.label]),3)]):E("",!0),a.mode=="multiple"&&e.hasSelected&&!e.search?T(e.$slots,"multiplelabel",{key:3,values:e.iv},()=>[P("div",{class:O(e.classList.multipleLabel)},J(e.multipleLabelText),3)]):E("",!0),a.placeholder&&!e.hasSelected&&!e.search?T(e.$slots,"placeholder",{key:4},()=>[P("div",{class:O(e.classList.placeholder)},J(a.placeholder),3)]):E("",!0),e.busy?T(e.$slots,"spinner",{key:5},()=>[P("span",{class:O(e.classList.spinner)},null,2)]):E("",!0),e.hasSelected&&!a.disabled&&a.canClear&&!e.busy?T(e.$slots,"clear",{key:6,clear:e.clear},()=>[P("span",{class:O(e.classList.clear),onMousedown:n[4]||(n[4]=(...t)=>e.clear&&e.clear(...t))},[P("span",{class:O(e.classList.clearIcon)},null,2)],34)]):E("",!0),a.caret?T(e.$slots,"caret",{key:7},()=>[P("span",{class:O(e.classList.caret),onMousedown:n[5]||(n[5]=ve((...t)=>e.handleCaretClick&&e.handleCaretClick(...t),["prevent","stop"]))},null,34)]):E("",!0),P("div",{class:O(e.classList.dropdown),tabindex:"-1"},[P("div",wl,[T(e.$slots,"beforelist",{options:e.fo}),P("ul",{class:O(e.classList.options)},[a.groups?(I(!0),B(ae,{key:0},se(e.fg,(t,c,p)=>(I(),B("li",{key:p,class:O(e.classList.group)},[P("div",{class:O(e.classList.groupLabel(t)),"data-pointed":e.isPointed(t),onMouseenter:b=>e.setPointer(t),onClick:b=>e.handleGroupClick(t)},[T(e.$slots,"grouplabel",{group:t},()=>[P("span",null,J(t[a.groupLabel]),1)])],42,Ol),P("ul",{class:O(e.classList.groupOptions)},[(I(!0),B(ae,null,se(t.__VISIBLE__,(b,q,V)=>(I(),B("li",{key:V,class:O(e.classList.option(b,t)),"data-pointed":e.isPointed(b),onMouseenter:D=>e.setPointer(b),onClick:D=>e.handleOptionClick(b)},[T(e.$slots,"option",{option:b,search:e.search},()=>[P("span",null,J(b[a.label]),1)])],42,Ll))),128))],2)],2))),128)):(I(!0),B(ae,{key:1},se(e.fo,(t,c,p)=>(I(),B("li",{key:p,class:O(e.classList.option(t)),"data-pointed":e.isPointed(t),onMouseenter:b=>e.setPointer(t),onClick:b=>e.handleOptionClick(t)},[T(e.$slots,"option",{option:t,search:e.search},()=>[P("span",null,J(t[a.label]),1)])],42,Pl))),128))],2),e.noOptions?T(e.$slots,"nooptions",{key:0},()=>[P("div",{class:O(e.classList.noOptions),innerHTML:a.noOptionsText},null,10,Il)]):E("",!0),e.noResults?T(e.$slots,"noresults",{key:1},()=>[P("div",{class:O(e.classList.noResults),innerHTML:a.noResultsText},null,10,Bl)]):E("",!0),T(e.$slots,"afterlist",{options:e.fo})]),T(e.$slots,"action")],2),a.required?(I(),B("input",{key:8,class:O(e.classList.fakeInput),tabindex:"-1",value:e.textValue,required:""},null,10,ql)):E("",!0),a.nativeSupport?(I(),B(ae,{key:9},[a.mode=="single"?(I(),B("input",{key:0,type:"hidden",name:a.name,value:e.plainValue!==void 0?e.plainValue:""},null,8,Cl)):(I(!0),B(ae,{key:1},se(e.plainValue,(t,c)=>(I(),B("input",{key:c,type:"hidden",name:`${a.name}[]`,value:t},null,8,Tl))),128))],64)):E("",!0),P("div",{class:O(e.classList.spacer)},null,2)],42,hl))}var Rl=nl(ml,[["render",Dl]]);export{Rl as default}; diff --git a/crater/public/build/assets/BaseTable.ec8995dc.js b/crater/public/build/assets/BaseTable.ec8995dc.js new file mode 100644 index 0000000..e744c68 --- /dev/null +++ b/crater/public/build/assets/BaseTable.ec8995dc.js @@ -0,0 +1 @@ +import{I as O,r as T,o as i,e as s,h as u,m as c,t as h,j as m,f as k,F as C,y as P,i as _,a0 as N,B as F,k as A,C as J,D as K,g as L,u as y,w as Q,A as U,l as X}from"./vendor.d12b5734.js";import{_ as Z,S as $}from"./main.465728e1.js";function V(a,t){if(!t||a===null||typeof a!="object")return a;const[e,n]=t.split(/\.(.+)/);return V(a[e],n)}function ee(a,t){return t.reduce((e,n)=>(e[n]=a[n],e),{})}class te{constructor(t,e){this.data=t,this.columns=e}getValue(t){return V(this.data,t)}getColumn(t){return this.columns.find(e=>e.key===t)}getSortableValue(t){const e=this.getColumn(t).dataType;let n=this.getValue(t);if(n==null)return"";if(n instanceof String&&(n=n.toLowerCase()),e.startsWith("date")){const b=e.replace("date:","");return O(n,b).format("YYYYMMDDHHmmss")}return e==="numeric"?n:n.toString()}}class ae{constructor(t){const e=ee(t,["key","label","thClass","tdClass","sortBy","sortable","hidden","dataType"]);for(const n in e)this[n]=t[n];e.dataType||(this.dataType="string"),e.sortable===void 0&&(this.sortable=!0)}getFilterFieldName(){return this.filterOn||this.key}isSortable(){return this.sortable}getSortPredicate(t,e){const n=this.getSortFieldName(),l=e.find(g=>g.key===n).dataType;return l.startsWith("date")||l==="numeric"?(g,d)=>{const p=g.getSortableValue(n),x=d.getSortableValue(n);return t==="desc"?x{const p=g.getSortableValue(n),x=d.getSortableValue(n);return t==="desc"?x.localeCompare(p):p.localeCompare(x)}}getSortFieldName(){return this.sortBy||this.key}}const ne={props:{pagination:{type:Object,default:()=>({})}},computed:{pages(){return this.pagination.totalPages===void 0?[]:this.pageLinks()},hasFirst(){return this.pagination.currentPage>=4||this.pagination.totalPages<10},hasLast(){return this.pagination.currentPage<=this.pagination.totalPages-3||this.pagination.totalPages<10},hasFirstEllipsis(){return this.pagination.currentPage>=4&&this.pagination.totalPages>=10},hasLastEllipsis(){return this.pagination.currentPage<=this.pagination.totalPages-3&&this.pagination.totalPages>=10},shouldShowPagination(){return this.pagination.totalPages===void 0||this.pagination.count===0?!1:this.pagination.totalPages>1}},methods:{isActive(a){return(this.pagination.currentPage||1)===a},pageClicked(a){a==="..."||a===this.pagination.currentPage||a>this.pagination.totalPages||a<1||this.$emit("pageChange",a)},pageLinks(){const a=[];let t=2,e=this.pagination.totalPages-1;this.pagination.totalPages>=10&&(t=Math.max(1,this.pagination.currentPage-2),e=Math.min(this.pagination.currentPage+2,this.pagination.totalPages));for(let n=t;n<=e;n++)a.push(n);return a}}},re={key:0,class:"flex items-center justify-between px-4 py-3 bg-white border-t border-gray-200 sm:px-6"},ie={class:"flex justify-between flex-1 sm:hidden"},se={class:"hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"},le={class:"text-sm text-gray-700"},oe=_(" Showing "+h(" ")+" "),de={key:0,class:"font-medium"},ge=_(" "+h(" ")+" to "+h(" ")+" "),ue={key:1,class:"font-medium"},ce={key:0},he={key:1},ye=_(" "+h(" ")+" of "+h(" ")+" "),fe={key:2,class:"font-medium"},me=_(" "+h(" ")+" results "),pe={class:"relative z-0 inline-flex -space-x-px rounded-md shadow-sm","aria-label":"Pagination"},be=u("span",{class:"sr-only"},"Previous",-1),xe={key:1,class:"relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300"},ve=["onClick"],ke={key:2,class:"relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300"},Ce=u("span",{class:"sr-only"},"Next",-1);function Pe(a,t,e,n,b,l){const g=T("BaseIcon");return l.shouldShowPagination?(i(),s("div",re,[u("div",ie,[u("a",{href:"#",class:c([{"disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400":e.pagination.currentPage===1},"relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"]),onClick:t[0]||(t[0]=d=>l.pageClicked(e.pagination.currentPage-1))}," Previous ",2),u("a",{href:"#",class:c([{"disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400":e.pagination.currentPage===e.pagination.totalPages},"relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"]),onClick:t[1]||(t[1]=d=>l.pageClicked(e.pagination.currentPage+1))}," Next ",2)]),u("div",se,[u("div",null,[u("p",le,[oe,e.pagination.limit&&e.pagination.currentPage?(i(),s("span",de,h(e.pagination.currentPage*e.pagination.limit-(e.pagination.limit-1)),1)):m("",!0),ge,e.pagination.limit&&e.pagination.currentPage?(i(),s("span",ue,[e.pagination.currentPage*e.pagination.limit<=e.pagination.totalCount?(i(),s("span",ce,h(e.pagination.currentPage*e.pagination.limit),1)):(i(),s("span",he,h(e.pagination.totalCount),1))])):m("",!0),ye,e.pagination.totalCount?(i(),s("span",fe,h(e.pagination.totalCount),1)):m("",!0),me])]),u("div",null,[u("nav",pe,[u("a",{href:"#",class:c([{"disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400":e.pagination.currentPage===1},"relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md hover:bg-gray-50"]),onClick:t[2]||(t[2]=d=>l.pageClicked(e.pagination.currentPage-1))},[be,k(g,{name:"ChevronLeftIcon"})],2),l.hasFirst?(i(),s("a",{key:0,href:"#","aria-current":"page",class:c([{"z-10 bg-primary-50 border-primary-500 text-primary-600":l.isActive(1),"bg-white border-gray-300 text-gray-500 hover:bg-gray-50":!l.isActive(1)},"relative inline-flex items-center px-4 py-2 text-sm font-medium border"]),onClick:t[3]||(t[3]=d=>l.pageClicked(1))}," 1 ",2)):m("",!0),l.hasFirstEllipsis?(i(),s("span",xe," ... ")):m("",!0),(i(!0),s(C,null,P(l.pages,d=>(i(),s("a",{key:d,href:"#",class:c([{"z-10 bg-primary-50 border-primary-500 text-primary-600":l.isActive(d),"bg-white border-gray-300 text-gray-500 hover:bg-gray-50":!l.isActive(d),disabled:d==="..."},"relative items-center hidden px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 hover:bg-gray-50 md:inline-flex"]),onClick:p=>l.pageClicked(d)},h(d),11,ve))),128)),l.hasLastEllipsis?(i(),s("span",ke," ... ")):m("",!0),l.hasLast?(i(),s("a",{key:3,href:"#","aria-current":"page",class:c([{"z-10 bg-primary-50 border-primary-500 text-primary-600":l.isActive(e.pagination.totalPages),"bg-white border-gray-300 text-gray-500 hover:bg-gray-50":!l.isActive(e.pagination.totalPages)},"relative inline-flex items-center px-4 py-2 text-sm font-medium border"]),onClick:t[4]||(t[4]=d=>l.pageClicked(e.pagination.totalPages))},h(e.pagination.totalPages),3)):m("",!0),u("a",{href:"#",class:c(["relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md hover:bg-gray-50",{"disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400":e.pagination.currentPage===e.pagination.totalPages}]),onClick:t[5]||(t[5]=d=>l.pageClicked(e.pagination.currentPage+1))},[Ce,k(g,{name:"ChevronRightIcon"})],2)])])])])):m("",!0)}var _e=Z(ne,[["render",Pe]]);const we={class:"flex flex-col"},Se={class:"-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8 pb-4 lg:pb-0"},Te={class:"inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"},Ne={class:"relative overflow-hidden bg-white border-b border-gray-200 shadow sm:rounded-lg"},Be=["onClick"],Fe={key:0,class:"asc-direction"},Ae={key:1,class:"desc-direction"},Le={key:0},Ve={key:1},Ie={key:0,class:"absolute top-0 left-0 z-10 flex items-center justify-center w-full h-full bg-white bg-opacity-60"},De={key:1,class:"text-center text-gray-500 pb-2 flex h-[160px] justify-center items-center flex-col"},Me={class:"block mt-1"},Re={props:{columns:{type:Array,required:!0},data:{type:[Array,Function],required:!0},sortBy:{type:String,default:""},sortOrder:{type:String,default:""},tableClass:{type:String,default:"min-w-full divide-y divide-gray-200"},theadClass:{type:String,default:"bg-gray-50"},tbodyClass:{type:String,default:""},noResultsMessage:{type:String,default:"No Results Found"},loading:{type:Boolean,default:!1},loadingType:{type:String,default:"placeholder",validator:function(a){return["placeholder","spinner"].indexOf(a)!==-1}},placeholderCount:{type:Number,default:3}},setup(a,{expose:t}){const e=a;let n=N([]),b=F(!1),l=N(e.columns.map(r=>new ae(r))),g=N({fieldName:"",order:""}),d=F("");const p=A(()=>Array.isArray(e.data)),x=A(()=>{if(!p.value||g.fieldName===""||l.length===0)return n.value;const r=I(g.fieldName);return r?[...n.value].sort(r.getSortPredicate(g.order,l)):n.value});function I(r){return l.find(o=>o.key===r)}function D(r){let o="whitespace-nowrap px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider";return r.defaultThClass&&(o=r.defaultThClass),r.sortable?o=`${o} cursor-pointer`:o=`${o} pointer-events-none`,r.thClass&&(o=`${o} ${r.thClass}`),o}function B(r){let o="px-6 py-4 text-sm text-gray-500 whitespace-nowrap";return r.defaultTdClass&&(o=r.defaultTdClass),r.tdClass&&(o=`${o} ${r.tdClass}`),o}function M(r){let o="w-full";return r.placeholderClass&&(o=`${o} ${r.placeholderClass}`),o}function z(){return d.value=null,e.data}async function E(){const r=d.value&&d.value.currentPage||1;b.value=!0;const o=await e.data({sort:g,page:r});return b.value=!1,d.value=o.pagination,o.data}function R(r){g.fieldName!==r.key?(g.fieldName=r.key,g.order="asc"):g.order=g.order==="asc"?"desc":"asc",p.value||w()}async function w(){const r=p.value?z():await E();n.value=r.map(o=>new te(o,l))}async function j(r){d.value.currentPage=r,await w()}async function Y(){await w()}function H(r,o){return U.exports.get(r,o)}return p.value&&J(()=>e.data,()=>{w()}),K(async()=>{await w()}),t({refresh:Y}),(r,o)=>{const q=T("base-content-placeholders-text"),W=T("base-content-placeholders"),G=T("BaseIcon");return i(),s("div",we,[u("div",Se,[u("div",Te,[u("div",Ne,[L(r.$slots,"header"),u("table",{class:c(a.tableClass)},[u("thead",{class:c(a.theadClass)},[u("tr",null,[(i(!0),s(C,null,P(y(l),f=>(i(),s("th",{key:f.key,class:c([D(f),{"text-bold text-black":y(g).fieldName===f.key}]),onClick:v=>R(f)},[_(h(f.label)+" ",1),y(g).fieldName===f.key&&y(g).order==="asc"?(i(),s("span",Fe," \u2191 ")):m("",!0),y(g).fieldName===f.key&&y(g).order==="desc"?(i(),s("span",Ae," \u2193 ")):m("",!0)],10,Be))),128))])],2),a.loadingType==="placeholder"&&(a.loading||y(b))?(i(),s("tbody",Le,[(i(!0),s(C,null,P(a.placeholderCount,f=>(i(),s("tr",{key:f,class:c(f%2==0?"bg-white":"bg-gray-50")},[(i(!0),s(C,null,P(a.columns,v=>(i(),s("td",{key:v.key,class:c(["",B(v)])},[k(W,{class:c(M(v)),rounded:!0},{default:Q(()=>[k(q,{class:"w-full h-6",lines:1})]),_:2},1032,["class"])],2))),128))],2))),128))])):(i(),s("tbody",Ve,[(i(!0),s(C,null,P(y(x),(f,v)=>(i(),s("tr",{key:v,class:c(v%2==0?"bg-white":"bg-gray-50")},[(i(!0),s(C,null,P(a.columns,S=>(i(),s("td",{key:S.key,class:c(["",B(S)])},[L(r.$slots,"cell-"+S.key,{row:f},()=>[_(h(H(f.data,S.key)),1)])],2))),128))],2))),128))]))],2),a.loadingType==="spinner"&&(a.loading||y(b))?(i(),s("div",Ie,[k($,{class:"w-10 h-10 text-primary-500"})])):!a.loading&&!y(b)&&y(x)&&y(x).length===0?(i(),s("div",De,[k(G,{name:"ExclamationCircleIcon",class:"w-6 h-6 text-gray-400"}),u("span",Me,h(a.noResultsMessage),1)])):m("",!0),y(d)?(i(),X(_e,{key:2,pagination:y(d),onPageChange:j},null,8,["pagination"])):m("",!0)])])])])}}};export{Re as default}; diff --git a/crater/public/build/assets/CapsuleIcon.37dfa933.js b/crater/public/build/assets/CapsuleIcon.37dfa933.js new file mode 100644 index 0000000..a06fd2e --- /dev/null +++ b/crater/public/build/assets/CapsuleIcon.37dfa933.js @@ -0,0 +1 @@ +import{o as d,e as i,h as l,m as C}from"./vendor.d12b5734.js";const o={width:"118",height:"110",viewBox:"0 0 118 110",fill:"none",xmlns:"http://www.w3.org/2000/svg"},r={"clip-path":"url(#clip0)"},n=l("defs",null,[l("clipPath",{id:"clip0"},[l("rect",{width:"117.333",height:"110",fill:"white"})])],-1),s={props:{primaryFillColor:{type:String,default:"fill-primary-500"},secondaryFillColor:{type:String,default:"fill-gray-600"}},setup(e){return(a,c)=>(d(),i("svg",o,[l("g",r,[l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M58.6672 32.9999C42.1415 32.9999 32.973 28.5119 32.5898 28.3194L33.4093 26.6804C33.4992 26.7244 42.6127 31.1666 58.6672 31.1666C74.542 31.1666 83.8388 26.7208 83.9323 26.6768L84.7354 28.3231C84.3449 28.5156 74.9618 32.9999 58.6672 32.9999Z",class:C(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M25.2438 39.0117L28.4191 40.8451C28.839 41.0871 29.1415 41.4831 29.2698 41.9597C29.3963 42.4346 29.3321 42.9296 29.0901 43.3494L14.4235 68.7521C14.099 69.3167 13.4866 69.6669 12.8248 69.6669C12.504 69.6669 12.1978 69.5844 11.9191 69.4231L8.74382 67.5897L7.82715 69.1774L11.0025 71.0107C11.5763 71.3426 12.2051 71.5002 12.8248 71.5002C14.0953 71.5002 15.3346 70.8421 16.0111 69.6687L30.6778 44.2661C31.6861 42.5189 31.083 40.2657 29.3358 39.2574L26.1605 37.4241L25.2438 39.0117Z",class:C(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M91.1729 37.4241L87.9976 39.2574C86.2504 40.2657 85.6472 42.5189 86.6556 44.2661L101.322 69.6687C101.999 70.8421 103.238 71.5002 104.509 71.5002C105.128 71.5002 105.757 71.3426 106.331 71.0107L109.506 69.1774L108.59 67.5897L105.414 69.4231C105.139 69.5826 104.826 69.6669 104.509 69.6669C103.847 69.6669 103.234 69.3167 102.91 68.7521L88.2432 43.3494C88.0012 42.9296 87.9371 42.4346 88.0636 41.9597C88.1919 41.4831 88.4944 41.0871 88.9142 40.8451L92.0896 39.0117L91.1729 37.4241Z",class:C(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M115.5 84.3333V87.6993C115.5 89.2797 114.424 90.6308 112.88 90.9883C112.013 91.19 111.049 91.4393 109.96 91.7198C102.573 93.6228 88.8268 97.1667 58.6667 97.1667C28.292 97.1667 14.6942 93.6338 7.38833 91.7345C6.29383 91.4503 5.324 91.1992 4.44767 90.9938C2.90767 90.6363 1.83333 89.2833 1.83333 87.7067V84.3333L0 82.5V87.7067C0 90.134 1.66833 92.2295 4.0315 92.7795C10.9322 94.3873 23.6812 99 58.6667 99C93.3478 99 106.372 94.3818 113.296 92.7758C115.661 92.2258 117.333 90.1285 117.333 87.6993V82.5",class:C(e.primaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M79.6139 20.1666L115.245 81.7354C115.841 82.7566 115.344 84.0656 114.214 84.4102C107.345 86.4966 89.3159 89.8333 58.6662 89.8333C27.9744 89.8333 9.97652 86.3371 3.12535 84.2526C1.99602 83.9079 1.49919 82.5989 2.09502 81.5778L37.7204 20.1666L36.6662 18.3333L0.503686 80.6666C-0.686148 82.7071 0.322186 85.3251 2.58085 86.0163C9.60985 88.1704 27.7104 91.6666 58.6662 91.6666C89.4625 91.6666 107.664 88.3189 114.742 86.1666C117.008 85.4772 118.022 82.8574 116.829 80.8133L80.6662 18.3333",class:C(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M110.814 92.4116L115.245 100.069C115.841 101.089 115.344 102.4 114.214 102.742C107.345 104.831 89.3159 108.167 58.6662 108.167C27.9744 108.167 9.97469 104.671 3.12535 102.585C1.99602 102.242 1.49919 100.931 2.09502 99.9117L6.41985 92.4556L4.75885 91.6672L0.503686 99.0006C-0.686148 101.041 0.322185 103.657 2.58085 104.35C9.60985 106.504 27.7104 110.001 58.6662 110.001C89.4625 110.001 107.664 106.653 114.742 104.501C117.007 103.811 118.022 101.191 116.829 99.1472L112.682 91.9789L110.814 92.4116Z",class:C(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M58.667 0C47.238 0 36.667 7.1335 36.667 18.3407V20.1667C36.667 20.1667 42.6052 23.8333 58.667 23.8333C74.6665 23.8333 80.667 20.1667 80.667 20.1667V18.3333C80.667 7.24167 70.767 0 58.667 0ZM58.667 1.83333C70.3527 1.83333 78.8337 8.7725 78.8337 18.3333V19.0172C76.6887 19.9302 70.5103 22 58.667 22C46.7705 22 40.6197 19.9283 38.5003 19.0227V18.3407C38.5003 12.3658 41.7692 8.55617 44.51 6.41117C48.2317 3.50167 53.3907 1.83333 58.667 1.83333Z",class:C(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M69.6667 53.1666C70.6768 53.1666 71.5 53.9898 71.5 54.9999V89.8333H73.3333V54.9999C73.3333 52.9741 71.6925 51.3333 69.6667 51.3333H47.6667C45.6408 51.3333 44 52.9741 44 54.9999V89.8333H45.8333V54.9999C45.8333 53.9898 46.6565 53.1666 47.6667 53.1666H69.6667Z",class:C(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M58.6667 56.8333C53.6048 56.8333 49.5 60.9381 49.5 65.9999C49.5 71.0618 53.6048 75.1666 58.6667 75.1666C63.7285 75.1666 67.8333 71.0618 67.8333 65.9999C67.8333 60.9381 63.7285 56.8333 58.6667 56.8333ZM58.6667 58.6666C62.711 58.6666 66 61.9556 66 65.9999C66 70.0443 62.711 73.3333 58.6667 73.3333C54.6223 73.3333 51.3333 70.0443 51.3333 65.9999C51.3333 61.9556 54.6223 58.6666 58.6667 58.6666Z",class:C(e.secondaryFillColor)},null,2),l("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M63.2503 66C62.7443 66 62.3337 65.5893 62.3337 65.0833C62.3337 63.5672 61.0998 62.3333 59.5837 62.3333C59.0777 62.3333 58.667 61.9227 58.667 61.4167C58.667 60.9107 59.0777 60.5 59.5837 60.5C62.11 60.5 64.167 62.5552 64.167 65.0833C64.167 65.5893 63.7563 66 63.2503 66Z",class:C(e.primaryFillColor)},null,2)]),n]))}};export{s as _}; diff --git a/crater/public/build/assets/CategoryModal.6fabb0b3.js b/crater/public/build/assets/CategoryModal.6fabb0b3.js new file mode 100644 index 0000000..58facbd --- /dev/null +++ b/crater/public/build/assets/CategoryModal.6fabb0b3.js @@ -0,0 +1 @@ +import{J as j,B as k,k as g,L as y,M as N,N as L,S as T,T as q,r as i,o as B,l as b,w as r,h as m,i as f,t as C,u as e,f as n,m as D,j as G,U}from"./vendor.d12b5734.js";import{u as z}from"./category.c88b90cd.js";import{c as E}from"./main.465728e1.js";const A={class:"flex justify-between w-full"},J=["onSubmit"],X={class:"p-8 sm:p-6"},F={class:"z-0 flex justify-end p-4 border-t border-gray-200 border-solid border-modal-bg"},Q={setup(H){const t=z(),u=E(),{t:p}=j();let c=k(!1);const h=g(()=>({currentCategory:{name:{required:y.withMessage(p("validation.required"),N),minLength:y.withMessage(p("validation.name_min_length",{count:3}),L(3))},description:{maxLength:y.withMessage(p("validation.description_maxlength",{count:255}),T(255))}}})),o=q(h,g(()=>t)),w=g(()=>u.active&&u.componentName==="CategoryModal");async function I(){if(o.value.currentCategory.$touch(),o.value.currentCategory.$invalid)return!0;const s=t.isEdit?t.updateCategory:t.addCategory;c.value=!0,await s(t.currentCategory),c.value=!1,u.refreshData&&u.refreshData(),d()}function d(){u.closeModal(),setTimeout(()=>{t.$reset(),o.value.$reset()},300)}return(s,a)=>{const v=i("BaseIcon"),x=i("BaseInput"),_=i("BaseInputGroup"),M=i("BaseTextarea"),V=i("BaseInputGrid"),$=i("BaseButton"),S=i("BaseModal");return B(),b(S,{show:e(w),onClose:d},{header:r(()=>[m("div",A,[f(C(e(u).title)+" ",1),n(v,{name:"XIcon",class:"w-6 h-6 text-gray-500 cursor-pointer",onClick:d})])]),default:r(()=>[m("form",{action:"",onSubmit:U(I,["prevent"])},[m("div",X,[n(V,{layout:"one-column"},{default:r(()=>[n(_,{label:s.$t("expenses.category"),error:e(o).currentCategory.name.$error&&e(o).currentCategory.name.$errors[0].$message,required:""},{default:r(()=>[n(x,{modelValue:e(t).currentCategory.name,"onUpdate:modelValue":a[0]||(a[0]=l=>e(t).currentCategory.name=l),invalid:e(o).currentCategory.name.$error,type:"text",onInput:a[1]||(a[1]=l=>e(o).currentCategory.name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),n(_,{label:s.$t("expenses.description"),error:e(o).currentCategory.description.$error&&e(o).currentCategory.description.$errors[0].$message},{default:r(()=>[n(M,{modelValue:e(t).currentCategory.description,"onUpdate:modelValue":a[2]||(a[2]=l=>e(t).currentCategory.description=l),rows:"4",cols:"50",onInput:a[3]||(a[3]=l=>e(o).currentCategory.description.$touch())},null,8,["modelValue"])]),_:1},8,["label","error"])]),_:1})]),m("div",F,[n($,{type:"button",variant:"primary-outline",class:"mr-3 text-sm",onClick:d},{default:r(()=>[f(C(s.$t("general.cancel")),1)]),_:1}),n($,{loading:e(c),disabled:e(c),variant:"primary",type:"submit"},{left:r(l=>[e(c)?G("",!0):(B(),b(v,{key:0,name:"SaveIcon",class:D(l.class)},null,8,["class"]))]),default:r(()=>[f(" "+C(e(t).isEdit?s.$t("general.update"):s.$t("general.save")),1)]),_:1},8,["loading","disabled"])])],40,J)]),_:1},8,["show"])}}};export{Q as _}; diff --git a/crater/public/build/assets/CompanyInfoSettings.23b88ef4.js b/crater/public/build/assets/CompanyInfoSettings.23b88ef4.js new file mode 100644 index 0000000..fdce4e8 --- /dev/null +++ b/crater/public/build/assets/CompanyInfoSettings.23b88ef4.js @@ -0,0 +1 @@ +var oe=Object.defineProperty;var T=Object.getOwnPropertySymbols;var se=Object.prototype.hasOwnProperty,ne=Object.prototype.propertyIsEnumerable;var R=(f,s,d)=>s in f?oe(f,s,{enumerable:!0,configurable:!0,writable:!0,value:d}):f[s]=d,A=(f,s)=>{for(var d in s||(s={}))se.call(s,d)&&R(f,d,s[d]);if(T)for(var d of T(s))ne.call(s,d)&&R(f,d,s[d]);return f};import{aN as le,J,B as C,a0 as E,k as F,L as h,M as k,P as de,T as O,r as u,o as I,l as q,w as r,h as m,t as b,u as e,f as o,i as j,m as P,j as z,U as H,ah as re,N as ie,e as K,x as ue,F as me}from"./vendor.d12b5734.js";import{b as Q,c as W,d as X}from"./main.465728e1.js";const ce={class:"flex justify-between w-full"},pe={class:"px-6 pt-6"},_e={class:"font-medium text-lg text-left"},fe={class:"mt-2 text-sm leading-snug text-gray-500",style:{"max-width":"680px"}},ye=["onSubmit"],ge={class:"p-4 sm:p-6 space-y-4"},ve={class:"z-0 flex justify-end p-4 bg-gray-50 border-modal-bg"},be={setup(f){const s=Q(),d=W(),S=X(),B=le(),{t:M}=J();let c=C(!1);const a=E({id:s.selectedCompany.id,name:null}),$=F(()=>d.active&&d.componentName==="DeleteCompanyModal"),g={formData:{name:{required:h.withMessage(M("validation.required"),k),sameAsName:h.withMessage(M("validation.company_name_not_same"),de(s.selectedCompany.name))}}},_=O(g,{formData:a},{$scope:!1});async function V(){if(_.value.$touch(),_.value.$invalid)return!0;const v=s.companies[0];c.value=!0;try{const y=await s.deleteCompany(a);console.log(y.data.success),y.data.success&&(p(),await s.setSelectedCompany(v),B.push("/admin/dashboard"),await S.setIsAppLoaded(!1),await S.bootstrap()),c.value=!1}catch{c.value=!1}}function N(){a.id=null,a.name="",_.value.$reset()}function p(){d.closeModal(),setTimeout(()=>{N(),_.value.$reset()},300)}return(v,y)=>{const U=u("BaseInput"),x=u("BaseInputGroup"),l=u("BaseButton"),t=u("BaseIcon"),D=u("BaseModal");return I(),q(D,{show:e($),onClose:p},{default:r(()=>[m("div",ce,[m("div",pe,[m("h6",_e,b(e(d).title),1),m("p",fe,b(v.$t("settings.company_info.delete_company_modal_desc",{company:e(s).selectedCompany.name})),1)])]),m("form",{action:"",onSubmit:H(V,["prevent"])},[m("div",ge,[o(x,{label:v.$t("settings.company_info.delete_company_modal_label",{company:e(s).selectedCompany.name}),error:e(_).formData.name.$error&&e(_).formData.name.$errors[0].$message,required:""},{default:r(()=>[o(U,{modelValue:e(a).name,"onUpdate:modelValue":y[0]||(y[0]=i=>e(a).name=i),invalid:e(_).formData.name.$error,onInput:y[1]||(y[1]=i=>e(_).formData.name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"])]),m("div",ve,[o(l,{class:"mr-3 text-sm",variant:"primary-outline",outline:"",type:"button",onClick:p},{default:r(()=>[j(b(v.$t("general.cancel")),1)]),_:1}),o(l,{loading:e(c),disabled:e(c),variant:"danger",type:"submit"},{left:r(i=>[e(c)?z("",!0):(I(),q(t,{key:0,name:"TrashIcon",class:P(i.class)},null,8,["class"]))]),default:r(()=>[j(" "+b(v.$t("general.delete")),1)]),_:1},8,["loading","disabled"])])],40,ye)]),_:1},8,["show"])}}},$e=["onSubmit"],Be={key:0,class:"py-5"},Ve={class:"text-lg leading-6 font-medium text-gray-900"},Ce={class:"mt-2 max-w-xl text-sm text-gray-500"},we={class:"mt-5"},Me={setup(f){const s=Q(),d=X(),S=W(),{t:B}=J(),M=re("utils");let c=C(!1);const a=E({name:null,logo:null,address:{address_street_1:"",address_street_2:"",website:"",country_id:null,state:"",city:"",phone:"",zip:""}});M.mergeSettings(a,A({},s.selectedCompany));let $=C([]),g=C(null),_=C(null);const V=C(!1);a.logo&&$.value.push({image:a.logo});const N=F(()=>({name:{required:h.withMessage(B("validation.required"),k),minLength:h.withMessage(B("validation.name_min_length"),ie(3))},address:{country_id:{required:h.withMessage(B("validation.required"),k)}}})),p=O(N,F(()=>a));d.fetchCountries();function v(l,t,D,i){_.value=i.name,g.value=t}function y(){g.value=null,V.value=!0}async function U(){if(p.value.$touch(),p.value.$invalid)return!0;if(c.value=!0,(await s.updateCompany(a)).data.data){if(g.value||V.value){let t=new FormData;g.value&&t.append("company_logo",JSON.stringify({name:_.value,data:g.value})),t.append("is_company_logo_removed",V.value),await s.updateCompanyLogo(t),g.value=null,V.value=!1}c.value=!1}c.value=!1}function x(l){S.openModal({title:B("settings.company_info.are_you_absolutely_sure"),componentName:"DeleteCompanyModal",size:"sm"})}return(l,t)=>{const D=u("BaseFileUploader"),i=u("BaseInputGroup"),G=u("BaseInputGrid"),w=u("BaseInput"),Y=u("BaseMultiselect"),L=u("BaseTextarea"),Z=u("BaseIcon"),ee=u("BaseButton"),ae=u("BaseDivider"),te=u("BaseSettingCard");return I(),K(me,null,[m("form",{onSubmit:H(U,["prevent"])},[o(te,{title:l.$t("settings.company_info.company_info"),description:l.$t("settings.company_info.section_description")},{default:r(()=>[o(G,{class:"mt-5"},{default:r(()=>[o(i,{label:l.$tc("settings.company_info.company_logo")},{default:r(()=>[o(D,{modelValue:e($),"onUpdate:modelValue":t[0]||(t[0]=n=>ue($)?$.value=n:$=n),base64:"",onChange:v,onRemove:y},null,8,["modelValue"])]),_:1},8,["label"])]),_:1}),o(G,{class:"mt-5"},{default:r(()=>[o(i,{label:l.$tc("settings.company_info.company_name"),error:e(p).name.$error&&e(p).name.$errors[0].$message,required:""},{default:r(()=>[o(w,{modelValue:e(a).name,"onUpdate:modelValue":t[1]||(t[1]=n=>e(a).name=n),invalid:e(p).name.$error,onBlur:t[2]||(t[2]=n=>e(p).name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),o(i,{label:l.$tc("settings.company_info.phone")},{default:r(()=>[o(w,{modelValue:e(a).address.phone,"onUpdate:modelValue":t[3]||(t[3]=n=>e(a).address.phone=n)},null,8,["modelValue"])]),_:1},8,["label"]),o(i,{label:l.$tc("settings.company_info.country"),error:e(p).address.country_id.$error&&e(p).address.country_id.$errors[0].$message,required:""},{default:r(()=>[o(Y,{modelValue:e(a).address.country_id,"onUpdate:modelValue":t[4]||(t[4]=n=>e(a).address.country_id=n),label:"name",invalid:e(p).address.country_id.$error,options:e(d).countries,"value-prop":"id","can-deselect":!0,"can-clear":!1,searchable:"","track-by":"name"},null,8,["modelValue","invalid","options"])]),_:1},8,["label","error"]),o(i,{label:l.$tc("settings.company_info.state")},{default:r(()=>[o(w,{modelValue:e(a).address.state,"onUpdate:modelValue":t[5]||(t[5]=n=>e(a).address.state=n),name:"state",type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(i,{label:l.$tc("settings.company_info.city")},{default:r(()=>[o(w,{modelValue:e(a).address.city,"onUpdate:modelValue":t[6]||(t[6]=n=>e(a).address.city=n),type:"text"},null,8,["modelValue"])]),_:1},8,["label"]),o(i,{label:l.$tc("settings.company_info.zip")},{default:r(()=>[o(w,{modelValue:e(a).address.zip,"onUpdate:modelValue":t[7]||(t[7]=n=>e(a).address.zip=n)},null,8,["modelValue"])]),_:1},8,["label"]),m("div",null,[o(i,{label:l.$tc("settings.company_info.address")},{default:r(()=>[o(L,{modelValue:e(a).address.address_street_1,"onUpdate:modelValue":t[8]||(t[8]=n=>e(a).address.address_street_1=n),rows:"2"},null,8,["modelValue"])]),_:1},8,["label"]),o(L,{modelValue:e(a).address.address_street_2,"onUpdate:modelValue":t[9]||(t[9]=n=>e(a).address.address_street_2=n),rows:"2",row:2,class:"mt-2"},null,8,["modelValue"])])]),_:1}),o(ee,{loading:e(c),disabled:e(c),type:"submit",class:"mt-6"},{left:r(n=>[e(c)?z("",!0):(I(),q(Z,{key:0,class:P(n.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[j(" "+b(l.$tc("settings.company_info.save")),1)]),_:1},8,["loading","disabled"]),e(s).companies.length!==1?(I(),K("div",Be,[o(ae,{class:"my-4"}),m("h3",Ve,b(l.$tc("settings.company_info.delete_company")),1),m("div",Ce,[m("p",null,b(l.$tc("settings.company_info.delete_company_description")),1)]),m("div",we,[m("button",{type:"button",class:"inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm",onClick:x},b(l.$tc("general.delete")),1)])])):z("",!0)]),_:1},8,["title","description"])],40,$e),o(be)],64)}}};export{Me as default}; diff --git a/crater/public/build/assets/Create.1d6bd807.js b/crater/public/build/assets/Create.1d6bd807.js new file mode 100644 index 0000000..87584a4 --- /dev/null +++ b/crater/public/build/assets/Create.1d6bd807.js @@ -0,0 +1 @@ +var ce=Object.defineProperty;var R=Object.getOwnPropertySymbols;var de=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable;var L=(_,s,c)=>s in _?ce(_,s,{enumerable:!0,configurable:!0,writable:!0,value:c}):_[s]=c,T=(_,s)=>{for(var c in s||(s={}))de.call(s,c)&&L(_,c,s[c]);if(R)for(var c of R(s))ye.call(s,c)&&L(_,c,s[c]);return _};import{G as pe,aN as _e,ah as ve,J as fe,B as w,a0 as Pe,k as S,L as C,M as q,aX as ge,O as be,aP as Be,T as $e,a7 as he,b1 as Ce,r as m,o as k,e as Ie,f as r,w as l,h as I,u as e,l as j,m as z,j as U,i as N,t as b,x as Se,U as Ve,F as Me}from"./vendor.d12b5734.js";import{_ as we}from"./ExchangeRateConverter.d865db6a.js";import{u as qe,l as ke,m as Ne,b as je,c as Ue,i as xe,d as De}from"./main.465728e1.js";import{u as Ae}from"./payment.93619753.js";import{_ as Ee}from"./SelectNotePopup.2e678c03.js";import{_ as Fe}from"./CreateCustomFields.c1c460e4.js";import{_ as Ge}from"./PaymentModeModal.a0b58785.js";import"./exchange-rate.85b564e2.js";import"./NoteModal.ebe10cf0.js";const Re=["onSubmit"],Le={class:"absolute left-3.5"},Te={class:"relative w-full"},ze={class:"relative mt-6"},He={class:"z-20 float-right text-sm font-semibold leading-5 text-primary-400"},Je={class:"mb-4 text-sm font-medium text-gray-800"},nt={setup(_){const s=pe(),c=_e(),t=Ae();qe();const V=ke();Ne(),je();const H=Ue(),x=xe();De();const D=ve("utils"),{t:p}=fe();let B=w(!1),M=w(!1),v=w([]);const f=w(null),A="newEstimate",J=Pe(["customer","company","customerCustom","payment","paymentCustom"]),$=S({get:()=>t.currentPayment.amount/100,set:a=>{t.currentPayment.amount=Math.round(a*100)}}),u=S(()=>t.isFetchingInitialData),d=S(()=>s.name==="payments.edit"),E=S(()=>d.value?p("payments.edit_payment"):p("payments.new_payment")),O=S(()=>({currentPayment:{customer_id:{required:C.withMessage(p("validation.required"),q)},payment_date:{required:C.withMessage(p("validation.required"),q)},amount:{required:C.withMessage(p("validation.required"),q),between:C.withMessage(p("validation.payment_greater_than_due_amount"),ge(0,t.currentPayment.maxPayableAmount))},exchange_rate:{required:be(function(){return C.withMessage(p("validation.required"),q),t.showExchangeRate}),decimal:C.withMessage(p("validation.valid_exchange_rate"),Be)}}})),i=$e(O,t,{$scope:A});he(()=>{t.currentPayment.customer_id&&Y(t.currentPayment.customer_id),s.query.customer&&(t.currentPayment.customer_id=s.query.customer)}),t.resetCurrentPayment(),s.query.customer&&(t.currentPayment.customer_id=s.query.customer),t.fetchPaymentInitialData(d.value),s.params.id&&!d.value&&Q();async function X(){H.openModal({title:p("settings.payment_modes.add_payment_mode"),componentName:"PaymentModeModal"})}function K(a){t.currentPayment.notes=""+a.notes}async function Q(){var n;let a=await x.fetchInvoice((n=s==null?void 0:s.params)==null?void 0:n.id);t.currentPayment.customer_id=a.data.data.customer.id,t.currentPayment.invoice_id=a.data.data.id}async function W(a){a&&(f.value=v.value.find(n=>n.id===a),$.value=f.value.due_amount/100,t.currentPayment.maxPayableAmount=f.value.due_amount)}function Y(a){if(a){let n={customer_id:a,status:"DUE",limit:"all"};d.value&&(n.status=""),M.value=!0,Promise.all([x.fetchInvoices(n),V.fetchCustomer(a)]).then(async([y,P])=>{y&&(v.value=[...y.data.data]),P&&P.data&&(t.currentPayment.selectedCustomer=P.data.data,t.currentPayment.customer=P.data.data,t.currentPayment.currency=P.data.data.currency,d.value&&!V.editCustomer&&t.currentPayment.customer_id&&(V.editCustomer=P.data.data)),t.currentPayment.invoice_id&&(f.value=v.value.find(g=>g.id===t.currentPayment.invoice_id),t.currentPayment.maxPayableAmount=f.value.due_amount+t.currentPayment.amount,$.value===0&&($.value=f.value.due_amount/100)),d.value&&(v.value=v.value.filter(g=>g.due_amount>0||g.id==t.currentPayment.invoice_id)),M.value=!1}).catch(y=>{M.value=!1,console.error(y,"error")})}}Ce(()=>{t.resetCurrentPayment(),v.value=[],V.editCustomer=null});async function Z(){if(i.value.$touch(),i.value.$invalid)return!1;B.value=!0;let a=T({},t.currentPayment),n=null;try{n=await(d.value?t.updatePayment:t.addPayment)(a),c.push(`/admin/payments/${n.data.data.id}/view`)}catch{B.value=!1}}function ee(a){let n={userId:a};s.params.id&&(n.model_id=s.params.id),t.currentPayment.invoice_id=f.value=null,t.currentPayment.amount=0,v.value=[],t.getNextNumber(n,!0)}return(a,n)=>{const y=m("BaseBreadcrumbItem"),P=m("BaseBreadcrumb"),g=m("BaseIcon"),F=m("BaseButton"),te=m("BasePageHeader"),ae=m("BaseDatePicker"),h=m("BaseInputGroup"),ne=m("BaseInput"),oe=m("BaseCustomerSelectInput"),G=m("BaseMultiselect"),re=m("BaseMoney"),se=m("BaseSelectAction"),le=m("BaseInputGrid"),ue=m("BaseCustomInput"),me=m("BaseCard"),ie=m("BasePage");return k(),Ie(Me,null,[r(Ge),r(ie,{class:"relative payment-create"},{default:l(()=>[I("form",{action:"",onSubmit:Ve(Z,["prevent"])},[r(te,{title:e(E),class:"mb-5"},{actions:l(()=>[r(F,{loading:e(B),disabled:e(B),variant:"primary",type:"submit",class:"hidden sm:flex"},{left:l(o=>[e(B)?U("",!0):(k(),j(g,{key:0,name:"SaveIcon",class:z(o.class)},null,8,["class"]))]),default:l(()=>[N(" "+b(e(d)?a.$t("payments.update_payment"):a.$t("payments.save_payment")),1)]),_:1},8,["loading","disabled"])]),default:l(()=>[r(P,null,{default:l(()=>[r(y,{title:a.$t("general.home"),to:"/admin/dashboard"},null,8,["title"]),r(y,{title:a.$tc("payments.payment",2),to:"/admin/payments"},null,8,["title"]),r(y,{title:e(E),to:"#",active:""},null,8,["title"])]),_:1})]),_:1},8,["title"]),r(me,null,{default:l(()=>[r(le,null,{default:l(()=>[r(h,{label:a.$t("payments.date"),"content-loading":e(u),required:"",error:e(i).currentPayment.payment_date.$error&&e(i).currentPayment.payment_date.$errors[0].$message},{default:l(()=>[r(ae,{modelValue:e(t).currentPayment.payment_date,"onUpdate:modelValue":[n[0]||(n[0]=o=>e(t).currentPayment.payment_date=o),n[1]||(n[1]=o=>e(i).currentPayment.payment_date.$touch())],"content-loading":e(u),"calendar-button":!0,"calendar-button-icon":"calendar",invalid:e(i).currentPayment.payment_date.$error},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["label","content-loading","error"]),r(h,{label:a.$t("payments.payment_number"),"content-loading":e(u),required:""},{default:l(()=>[r(ne,{modelValue:e(t).currentPayment.payment_number,"onUpdate:modelValue":n[2]||(n[2]=o=>e(t).currentPayment.payment_number=o),"content-loading":e(u)},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(h,{label:a.$t("payments.customer"),error:e(i).currentPayment.customer_id.$error&&e(i).currentPayment.customer_id.$errors[0].$message,"content-loading":e(u),required:""},{default:l(()=>[e(u)?U("",!0):(k(),j(oe,{key:0,modelValue:e(t).currentPayment.customer_id,"onUpdate:modelValue":[n[3]||(n[3]=o=>e(t).currentPayment.customer_id=o),n[4]||(n[4]=o=>ee(e(t).currentPayment.customer_id))],"content-loading":e(u),invalid:e(i).currentPayment.customer_id.$error,placeholder:a.$t("customers.select_a_customer"),"show-action":""},null,8,["modelValue","content-loading","invalid","placeholder"]))]),_:1},8,["label","error","content-loading"]),r(h,{"content-loading":e(u),label:a.$t("payments.invoice"),"help-text":f.value?`Due Amount: ${e(t).currentPayment.maxPayableAmount/100}`:""},{default:l(()=>[r(G,{modelValue:e(t).currentPayment.invoice_id,"onUpdate:modelValue":n[5]||(n[5]=o=>e(t).currentPayment.invoice_id=o),"content-loading":e(u),"value-prop":"id","track-by":"invoice_number",label:"invoice_number",options:e(v),loading:e(M),placeholder:a.$t("invoices.select_invoice"),onSelect:W},{singlelabel:l(({value:o})=>[I("div",Le,b(o.invoice_number)+" ("+b(e(D).formatMoney(o.total,o.customer.currency))+") ",1)]),option:l(({option:o})=>[N(b(o.invoice_number)+" ("+b(e(D).formatMoney(o.total,o.customer.currency))+") ",1)]),_:1},8,["modelValue","content-loading","options","loading","placeholder"])]),_:1},8,["content-loading","label","help-text"]),r(h,{label:a.$t("payments.amount"),"content-loading":e(u),error:e(i).currentPayment.amount.$error&&e(i).currentPayment.amount.$errors[0].$message,required:""},{default:l(()=>[I("div",Te,[r(re,{key:e(t).currentPayment.currency,modelValue:e($),"onUpdate:modelValue":[n[6]||(n[6]=o=>Se($)?$.value=o:null),n[7]||(n[7]=o=>e(i).currentPayment.amount.$touch())],currency:e(t).currentPayment.currency,"content-loading":e(u),invalid:e(i).currentPayment.amount.$error},null,8,["modelValue","currency","content-loading","invalid"])])]),_:1},8,["label","content-loading","error"]),r(h,{"content-loading":e(u),label:a.$t("payments.payment_mode")},{default:l(()=>[r(G,{modelValue:e(t).currentPayment.payment_method_id,"onUpdate:modelValue":n[8]||(n[8]=o=>e(t).currentPayment.payment_method_id=o),"content-loading":e(u),label:"name","value-prop":"id","track-by":"name",options:e(t).paymentModes,placeholder:a.$t("payments.select_payment_mode"),searchable:""},{action:l(()=>[r(se,{onClick:X},{default:l(()=>[r(g,{name:"PlusIcon",class:"h-4 mr-2 -ml-2 text-center text-primary-400"}),N(" "+b(a.$t("settings.payment_modes.add_payment_mode")),1)]),_:1})]),_:1},8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["content-loading","label"]),r(we,{store:e(t),"store-prop":"currentPayment",v:e(i).currentPayment,"is-loading":e(u),"is-edit":e(d),"customer-currency":e(t).currentPayment.currency_id},null,8,["store","v","is-loading","is-edit","customer-currency"])]),_:1}),r(Fe,{type:"Payment","is-edit":e(d),"is-loading":e(u),store:e(t),"store-prop":"currentPayment","custom-field-scope":A,class:"mt-6"},null,8,["is-edit","is-loading","store"]),I("div",ze,[I("div",He,[r(Ee,{type:"Payment",onSelect:K})]),I("label",Je,b(a.$t("estimates.notes")),1),r(ue,{modelValue:e(t).currentPayment.notes,"onUpdate:modelValue":n[9]||(n[9]=o=>e(t).currentPayment.notes=o),"content-loading":e(u),fields:e(J),class:"mt-1"},null,8,["modelValue","content-loading","fields"])]),r(F,{loading:e(B),"content-loading":e(u),variant:"primary",type:"submit",class:"flex justify-center w-full mt-4 sm:hidden md:hidden"},{left:l(o=>[e(B)?U("",!0):(k(),j(g,{key:0,name:"SaveIcon",class:z(o.class)},null,8,["class"]))]),default:l(()=>[N(" "+b(e(d)?a.$t("payments.update_payment"):a.$t("payments.save_payment")),1)]),_:1},8,["loading","content-loading"])]),_:1})],40,Re)]),_:1})],64)}}};export{nt as default}; diff --git a/crater/public/build/assets/Create.68c99c93.js b/crater/public/build/assets/Create.68c99c93.js new file mode 100644 index 0000000..cf243fe --- /dev/null +++ b/crater/public/build/assets/Create.68c99c93.js @@ -0,0 +1 @@ +import{G as ie,aN as de,J as ue,B as q,k as b,L as m,M as $,b2 as ce,S as N,O as pe,aP as me,T as ge,b1 as xe,r as d,o as v,e as _e,f as r,w as o,h as F,u as e,l as h,m as U,i as w,t as S,j as C,x as ye,U as fe,F as ve}from"./vendor.d12b5734.js";import{u as Ee}from"./expense.ea1e799e.js";import{u as be}from"./category.c88b90cd.js";import{l as $e,b as he,m as Ce,c as Be,d as Ve}from"./main.465728e1.js";import{_ as we}from"./CreateCustomFields.c1c460e4.js";import{_ as Se}from"./CategoryModal.6fabb0b3.js";import{_ as Me}from"./ExchangeRateConverter.d865db6a.js";import"./exchange-rate.85b564e2.js";const Ie=["onSubmit"],ke={class:"hidden md:block"},qe={class:"block md:hidden"},Ae={setup(Fe){const _=$e(),j=he(),t=Ee(),y=be(),G=Ce(),T=Be(),f=ie(),A=de(),{t:u}=ue(),D=Ve();let g=q(!1),i=q(!1);const R="newExpense",M=q(!1),L=b(()=>({currentExpense:{expense_category_id:{required:m.withMessage(u("validation.required"),$)},expense_date:{required:m.withMessage(u("validation.required"),$)},amount:{required:m.withMessage(u("validation.required"),$),minValue:m.withMessage(u("validation.price_minvalue"),ce(.1)),maxLength:m.withMessage(u("validation.price_maxlength"),N(20))},notes:{maxLength:m.withMessage(u("validation.description_maxlength"),N(65e3))},currency_id:{required:m.withMessage(u("validation.required"),$)},exchange_rate:{required:pe(function(){return m.withMessage(u("validation.required"),$),t.showExchangeRate}),decimal:m.withMessage(u("validation.valid_exchange_rate"),me)}}})),l=ge(L,t,{$scope:R}),I=b({get:()=>t.currentExpense.amount/100,set:a=>{t.currentExpense.amount=Math.round(a*100)}}),c=b(()=>f.name==="expenses.edit"),P=b(()=>c.value?u("expenses.edit_expense"):u("expenses.new_expense")),O=b(()=>c.value?`/reports/expenses/${f.params.id}/download-receipt`:"");t.resetCurrentExpenseData(),G.resetCustomFields(),X();function z(a,n){t.currentExpense.attachment_receipt=n}function H(){t.currentExpense.attachment_receipt=null,M.value=!0}function J(){T.openModal({title:u("settings.expense_category.add_category"),componentName:"CategoryModal",size:"sm"})}function K(a){t.currentExpense.selectedCurrency=D.currencies.find(n=>n.id===a)}async function Q(a){let n=await y.fetchCategories({search:a});if(n.data.data.length>0&&y.editCategory&&!n.data.data.find(p=>p.id==y.editCategory.id)){let p=Object.assign({},y.editCategory);n.data.data.unshift(p)}return n.data.data}async function W(a){let n=await _.fetchCustomers({search:a});if(n.data.data.length>0&&_.editCustomer&&!n.data.data.find(p=>p.id==_.editCustomer.id)){let p=Object.assign({},_.editCustomer);n.data.data.unshift(p)}return n.data.data}async function X(){if(c.value||(t.currentExpense.currency_id=j.selectedCompanyCurrency.id,t.currentExpense.selectedCurrency=j.selectedCompanyCurrency),i.value=!0,await t.fetchPaymentModes({limit:"all"}),c.value){const a=await t.fetchExpense(f.params.id);t.currentExpense.currency_id=t.currentExpense.selectedCurrency.id,a.data&&(!y.editCategory&&a.data.data.expense_category&&(y.editCategory=a.data.data.expense_category),!_.editCustomer&&a.data.data.customer&&(_.editCustomer=a.data.data.customer))}else f.query.customer&&(t.currentExpense.customer_id=f.query.customer);i.value=!1}async function Y(){if(l.value.$touch(),l.value.$invalid)return;g.value=!0;let a=t.currentExpense;try{c.value?await t.updateExpense({id:f.params.id,data:a,isAttachmentReceiptRemoved:M.value}):await t.addExpense(a),g.value=!1,t.currentExpense.attachment_receipt=null,M.value=!1,A.push("/admin/expenses")}catch(n){console.error(n),g.value=!1;return}}return xe(()=>{t.resetCurrentExpenseData(),_.editCustomer=null,y.editCategory=null}),(a,n)=>{const E=d("BaseBreadcrumbItem"),p=d("BaseBreadcrumb"),B=d("BaseIcon"),k=d("BaseButton"),Z=d("BasePageHeader"),ee=d("BaseSelectAction"),V=d("BaseMultiselect"),x=d("BaseInputGroup"),te=d("BaseDatePicker"),ne=d("BaseMoney"),ae=d("BaseTextarea"),re=d("BaseFileUploader"),se=d("BaseInputGrid"),oe=d("BaseCard"),le=d("BasePage");return v(),_e(ve,null,[r(Se),r(le,{class:"relative"},{default:o(()=>[F("form",{action:"",onSubmit:fe(Y,["prevent"])},[r(Z,{title:e(P),class:"mb-5"},{actions:o(()=>[e(c)&&e(t).currentExpense.attachment_receipt_url?(v(),h(k,{key:0,href:e(O),tag:"a",variant:"primary-outline",type:"button",class:"mr-2"},{left:o(s=>[r(B,{name:"DownloadIcon",class:U(s.class)},null,8,["class"])]),default:o(()=>[w(" "+S(a.$t("expenses.download_receipt")),1)]),_:1},8,["href"])):C("",!0),F("div",ke,[r(k,{loading:e(g),"content-loading":e(i),disabled:e(g),variant:"primary",type:"submit"},{left:o(s=>[e(g)?C("",!0):(v(),h(B,{key:0,name:"SaveIcon",class:U(s.class)},null,8,["class"]))]),default:o(()=>[w(" "+S(e(c)?a.$t("expenses.update_expense"):a.$t("expenses.save_expense")),1)]),_:1},8,["loading","content-loading","disabled"])])]),default:o(()=>[r(p,null,{default:o(()=>[r(E,{title:a.$t("general.home"),to:"/admin/dashboard"},null,8,["title"]),r(E,{title:a.$tc("expenses.expense",2),to:"/admin/expenses"},null,8,["title"]),r(E,{title:e(P),to:"#",active:""},null,8,["title"])]),_:1})]),_:1},8,["title"]),r(oe,null,{default:o(()=>[r(se,null,{default:o(()=>[r(x,{label:a.$t("expenses.category"),error:e(l).currentExpense.expense_category_id.$error&&e(l).currentExpense.expense_category_id.$errors[0].$message,"content-loading":e(i),required:""},{default:o(()=>[e(i)?C("",!0):(v(),h(V,{key:0,modelValue:e(t).currentExpense.expense_category_id,"onUpdate:modelValue":n[0]||(n[0]=s=>e(t).currentExpense.expense_category_id=s),"content-loading":e(i),"value-prop":"id",label:"name","track-by":"id",options:Q,"filter-results":!1,"resolve-on-load":"",delay:500,searchable:"",invalid:e(l).currentExpense.expense_category_id.$error,placeholder:a.$t("expenses.categories.select_a_category"),onInput:n[1]||(n[1]=s=>e(l).currentExpense.expense_category_id.$touch())},{action:o(()=>[r(ee,{onClick:J},{default:o(()=>[r(B,{name:"PlusIcon",class:"h-4 mr-2 -ml-2 text-center text-primary-400"}),w(" "+S(a.$t("settings.expense_category.add_new_category")),1)]),_:1})]),_:1},8,["modelValue","content-loading","invalid","placeholder"]))]),_:1},8,["label","error","content-loading"]),r(x,{label:a.$t("expenses.expense_date"),error:e(l).currentExpense.expense_date.$error&&e(l).currentExpense.expense_date.$errors[0].$message,"content-loading":e(i),required:""},{default:o(()=>[r(te,{modelValue:e(t).currentExpense.expense_date,"onUpdate:modelValue":n[2]||(n[2]=s=>e(t).currentExpense.expense_date=s),"content-loading":e(i),"calendar-button":!0,invalid:e(l).currentExpense.expense_date.$error,onInput:n[3]||(n[3]=s=>e(l).currentExpense.expense_date.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["label","error","content-loading"]),r(x,{label:a.$t("expenses.amount"),error:e(l).currentExpense.amount.$error&&e(l).currentExpense.amount.$errors[0].$message,"content-loading":e(i),required:""},{default:o(()=>[r(ne,{key:e(t).currentExpense.selectedCurrency,modelValue:e(I),"onUpdate:modelValue":n[4]||(n[4]=s=>ye(I)?I.value=s:null),class:"focus:border focus:border-solid focus:border-primary-500",invalid:e(l).currentExpense.amount.$error,currency:e(t).currentExpense.selectedCurrency,onInput:n[5]||(n[5]=s=>e(l).currentExpense.amount.$touch())},null,8,["modelValue","invalid","currency"])]),_:1},8,["label","error","content-loading"]),r(x,{label:a.$t("expenses.currency"),"content-loading":e(i),error:e(l).currentExpense.currency_id.$error&&e(l).currentExpense.currency_id.$errors[0].$message,required:""},{default:o(()=>[r(V,{modelValue:e(t).currentExpense.currency_id,"onUpdate:modelValue":[n[6]||(n[6]=s=>e(t).currentExpense.currency_id=s),K],"value-prop":"id",label:"name","track-by":"name","content-loading":e(i),options:e(D).currencies,searchable:"","can-deselect":!1,placeholder:a.$t("customers.select_currency"),invalid:e(l).currentExpense.currency_id.$error,class:"w-full"},null,8,["modelValue","content-loading","options","placeholder","invalid"])]),_:1},8,["label","content-loading","error"]),r(Me,{store:e(t),"store-prop":"currentExpense",v:e(l).currentExpense,"is-loading":e(i),"is-edit":e(c),"customer-currency":e(t).currentExpense.currency_id},null,8,["store","v","is-loading","is-edit","customer-currency"]),r(x,{"content-loading":e(i),label:a.$t("expenses.customer")},{default:o(()=>[e(i)?C("",!0):(v(),h(V,{key:0,modelValue:e(t).currentExpense.customer_id,"onUpdate:modelValue":n[7]||(n[7]=s=>e(t).currentExpense.customer_id=s),"content-loading":e(i),"value-prop":"id",label:"name","track-by":"id",options:W,"filter-results":!1,"resolve-on-load":"",delay:500,searchable:"",placeholder:a.$t("customers.select_a_customer")},null,8,["modelValue","content-loading","placeholder"]))]),_:1},8,["content-loading","label"]),r(x,{"content-loading":e(i),label:a.$t("payments.payment_mode")},{default:o(()=>[r(V,{modelValue:e(t).currentExpense.payment_method_id,"onUpdate:modelValue":n[8]||(n[8]=s=>e(t).currentExpense.payment_method_id=s),"content-loading":e(i),label:"name","value-prop":"id","track-by":"name",options:e(t).paymentModes,placeholder:a.$t("payments.select_payment_mode"),searchable:""},null,8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["content-loading","label"]),r(x,{"content-loading":e(i),label:a.$t("expenses.note"),error:e(l).currentExpense.notes.$error&&e(l).currentExpense.notes.$errors[0].$message},{default:o(()=>[r(ae,{modelValue:e(t).currentExpense.notes,"onUpdate:modelValue":n[9]||(n[9]=s=>e(t).currentExpense.notes=s),"content-loading":e(i),row:4,rows:"4",onInput:n[10]||(n[10]=s=>e(l).currentExpense.notes.$touch())},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label","error"]),r(x,{label:a.$t("expenses.receipt")},{default:o(()=>[r(re,{modelValue:e(t).currentExpense.receiptFiles,"onUpdate:modelValue":n[11]||(n[11]=s=>e(t).currentExpense.receiptFiles=s),accept:"image/*,.doc,.docx,.pdf,.csv,.xlsx,.xls",onChange:z,onRemove:H},null,8,["modelValue"])]),_:1},8,["label"]),r(we,{"is-edit":e(c),class:"col-span-2","is-loading":e(i),type:"Expense",store:e(t),"store-prop":"currentExpense","custom-field-scope":R},null,8,["is-edit","is-loading","store"]),F("div",qe,[r(k,{loading:e(g),tabindex:6,variant:"primary",type:"submit",class:"flex justify-center w-full"},{left:o(s=>[e(g)?C("",!0):(v(),h(B,{key:0,name:"SaveIcon",class:U(s.class)},null,8,["class"]))]),default:o(()=>[w(" "+S(e(c)?a.$t("expenses.update_expense"):a.$t("expenses.save_expense")),1)]),_:1},8,["loading"])])]),_:1})]),_:1})],40,Ie)]),_:1})],64)}}};export{Ae as default}; diff --git a/crater/public/build/assets/Create.c666337c.js b/crater/public/build/assets/Create.c666337c.js new file mode 100644 index 0000000..fdc128b --- /dev/null +++ b/crater/public/build/assets/Create.c666337c.js @@ -0,0 +1 @@ +var W=Object.defineProperty,X=Object.defineProperties;var Y=Object.getOwnPropertyDescriptors;var S=Object.getOwnPropertySymbols;var Z=Object.prototype.hasOwnProperty,x=Object.prototype.propertyIsEnumerable;var k=(m,a,o)=>a in m?W(m,a,{enumerable:!0,configurable:!0,writable:!0,value:o}):m[a]=o,j=(m,a)=>{for(var o in a||(a={}))Z.call(a,o)&&k(m,o,a[o]);if(S)for(var o of S(a))x.call(a,o)&&k(m,o,a[o]);return m},N=(m,a)=>X(m,Y(a));import{J as ee,G as ae,aN as te,B as b,k as V,L as p,M as $,N as G,Q as oe,O as se,T as ne,r as d,o as w,l as h,w as u,f as s,u as e,h as y,e as re,y as le,F as ie,m as ue,j as de,i as me,t as ce,U as pe}from"./vendor.d12b5734.js";import{b as ge}from"./main.465728e1.js";import{V as fe}from"./index.esm.85b4999a.js";import{u as ve}from"./users.27a53e97.js";const $e=["onSubmit"],De={class:"grid grid-cols-12"},Be={class:"space-y-6"},ye={setup(m){const a=ve(),{t:o}=ee(),q=ae(),L=te(),P=ge();let g=b(!1),l=b(!1);b([]);let I=b([]);const f=V(()=>q.name==="users.edit"),M=V(()=>f.value?o("users.edit_user"):o("users.new_user")),E=V(()=>({userData:{name:{required:p.withMessage(o("validation.required"),$),minLength:p.withMessage(o("validation.name_min_length",{count:3}),G(3))},email:{required:p.withMessage(o("validation.required"),$),email:p.withMessage(o("validation.email_incorrect"),oe)},password:{required:se(function(){return p.withMessage(o("validation.required"),$),!f.value}),minLength:p.withMessage(o("validation.password_min_length",{count:8}),G(8))},companies:{required:p.withMessage(o("validation.required"),$)}}})),F={role:{required:p.withMessage(o("validation.required"),$)}},n=ne(E,a,{$scope:!0});R(),a.resetUserData();async function R(){var i;l.value=!0;try{f.value&&await a.fetchUser(q.params.id);let t=await P.fetchUserCompanies();((i=t==null?void 0:t.data)==null?void 0:i.data)&&(I.value=t.data.data.map(c=>(c.role=null,c)))}catch{l.value=!1}l.value=!1}async function T(){if(n.value.$touch(),n.value.$invalid)return!0;try{g.value=!0;let i=N(j({},a.userData),{companies:a.userData.companies.map(c=>({role:c.role,id:c.id}))});await(f.value?a.updateUser:a.addUser)(i),L.push("/admin/users"),g.value=!1}catch{g.value=!1}}return(i,t)=>{const c=d("BaseBreadcrumbItem"),H=d("BaseBreadcrumb"),z=d("BasePageHeader"),D=d("BaseInput"),v=d("BaseInputGroup"),U=d("BaseMultiselect"),A=d("BaseInputGrid"),J=d("BaseIcon"),O=d("BaseButton"),Q=d("BaseCard"),K=d("BasePage");return w(),h(K,null,{default:u(()=>[s(z,{title:e(M)},{default:u(()=>[s(H,null,{default:u(()=>[s(c,{title:i.$t("general.home"),to:"dashboard"},null,8,["title"]),s(c,{title:i.$tc("users.user",2),to:"/admin/users"},null,8,["title"]),s(c,{title:e(M),to:"#",active:""},null,8,["title"])]),_:1})]),_:1},8,["title"]),y("form",{action:"",autocomplete:"off",onSubmit:pe(T,["prevent"])},[y("div",De,[s(Q,{class:"mt-6 col-span-12 md:col-span-8"},{default:u(()=>[s(A,{layout:"one-column"},{default:u(()=>[s(v,{"content-loading":e(l),label:i.$t("users.name"),error:e(n).userData.name.$error&&e(n).userData.name.$errors[0].$message,required:""},{default:u(()=>[s(D,{modelValue:e(a).userData.name,"onUpdate:modelValue":t[0]||(t[0]=r=>e(a).userData.name=r),modelModifiers:{trim:!0},"content-loading":e(l),invalid:e(n).userData.name.$error,onInput:t[1]||(t[1]=r=>e(n).userData.name.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["content-loading","label","error"]),s(v,{"content-loading":e(l),label:i.$t("users.email"),error:e(n).userData.email.$error&&e(n).userData.email.$errors[0].$message,required:""},{default:u(()=>[s(D,{modelValue:e(a).userData.email,"onUpdate:modelValue":t[2]||(t[2]=r=>e(a).userData.email=r),modelModifiers:{trim:!0},type:"email","content-loading":e(l),invalid:e(n).userData.email.$error,onInput:t[3]||(t[3]=r=>e(n).userData.email.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["content-loading","label","error"]),s(v,{"content-loading":e(l),label:i.$t("users.companies"),error:e(n).userData.companies.$error&&e(n).userData.companies.$errors[0].$message,required:""},{default:u(()=>[s(U,{modelValue:e(a).userData.companies,"onUpdate:modelValue":t[4]||(t[4]=r=>e(a).userData.companies=r),mode:"tags",object:!0,autocomplete:"new-password",label:"name",options:e(I),"value-prop":"id",invalid:e(n).userData.companies.$error,"content-loading":e(l),searchable:"","can-deselect":!1,class:"w-full","track-by":"name"},null,8,["modelValue","options","invalid","content-loading"])]),_:1},8,["content-loading","label","error"]),(w(!0),re(ie,null,le(e(a).userData.companies,(r,B)=>(w(),h(e(fe),{key:B,state:r,rules:F},{default:u(({v:_})=>[y("div",Be,[s(v,{"content-loading":e(l),label:i.$t("users.select_company_role",{company:r.name}),error:_.role.$error&&_.role.$errors[0].$message,required:""},{default:u(()=>[s(U,{modelValue:e(a).userData.companies[B].role,"onUpdate:modelValue":C=>e(a).userData.companies[B].role=C,"value-prop":"name","track-by":"id",autocomplete:"off","content-loading":e(l),label:"name",options:e(a).userData.companies[B].roles,"can-deselect":!1,invalid:_.role.$invalid,onChange:C=>_.role.$touch()},null,8,["modelValue","onUpdate:modelValue","content-loading","options","invalid","onChange"])]),_:2},1032,["content-loading","label","error"])])]),_:2},1032,["state"]))),128)),s(v,{"content-loading":e(l),label:i.$tc("users.password"),error:e(n).userData.password.$error&&e(n).userData.password.$errors[0].$message,required:!e(f)},{default:u(()=>[s(D,{modelValue:e(a).userData.password,"onUpdate:modelValue":t[5]||(t[5]=r=>e(a).userData.password=r),name:"new-password",autocomplete:"new-password","content-loading":e(l),type:"password",invalid:e(n).userData.password.$error,onInput:t[6]||(t[6]=r=>e(n).userData.password.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["content-loading","label","error","required"]),s(v,{"content-loading":e(l),label:i.$t("users.phone")},{default:u(()=>[s(D,{modelValue:e(a).userData.phone,"onUpdate:modelValue":t[7]||(t[7]=r=>e(a).userData.phone=r),modelModifiers:{trim:!0},"content-loading":e(l)},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"])]),_:1}),s(O,{"content-loading":e(l),type:"submit",loading:e(g),disabled:e(g),class:"mt-6"},{left:u(r=>[e(g)?de("",!0):(w(),h(J,{key:0,name:"SaveIcon",class:ue(r.class)},null,8,["class"]))]),default:u(()=>[me(" "+ce(e(f)?i.$t("users.update_user"):i.$t("users.save_user")),1)]),_:1},8,["content-loading","loading","disabled"])]),_:1})])],40,$e)]),_:1})}}};export{ye as default}; diff --git a/crater/public/build/assets/Create.ddeb574a.js b/crater/public/build/assets/Create.ddeb574a.js new file mode 100644 index 0000000..e4bb45c --- /dev/null +++ b/crater/public/build/assets/Create.ddeb574a.js @@ -0,0 +1 @@ +var ae=Object.defineProperty;var G=Object.getOwnPropertySymbols;var ie=Object.prototype.hasOwnProperty,ue=Object.prototype.propertyIsEnumerable;var N=(y,o,b)=>o in y?ae(y,o,{enumerable:!0,configurable:!0,writable:!0,value:b}):y[o]=b,T=(y,o)=>{for(var b in o||(o={}))ie.call(o,b)&&N(y,b,o[b]);if(G)for(var b of G(o))ue.call(o,b)&&N(y,b,o[b]);return y};import{J as de,aN as me,G as ce,B,k as M,L as g,M as R,N as F,O as A,Q as pe,P as ge,R as be,S as q,T as Ce,r as p,o as _,l as $,w as i,h as m,f as r,m as O,i as H,t as v,u as e,j as V,x as L,e as J,U as fe}from"./vendor.d12b5734.js";import{l as _e,m as $e,d as ye,b as ve,n as Ve}from"./main.465728e1.js";import{_ as we}from"./CreateCustomFields.c1c460e4.js";const he=["onSubmit"],Be={class:"flex items-center justify-end"},Me={class:"grid grid-cols-5 gap-4 mb-8"},Ie={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},xe={class:"grid grid-cols-5 gap-4 mb-8"},Ue={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},ke={class:"md:col-span-2"},Se={class:"text-sm text-gray-500"},qe={class:"grid grid-cols-5 gap-4 mb-8"},Le={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},ze={class:"space-y-6"},Pe={class:"flex items-center justify-start mb-6 md:justify-end md:mb-0"},Fe={class:"p-1"},je={key:0,class:"grid grid-cols-5 gap-4 mb-8"},De={class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},Ee={class:"space-y-6"},Ge={class:"grid grid-cols-5 gap-2 mb-8"},Ne={key:0,class:"col-span-5 text-lg font-semibold text-left lg:col-span-1"},Te={class:"col-span-5 lg:col-span-4"},Je={setup(y){const o=_e(),b=$e(),z=ye(),Q=ve(),j="customFields",{t:c}=de(),K=me(),W=ce();let s=B(!1),C=B(!1),f=B(!1);B(!1);const I=B(!1),h=M(()=>W.name==="customers.edit");let X=M(()=>o.isFetchingInitialSettings);const D=M(()=>h.value?c("customers.edit_customer"):c("customers.new_customer")),Y=M(()=>({currentCustomer:{name:{required:g.withMessage(c("validation.required"),R),minLength:g.withMessage(c("validation.name_min_length",{count:3}),F(3))},prefix:{minLength:g.withMessage(c("validation.name_min_length",{count:3}),F(3))},currency_id:{required:g.withMessage(c("validation.required"),R)},email:{required:g.withMessage(c("validation.required"),A(o.currentCustomer.enable_portal==!0)),email:g.withMessage(c("validation.email_incorrect"),pe)},password:{required:g.withMessage(c("validation.required"),A(o.currentCustomer.enable_portal==!0&&!o.currentCustomer.password_added)),minLength:g.withMessage(c("validation.password_min_length",{count:8}),F(8))},confirm_password:{sameAsPassword:g.withMessage(c("validation.password_incorrect"),ge(o.currentCustomer.password))},website:{url:g.withMessage(c("validation.invalid_url"),be)},billing:{address_street_1:{maxLength:g.withMessage(c("validation.address_maxlength",{count:255}),q(255))},address_street_2:{maxLength:g.withMessage(c("validation.address_maxlength",{count:255}),q(255))}},shipping:{address_street_1:{maxLength:g.withMessage(c("validation.address_maxlength",{count:255}),q(255))},address_street_2:{maxLength:g.withMessage(c("validation.address_maxlength",{count:255}),q(255))}}}})),Z=M(()=>`${window.location.origin}/${Q.selectedCompany.slug}/customer/login`),a=Ce(Y,o,{$scope:j});o.resetCurrentCustomer(),o.fetchCustomerInitialSettings(h.value);async function ee(){if(a.value.$touch(),a.value.$invalid)return!0;I.value=!0;let l=T({},o.currentCustomer),t=null;try{t=await(h.value?o.updateCustomer:o.addCustomer)(l)}catch{I.value=!1;return}K.push(`/admin/customers/${t.data.data.id}/view`)}return(l,t)=>{const x=p("BaseBreadcrumbItem"),te=p("BaseBreadcrumb-item"),oe=p("BaseBreadcrumb"),w=p("BaseIcon"),E=p("BaseButton"),ne=p("BasePageHeader"),d=p("BaseInput"),u=p("BaseInputGroup"),P=p("BaseMultiselect"),U=p("BaseInputGrid"),k=p("BaseDivider"),re=p("BaseSwitch"),S=p("BaseTextarea"),se=p("BaseCard"),le=p("BasePage");return _(),$(le,null,{default:i(()=>[m("form",{onSubmit:fe(ee,["prevent"])},[r(ne,{title:e(D)},{actions:i(()=>[m("div",Be,[r(E,{type:"submit",loading:I.value,disabled:I.value},{left:i(n=>[r(w,{name:"SaveIcon",class:O(n.class)},null,8,["class"])]),default:i(()=>[H(" "+v(e(h)?l.$t("customers.update_customer"):l.$t("customers.save_customer")),1)]),_:1},8,["loading","disabled"])])]),default:i(()=>[r(oe,null,{default:i(()=>[r(x,{title:l.$t("general.home"),to:"dashboard"},null,8,["title"]),r(x,{title:l.$tc("customers.customer",2),to:"/admin/customers"},null,8,["title"]),r(te,{title:e(D),to:"#",active:""},null,8,["title"])]),_:1})]),_:1},8,["title"]),r(se,{class:"mt-5"},{default:i(()=>[m("div",Me,[m("h6",Ie,v(l.$t("customers.basic_info")),1),r(U,{class:"col-span-5 lg:col-span-4"},{default:i(()=>[r(u,{label:l.$t("customers.display_name"),required:"",error:e(a).currentCustomer.name.$error&&e(a).currentCustomer.name.$errors[0].$message,"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.name,"onUpdate:modelValue":t[0]||(t[0]=n=>e(o).currentCustomer.name=n),"content-loading":e(s),type:"text",name:"name",class:"",invalid:e(a).currentCustomer.name.$error,onInput:t[1]||(t[1]=n=>e(a).currentCustomer.name.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["label","error","content-loading"]),r(u,{label:l.$t("customers.primary_contact_name"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.contact_name,"onUpdate:modelValue":t[2]||(t[2]=n=>e(o).currentCustomer.contact_name=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(u,{error:e(a).currentCustomer.email.$error&&e(a).currentCustomer.email.$errors[0].$message,"content-loading":e(s),label:l.$t("customers.email")},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.email,"onUpdate:modelValue":t[3]||(t[3]=n=>e(o).currentCustomer.email=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"email",invalid:e(a).currentCustomer.email.$error,onInput:t[4]||(t[4]=n=>e(a).currentCustomer.email.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["error","content-loading","label"]),r(u,{label:l.$t("customers.phone"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.phone,"onUpdate:modelValue":t[5]||(t[5]=n=>e(o).currentCustomer.phone=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"phone"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(u,{label:l.$t("customers.primary_currency"),"content-loading":e(s),error:e(a).currentCustomer.currency_id.$error&&e(a).currentCustomer.currency_id.$errors[0].$message,required:""},{default:i(()=>[r(P,{modelValue:e(o).currentCustomer.currency_id,"onUpdate:modelValue":t[6]||(t[6]=n=>e(o).currentCustomer.currency_id=n),"value-prop":"id",label:"name","track-by":"name","content-loading":e(s),options:e(z).currencies,searchable:"","can-deselect":!1,placeholder:l.$t("customers.select_currency"),invalid:e(a).currentCustomer.currency_id.$error,class:"w-full"},null,8,["modelValue","content-loading","options","placeholder","invalid"])]),_:1},8,["label","content-loading","error"]),r(u,{error:e(a).currentCustomer.website.$error&&e(a).currentCustomer.website.$errors[0].$message,label:l.$t("customers.website"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.website,"onUpdate:modelValue":t[7]||(t[7]=n=>e(o).currentCustomer.website=n),"content-loading":e(s),type:"url",onInput:t[8]||(t[8]=n=>e(a).currentCustomer.website.$touch())},null,8,["modelValue","content-loading"])]),_:1},8,["error","label","content-loading"]),r(u,{label:l.$t("customers.prefix"),error:e(a).currentCustomer.prefix.$error&&e(a).currentCustomer.prefix.$errors[0].$message,"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.prefix,"onUpdate:modelValue":t[9]||(t[9]=n=>e(o).currentCustomer.prefix=n),"content-loading":e(s),type:"text",name:"name",class:"",invalid:e(a).currentCustomer.prefix.$error,onInput:t[10]||(t[10]=n=>e(a).currentCustomer.prefix.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["label","error","content-loading"])]),_:1})]),r(k,{class:"mb-5 md:mb-8"}),m("div",xe,[m("h6",Ue,v(l.$t("customers.portal_access")),1),r(U,{class:"col-span-5 lg:col-span-4"},{default:i(()=>[m("div",ke,[m("p",Se,v(l.$t("customers.portal_access_text")),1),r(re,{modelValue:e(o).currentCustomer.enable_portal,"onUpdate:modelValue":t[11]||(t[11]=n=>e(o).currentCustomer.enable_portal=n),class:"mt-1 flex"},null,8,["modelValue"])]),e(o).currentCustomer.enable_portal?(_(),$(u,{key:0,"content-loading":e(s),label:l.$t("customers.portal_access_url"),class:"md:col-span-2","help-text":l.$t("customers.portal_access_url_help")},{default:i(()=>[r(Ve,{token:e(Z)},null,8,["token"])]),_:1},8,["content-loading","label","help-text"])):V("",!0),e(o).currentCustomer.enable_portal?(_(),$(u,{key:1,"content-loading":e(s),error:e(a).currentCustomer.password.$error&&e(a).currentCustomer.password.$errors[0].$message,label:l.$t("customers.password")},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.password,"onUpdate:modelValue":t[14]||(t[14]=n=>e(o).currentCustomer.password=n),modelModifiers:{trim:!0},"content-loading":e(s),type:e(C)?"text":"password",name:"password",invalid:e(a).currentCustomer.password.$error,onInput:t[15]||(t[15]=n=>e(a).currentCustomer.password.$touch())},{right:i(()=>[e(C)?(_(),$(w,{key:0,name:"EyeOffIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:t[12]||(t[12]=n=>L(C)?C.value=!e(C):C=!e(C))})):(_(),$(w,{key:1,name:"EyeIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:t[13]||(t[13]=n=>L(C)?C.value=!e(C):C=!e(C))}))]),_:1},8,["modelValue","content-loading","type","invalid"])]),_:1},8,["content-loading","error","label"])):V("",!0),e(o).currentCustomer.enable_portal?(_(),$(u,{key:2,error:e(a).currentCustomer.confirm_password.$error&&e(a).currentCustomer.confirm_password.$errors[0].$message,"content-loading":e(s),label:"Confirm Password"},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.confirm_password,"onUpdate:modelValue":t[18]||(t[18]=n=>e(o).currentCustomer.confirm_password=n),modelModifiers:{trim:!0},"content-loading":e(s),type:e(f)?"text":"password",name:"confirm_password",invalid:e(a).currentCustomer.confirm_password.$error,onInput:t[19]||(t[19]=n=>e(a).currentCustomer.confirm_password.$touch())},{right:i(()=>[e(f)?(_(),$(w,{key:0,name:"EyeOffIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:t[16]||(t[16]=n=>L(f)?f.value=!e(f):f=!e(f))})):(_(),$(w,{key:1,name:"EyeIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:t[17]||(t[17]=n=>L(f)?f.value=!e(f):f=!e(f))}))]),_:1},8,["modelValue","content-loading","type","invalid"])]),_:1},8,["error","content-loading"])):V("",!0)]),_:1})]),r(k,{class:"mb-5 md:mb-8"}),m("div",qe,[m("h6",Le,v(l.$t("customers.billing_address")),1),e(o).currentCustomer.billing?(_(),$(U,{key:0,class:"col-span-5 lg:col-span-4"},{default:i(()=>[r(u,{label:l.$t("customers.name"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.billing.name,"onUpdate:modelValue":t[20]||(t[20]=n=>e(o).currentCustomer.billing.name=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",class:"w-full",name:"address_name"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(u,{label:l.$t("customers.country"),"content-loading":e(s)},{default:i(()=>[r(P,{modelValue:e(o).currentCustomer.billing.country_id,"onUpdate:modelValue":t[21]||(t[21]=n=>e(o).currentCustomer.billing.country_id=n),"value-prop":"id",label:"name","track-by":"name","resolve-on-load":"",searchable:"","content-loading":e(s),options:e(z).countries,placeholder:l.$t("general.select_country"),class:"w-full"},null,8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["label","content-loading"]),r(u,{label:l.$t("customers.state"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.billing.state,"onUpdate:modelValue":t[22]||(t[22]=n=>e(o).currentCustomer.billing.state=n),"content-loading":e(s),name:"billing.state",type:"text"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(u,{"content-loading":e(s),label:l.$t("customers.city")},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.billing.city,"onUpdate:modelValue":t[23]||(t[23]=n=>e(o).currentCustomer.billing.city=n),"content-loading":e(s),name:"billing.city",type:"text"},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"]),r(u,{label:l.$t("customers.address"),error:e(a).currentCustomer.billing.address_street_1.$error&&e(a).currentCustomer.billing.address_street_1.$errors[0].$message||e(a).currentCustomer.billing.address_street_2.$error&&e(a).currentCustomer.billing.address_street_2.$errors[0].$message,"content-loading":e(s)},{default:i(()=>[r(S,{modelValue:e(o).currentCustomer.billing.address_street_1,"onUpdate:modelValue":t[24]||(t[24]=n=>e(o).currentCustomer.billing.address_street_1=n),modelModifiers:{trim:!0},"content-loading":e(s),placeholder:l.$t("general.street_1"),type:"text",name:"billing_street1","container-class":"mt-3",onInput:t[25]||(t[25]=n=>e(a).currentCustomer.billing.address_street_1.$touch())},null,8,["modelValue","content-loading","placeholder"]),r(S,{modelValue:e(o).currentCustomer.billing.address_street_2,"onUpdate:modelValue":t[26]||(t[26]=n=>e(o).currentCustomer.billing.address_street_2=n),modelModifiers:{trim:!0},"content-loading":e(s),placeholder:l.$t("general.street_2"),type:"text",class:"mt-3",name:"billing_street2","container-class":"mt-3",onInput:t[27]||(t[27]=n=>e(a).currentCustomer.billing.address_street_2.$touch())},null,8,["modelValue","content-loading","placeholder"])]),_:1},8,["label","error","content-loading"]),m("div",ze,[r(u,{"content-loading":e(s),label:l.$t("customers.phone"),class:"text-left"},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.billing.phone,"onUpdate:modelValue":t[28]||(t[28]=n=>e(o).currentCustomer.billing.phone=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"phone"},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"]),r(u,{label:l.$t("customers.zip_code"),"content-loading":e(s),class:"mt-2 text-left"},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.billing.zip,"onUpdate:modelValue":t[29]||(t[29]=n=>e(o).currentCustomer.billing.zip=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"zip"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"])])]),_:1})):V("",!0)]),r(k,{class:"mb-5 md:mb-8"}),m("div",Pe,[m("div",Fe,[r(E,{type:"button","content-loading":e(s),size:"sm",variant:"primary-outline",onClick:t[30]||(t[30]=n=>e(o).copyAddress(!0))},{left:i(n=>[r(w,{name:"DocumentDuplicateIcon",class:O(n.class)},null,8,["class"])]),default:i(()=>[H(" "+v(l.$t("customers.copy_billing_address")),1)]),_:1},8,["content-loading"])])]),e(o).currentCustomer.shipping?(_(),J("div",je,[m("h6",De,v(l.$t("customers.shipping_address")),1),r(U,{class:"col-span-5 lg:col-span-4"},{default:i(()=>[r(u,{"content-loading":e(s),label:l.$t("customers.name")},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.shipping.name,"onUpdate:modelValue":t[31]||(t[31]=n=>e(o).currentCustomer.shipping.name=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"address_name"},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"]),r(u,{label:l.$t("customers.country"),"content-loading":e(s)},{default:i(()=>[r(P,{modelValue:e(o).currentCustomer.shipping.country_id,"onUpdate:modelValue":t[32]||(t[32]=n=>e(o).currentCustomer.shipping.country_id=n),"value-prop":"id",label:"name","track-by":"name","resolve-on-load":"",searchable:"","content-loading":e(s),options:e(z).countries,placeholder:l.$t("general.select_country"),class:"w-full"},null,8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["label","content-loading"]),r(u,{label:l.$t("customers.state"),"content-loading":e(s)},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.shipping.state,"onUpdate:modelValue":t[33]||(t[33]=n=>e(o).currentCustomer.shipping.state=n),"content-loading":e(s),name:"shipping.state",type:"text"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),r(u,{"content-loading":e(s),label:l.$t("customers.city")},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.shipping.city,"onUpdate:modelValue":t[34]||(t[34]=n=>e(o).currentCustomer.shipping.city=n),"content-loading":e(s),name:"shipping.city",type:"text"},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"]),r(u,{label:l.$t("customers.address"),"content-loading":e(s),error:e(a).currentCustomer.shipping.address_street_1.$error&&e(a).currentCustomer.shipping.address_street_1.$errors[0].$message||e(a).currentCustomer.shipping.address_street_2.$error&&e(a).currentCustomer.shipping.address_street_2.$errors[0].$message},{default:i(()=>[r(S,{modelValue:e(o).currentCustomer.shipping.address_street_1,"onUpdate:modelValue":t[35]||(t[35]=n=>e(o).currentCustomer.shipping.address_street_1=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",placeholder:l.$t("general.street_1"),name:"shipping_street1",onInput:t[36]||(t[36]=n=>e(a).currentCustomer.shipping.address_street_1.$touch())},null,8,["modelValue","content-loading","placeholder"]),r(S,{modelValue:e(o).currentCustomer.shipping.address_street_2,"onUpdate:modelValue":t[37]||(t[37]=n=>e(o).currentCustomer.shipping.address_street_2=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",placeholder:l.$t("general.street_2"),name:"shipping_street2",class:"mt-3","container-class":"mt-3",onInput:t[38]||(t[38]=n=>e(a).currentCustomer.shipping.address_street_2.$touch())},null,8,["modelValue","content-loading","placeholder"])]),_:1},8,["label","content-loading","error"]),m("div",Ee,[r(u,{"content-loading":e(s),label:l.$t("customers.phone"),class:"text-left"},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.shipping.phone,"onUpdate:modelValue":t[39]||(t[39]=n=>e(o).currentCustomer.shipping.phone=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"phone"},null,8,["modelValue","content-loading"])]),_:1},8,["content-loading","label"]),r(u,{label:l.$t("customers.zip_code"),"content-loading":e(s),class:"mt-2 text-left"},{default:i(()=>[r(d,{modelValue:e(o).currentCustomer.shipping.zip,"onUpdate:modelValue":t[40]||(t[40]=n=>e(o).currentCustomer.shipping.zip=n),modelModifiers:{trim:!0},"content-loading":e(s),type:"text",name:"zip"},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"])])]),_:1})])):V("",!0),e(b).customFields.length>0?(_(),$(k,{key:1,class:"mb-5 md:mb-8"})):V("",!0),m("div",Ge,[e(b).customFields.length>0?(_(),J("h6",Ne,v(l.$t("settings.custom_fields.title")),1)):V("",!0),m("div",Te,[r(we,{type:"Customer",store:e(o),"store-prop":"currentCustomer","is-edit":e(h),"is-loading":e(X),"custom-field-scope":j},null,8,["store","is-edit","is-loading"])])])]),_:1})],40,he)]),_:1})}}};export{Je as default}; diff --git a/crater/public/build/assets/Create.f0feda6b.js b/crater/public/build/assets/Create.f0feda6b.js new file mode 100644 index 0000000..019f36b --- /dev/null +++ b/crater/public/build/assets/Create.f0feda6b.js @@ -0,0 +1 @@ +var oe=Object.defineProperty,se=Object.defineProperties;var le=Object.getOwnPropertyDescriptors;var N=Object.getOwnPropertySymbols;var re=Object.prototype.hasOwnProperty,ie=Object.prototype.propertyIsEnumerable;var P=(u,e,r)=>e in u?oe(u,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):u[e]=r,b=(u,e)=>{for(var r in e||(e={}))re.call(e,r)&&P(u,r,e[r]);if(N)for(var r of N(e))ie.call(e,r)&&P(u,r,e[r]);return u},h=(u,e)=>se(u,le(e));import{J as me,G as ue,aN as ce,B as T,k as p,L as x,M as de,N as pe,S as _e,T as ge,r as s,o as M,l as w,w as l,f as o,u as t,h as j,x as q,i as E,t as G,j as L,m as Ie,U as fe}from"./vendor.d12b5734.js";import{p as ve,q as Be,c as be,b as $e,e as ye,g as Ve}from"./main.465728e1.js";import{_ as Se}from"./ItemUnitModal.031bb625.js";const he=["onSubmit"],Ue={setup(u){const e=ve(),r=Be(),$=be(),z=$e(),{t:_}=me(),y=ue(),A=ce(),D=ye(),I=T(!1),V=T(z.selectedCompanySettings.tax_per_item);let i=T(!1);e.$reset(),J();const v=p({get:()=>e.currentItem.price/100,set:n=>{e.currentItem.price=Math.round(n*100)}}),S=p({get:()=>{var n,a;return(a=(n=e==null?void 0:e.currentItem)==null?void 0:n.taxes)==null?void 0:a.map(d=>{if(d)return h(b({},d),{tax_type_id:d.id,tax_name:d.name+" ("+d.percent+"%)"})})},set:n=>{e.currentItem.taxes=n}}),B=p(()=>y.name==="items.edit"),U=p(()=>B.value?_("items.edit_item"):_("items.new_item")),R=p(()=>r.taxTypes.map(n=>h(b({},n),{tax_type_id:n.id,tax_name:n.name+" ("+n.percent+"%)"}))),Y=p(()=>V.value==="YES"),H=p(()=>({currentItem:{name:{required:x.withMessage(_("validation.required"),de),minLength:x.withMessage(_("validation.name_min_length",{count:3}),pe(3))},description:{maxLength:x.withMessage(_("validation.description_maxlength"),_e(65e3))}}})),c=ge(H,e);async function F(){$.openModal({title:_("settings.customization.items.add_item_unit"),componentName:"ItemUnitModal",size:"sm"})}async function J(){if(i.value=!0,await e.fetchItemUnits({limit:"all"}),D.hasAbilities(Ve.VIEW_TAX_TYPE)&&await r.fetchTaxTypes({limit:"all"}),B.value){let n=y.params.id;await e.fetchItem(n),e.currentItem.tax_per_item===1?V.value="YES":V.value="NO"}i.value=!1}async function O(){if(c.value.currentItem.$touch(),c.value.currentItem.$invalid)return!1;I.value=!0;try{let a=b({id:y.params.id},e.currentItem);e.currentItem&&e.currentItem.taxes&&(a.taxes=e.currentItem.taxes.map(g=>({tax_type_id:g.tax_type_id,amount:v.value*g.percent,percent:g.percent,name:g.name,collective_tax:0}))),await(B.value?e.updateItem:e.addItem)(a),I.value=!1,A.push("/admin/items"),n()}catch{I.value=!1;return}function n(){$.closeModal(),setTimeout(()=>{e.resetCurrentItem(),$.$reset(),c.value.$reset()},300)}}return(n,a)=>{const d=s("BaseBreadcrumbItem"),g=s("BaseBreadcrumb"),W=s("BasePageHeader"),X=s("BaseInput"),f=s("BaseInputGroup"),K=s("BaseMoney"),C=s("BaseIcon"),Q=s("BaseSelectAction"),k=s("BaseMultiselect"),Z=s("BaseTextarea"),ee=s("BaseButton"),te=s("BaseInputGrid"),ne=s("BaseCard"),ae=s("BasePage");return M(),w(ae,null,{default:l(()=>[o(W,{title:t(U)},{default:l(()=>[o(g,null,{default:l(()=>[o(d,{title:n.$t("general.home"),to:"dashboard"},null,8,["title"]),o(d,{title:n.$tc("items.item",2),to:"/admin/items"},null,8,["title"]),o(d,{title:t(U),to:"#",active:""},null,8,["title"])]),_:1})]),_:1},8,["title"]),o(Se),j("form",{class:"grid lg:grid-cols-2 mt-6",action:"submit",onSubmit:fe(O,["prevent"])},[o(ne,{class:"w-full"},{default:l(()=>[o(te,{layout:"one-column"},{default:l(()=>[o(f,{label:n.$t("items.name"),"content-loading":t(i),required:"",error:t(c).currentItem.name.$error&&t(c).currentItem.name.$errors[0].$message},{default:l(()=>[o(X,{modelValue:t(e).currentItem.name,"onUpdate:modelValue":a[0]||(a[0]=m=>t(e).currentItem.name=m),"content-loading":t(i),invalid:t(c).currentItem.name.$error,onInput:a[1]||(a[1]=m=>t(c).currentItem.name.$touch())},null,8,["modelValue","content-loading","invalid"])]),_:1},8,["label","content-loading","error"]),o(f,{label:n.$t("items.price"),"content-loading":t(i)},{default:l(()=>[o(K,{modelValue:t(v),"onUpdate:modelValue":a[2]||(a[2]=m=>q(v)?v.value=m:null),"content-loading":t(i)},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading"]),o(f,{"content-loading":t(i),label:n.$t("items.unit")},{default:l(()=>[o(k,{modelValue:t(e).currentItem.unit_id,"onUpdate:modelValue":a[3]||(a[3]=m=>t(e).currentItem.unit_id=m),"content-loading":t(i),label:"name",options:t(e).itemUnits,"value-prop":"id","can-deselect":!1,"can-clear":!1,placeholder:n.$t("items.select_a_unit"),searchable:"","track-by":"name"},{action:l(()=>[o(Q,{onClick:F},{default:l(()=>[o(C,{name:"PlusIcon",class:"h-4 mr-2 -ml-2 text-center text-primary-400"}),E(" "+G(n.$t("settings.customization.items.add_item_unit")),1)]),_:1})]),_:1},8,["modelValue","content-loading","options","placeholder"])]),_:1},8,["content-loading","label"]),t(Y)?(M(),w(f,{key:0,label:n.$t("items.taxes"),"content-loading":t(i)},{default:l(()=>[o(k,{modelValue:t(S),"onUpdate:modelValue":a[4]||(a[4]=m=>q(S)?S.value=m:null),"content-loading":t(i),options:t(R),mode:"tags",label:"tax_name",class:"w-full","value-prop":"id","can-deselect":!1,"can-clear":!1,searchable:"","track-by":"tax_name",object:""},null,8,["modelValue","content-loading","options"])]),_:1},8,["label","content-loading"])):L("",!0),o(f,{label:n.$t("items.description"),"content-loading":t(i),error:t(c).currentItem.description.$error&&t(c).currentItem.description.$errors[0].$message},{default:l(()=>[o(Z,{modelValue:t(e).currentItem.description,"onUpdate:modelValue":a[5]||(a[5]=m=>t(e).currentItem.description=m),"content-loading":t(i),name:"description",row:2,rows:"2",onInput:a[6]||(a[6]=m=>t(c).currentItem.description.$touch())},null,8,["modelValue","content-loading"])]),_:1},8,["label","content-loading","error"]),j("div",null,[o(ee,{"content-loading":t(i),type:"submit",loading:I.value},{left:l(m=>[I.value?L("",!0):(M(),w(C,{key:0,name:"SaveIcon",class:Ie(m.class)},null,8,["class"]))]),default:l(()=>[E(" "+G(t(B)?n.$t("items.update_item"):n.$t("items.save_item")),1)]),_:1},8,["content-loading","loading"])])]),_:1})]),_:1})],40,he)]),_:1})}}};export{Ue as default}; diff --git a/crater/public/build/assets/CreateCustomFields.c1c460e4.js b/crater/public/build/assets/CreateCustomFields.c1c460e4.js new file mode 100644 index 0000000..c92d271 --- /dev/null +++ b/crater/public/build/assets/CreateCustomFields.c1c460e4.js @@ -0,0 +1 @@ +var I=Object.defineProperty,b=Object.defineProperties;var g=Object.getOwnPropertyDescriptors;var y=Object.getOwnPropertySymbols;var q=Object.prototype.hasOwnProperty,h=Object.prototype.propertyIsEnumerable;var f=(e,t,r)=>t in e?I(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,_=(e,t)=>{for(var r in t||(t={}))q.call(t,r)&&f(e,r,t[r]);if(y)for(var r of y(t))h.call(t,r)&&f(e,r,t[r]);return e},v=(e,t)=>b(e,g(t));import{J as j,L as w,O as V,T as L,k as T,aE as F,r as E,o as n,l as m,w as P,aj as O,u as c,_ as S,C as x,e as D,f as A,F as R,y as k,j as B,I as C}from"./vendor.d12b5734.js";import{o as i,m as Y}from"./main.465728e1.js";function $(e){switch(e){case"./types/DateTimeType.vue":return i(()=>import("./DateTimeType.6886ff98.js"),["assets/DateTimeType.6886ff98.js","assets/vendor.d12b5734.js"]);case"./types/DateType.vue":return i(()=>import("./DateType.12fc8765.js"),["assets/DateType.12fc8765.js","assets/vendor.d12b5734.js"]);case"./types/DropdownType.vue":return i(()=>import("./DropdownType.2d01b840.js"),["assets/DropdownType.2d01b840.js","assets/vendor.d12b5734.js"]);case"./types/InputType.vue":return i(()=>import("./InputType.cf0dfc7c.js"),["assets/InputType.cf0dfc7c.js","assets/vendor.d12b5734.js"]);case"./types/NumberType.vue":return i(()=>import("./NumberType.7b73360f.js"),["assets/NumberType.7b73360f.js","assets/vendor.d12b5734.js"]);case"./types/PhoneType.vue":return i(()=>import("./PhoneType.29ae66c8.js"),["assets/PhoneType.29ae66c8.js","assets/vendor.d12b5734.js"]);case"./types/SwitchType.vue":return i(()=>import("./SwitchType.591a8b07.js"),["assets/SwitchType.591a8b07.js","assets/vendor.d12b5734.js"]);case"./types/TextAreaType.vue":return i(()=>import("./TextAreaType.27565abe.js"),["assets/TextAreaType.27565abe.js","assets/vendor.d12b5734.js"]);case"./types/TimeType.vue":return i(()=>import("./TimeType.8ac8afd1.js"),["assets/TimeType.8ac8afd1.js","assets/vendor.d12b5734.js"]);case"./types/UrlType.vue":return i(()=>import("./UrlType.d123ab64.js"),["assets/UrlType.d123ab64.js","assets/vendor.d12b5734.js"]);default:return new Promise(function(t,r){(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(r.bind(null,new Error("Unknown variable dynamic import: "+e)))})}}const M={props:{field:{type:Object,required:!0},customFieldScope:{type:String,required:!0},index:{type:Number,required:!0},store:{type:Object,required:!0},storeProp:{type:String,required:!0}},setup(e){const t=e,{t:r}=j(),d={value:{required:w.withMessage(r("validation.required"),V(t.field.is_required))}},a=L(d,T(()=>t.field),{$scope:t.customFieldScope}),o=T(()=>t.field.type?F(()=>$(`./types/${t.field.type}Type.vue`)):!1);return(u,s)=>{const l=E("BaseInputGroup");return n(),m(l,{label:e.field.label,required:!!e.field.is_required,error:c(a).value.$error&&c(a).value.$errors[0].$message},{default:P(()=>[(n(),m(O(c(o)),{modelValue:e.field.value,"onUpdate:modelValue":s[0]||(s[0]=p=>e.field.value=p),options:e.field.options,invalid:c(a).value.$error,placeholder:e.field.placeholder},null,8,["modelValue","options","invalid","placeholder"]))]),_:1},8,["label","required","error"])}}},N={key:0},J={props:{store:{type:Object,required:!0},storeProp:{type:String,required:!0},isEdit:{type:Boolean,default:!1},type:{type:String,default:null},gridLayout:{type:String,default:"two-column"},isLoading:{type:Boolean,default:null},customFieldScope:{type:String,required:!0}},setup(e){const t=e,r=Y();a();function d(){t.isEdit&&t.store[t.storeProp].fields.forEach(o=>{const u=t.store[t.storeProp].customFields.findIndex(s=>s.id===o.custom_field_id);if(u>-1){let s=o.default_answer;s&&o.custom_field.type==="DateTime"&&(s=C(o.default_answer,"YYYY-MM-DD HH:mm:ss").format("YYYY-MM-DD HH:mm")),t.store[t.storeProp].customFields[u]=v(_({},o),{id:o.custom_field_id,value:s,label:o.custom_field.label,options:o.custom_field.options,is_required:o.custom_field.is_required,placeholder:o.custom_field.placeholder,order:o.custom_field.order})}})}async function a(){let u=(await r.fetchCustomFields({type:t.type,limit:"all"})).data.data;u.map(s=>s.value=s.default_answer),t.store[t.storeProp].customFields=S.sortBy(u,s=>s.order),d()}return x(()=>t.store[t.storeProp].fields,o=>{d()}),(o,u)=>{const s=E("BaseInputGrid");return e.store[e.storeProp]&&e.store[e.storeProp].customFields.length>0&&!e.isLoading?(n(),D("div",N,[A(s,{layout:e.gridLayout},{default:P(()=>[(n(!0),D(R,null,k(e.store[e.storeProp].customFields,(l,p)=>(n(),m(M,{key:l.id,"custom-field-scope":e.customFieldScope,store:e.store,"store-prop":e.storeProp,index:p,field:l},null,8,["custom-field-scope","store","store-prop","index","field"]))),128))]),_:1},8,["layout"])])):B("",!0)}}};export{J as _}; diff --git a/crater/public/build/assets/CustomFieldsSetting.feceee26.js b/crater/public/build/assets/CustomFieldsSetting.feceee26.js new file mode 100644 index 0000000..7b19886 --- /dev/null +++ b/crater/public/build/assets/CustomFieldsSetting.feceee26.js @@ -0,0 +1 @@ +var ie=Object.defineProperty;var W=Object.getOwnPropertySymbols;var de=Object.prototype.hasOwnProperty,me=Object.prototype.propertyIsEnumerable;var Z=(m,n,e)=>n in m?ie(m,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):m[n]=e,ee=(m,n)=>{for(var e in n||(n={}))de.call(n,e)&&Z(m,e,n[e]);if(W)for(var e of W(n))me.call(n,e)&&Z(m,e,n[e]);return m};import{J as H,G as ce,ah as te,r as d,o as C,l as F,w as u,f as l,u as t,i as B,t as $,j as M,B as L,e as z,aY as pe,U as se,a0 as le,k as D,aE as _e,L as k,M as A,aT as fe,T as ye,h as O,x as oe,y as ve,m as G,F as Ce,aj as be,V as ge}from"./vendor.d12b5734.js";import{j as Fe,u as Te,m as K,e as ae,c as Y,g as U,o as T}from"./main.465728e1.js";const we={props:{row:{type:Object,default:null},table:{type:Object,default:null},loadData:{type:Function,default:null}},setup(m){const n=m,e=Fe();Te();const{t:i}=H(),v=K();ce();const f=ae(),c=Y();te("utils");async function p(b){await v.fetchCustomField(b),c.openModal({title:i("settings.custom_fields.edit_custom_field"),componentName:"CustomFieldModal",size:"sm",data:b,refreshData:n.loadData})}async function V(b){e.openDialog({title:i("general.are_you_sure"),message:i("settings.custom_fields.custom_field_confirm_delete"),yesLabel:i("general.ok"),noLabel:i("general.cancel"),variant:"danger",hideNoButton:!1,size:"lg"}).then(async g=>{g&&(await v.deleteCustomFields(b),n.loadData&&n.loadData())})}return(b,g)=>{const y=d("BaseIcon"),I=d("BaseDropdownItem"),h=d("BaseDropdown");return C(),F(h,null,{activator:u(()=>[l(y,{name:"DotsHorizontalIcon",class:"h-5 text-gray-500"})]),default:u(()=>[t(f).hasAbilities(t(U).EDIT_CUSTOM_FIELDS)?(C(),F(I,{key:0,onClick:g[0]||(g[0]=o=>p(m.row.id))},{default:u(()=>[l(y,{name:"PencilIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),B(" "+$(b.$t("general.edit")),1)]),_:1})):M("",!0),t(f).hasAbilities(t(U).DELETE_CUSTOM_FIELDS)?(C(),F(I,{key:1,onClick:g[1]||(g[1]=o=>V(m.row.id))},{default:u(()=>[l(y,{name:"TrashIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),B(" "+$(b.$t("general.delete")),1)]),_:1})):M("",!0)]),_:1})}}},$e={class:"flex items-center mt-1"},Ie={emits:["onAdd"],setup(m,{emit:n}){const e=L(null);function i(){if(e.value==null||e.value==""||e.value==null)return!0;n("onAdd",e.value),e.value=null}return(v,f)=>{const c=d("BaseInput"),p=d("BaseIcon");return C(),z("div",$e,[l(c,{modelValue:e.value,"onUpdate:modelValue":f[0]||(f[0]=V=>e.value=V),type:"text",class:"w-full md:w-96",placeholder:v.$t("settings.custom_fields.press_enter_to_add"),onClick:i,onKeydown:pe(se(i,["prevent","stop"]),["enter"])},null,8,["modelValue","placeholder","onKeydown"]),l(p,{name:"PlusCircleIcon",class:"ml-1 text-primary-500 cursor-pointer",onClick:i})])}}};function he(m){switch(m){case"../../custom-fields/types/DateTimeType.vue":return T(()=>import("./DateTimeType.6886ff98.js"),["assets/DateTimeType.6886ff98.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/DateType.vue":return T(()=>import("./DateType.12fc8765.js"),["assets/DateType.12fc8765.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/DropdownType.vue":return T(()=>import("./DropdownType.2d01b840.js"),["assets/DropdownType.2d01b840.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/InputType.vue":return T(()=>import("./InputType.cf0dfc7c.js"),["assets/InputType.cf0dfc7c.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/NumberType.vue":return T(()=>import("./NumberType.7b73360f.js"),["assets/NumberType.7b73360f.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/PhoneType.vue":return T(()=>import("./PhoneType.29ae66c8.js"),["assets/PhoneType.29ae66c8.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/SwitchType.vue":return T(()=>import("./SwitchType.591a8b07.js"),["assets/SwitchType.591a8b07.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/TextAreaType.vue":return T(()=>import("./TextAreaType.27565abe.js"),["assets/TextAreaType.27565abe.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/TimeType.vue":return T(()=>import("./TimeType.8ac8afd1.js"),["assets/TimeType.8ac8afd1.js","assets/vendor.d12b5734.js"]);case"../../custom-fields/types/UrlType.vue":return T(()=>import("./UrlType.d123ab64.js"),["assets/UrlType.d123ab64.js","assets/vendor.d12b5734.js"]);default:return new Promise(function(n,e){(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(e.bind(null,new Error("Unknown variable dynamic import: "+m)))})}}const Be={class:"flex justify-between w-full"},De=["onSubmit"],Ve={class:"overflow-y-auto max-h-[550px]"},Se={class:"px-4 md:px-8 py-8 overflow-y-auto sm:p-6"},Ee={class:"z-0 flex justify-end p-4 border-t border-solid border-gray-light border-modal-bg"},qe={setup(m){const n=Y(),e=K(),{t:i}=H();let v=L(!1);const f=le(["Customer","Invoice","Estimate","Expense","Payment"]),c=le([{label:"Text",value:"Input"},{label:"Textarea",value:"TextArea"},{label:"Phone",value:"Phone"},{label:"URL",value:"Url"},{label:"Number",value:"Number"},{label:"Select Field",value:"Dropdown"},{label:"Switch Toggle",value:"Switch"},{label:"Date",value:"Date"},{label:"Time",value:"Time"},{label:"Date & Time",value:"DateTime"}]);let p=L(c[0]);const V=D(()=>n.active&&n.componentName==="CustomFieldModal"),b=D(()=>p.value&&p.value.label==="Switch Toggle"),g=D(()=>p.value&&p.value.label==="Select Field"),y=D(()=>e.currentCustomField.type?_e(()=>he(`../../custom-fields/types/${e.currentCustomField.type}Type.vue`)):!1),I=D({get:()=>e.currentCustomField.is_required===1,set:s=>{const a=s?1:0;e.currentCustomField.is_required=a}}),h=D(()=>({currentCustomField:{type:{required:k.withMessage(i("validation.required"),A)},name:{required:k.withMessage(i("validation.required"),A)},label:{required:k.withMessage(i("validation.required"),A)},model_type:{required:k.withMessage(i("validation.required"),A)},order:{required:k.withMessage(i("validation.required"),A),numeric:k.withMessage(i("validation.numbers_only"),fe)},type:{required:k.withMessage(i("validation.required"),A)}}})),o=ye(h,D(()=>e));function S(){e.isEdit?p.value=c.find(s=>s.value==e.currentCustomField.type):(e.currentCustomField.model_type=f[0],e.currentCustomField.type=c[0].value,p.value=c[0])}async function P(){if(o.value.currentCustomField.$touch(),o.value.currentCustomField.$invalid)return!0;v.value=!0;let s=ee({},e.currentCustomField);if(e.currentCustomField.options&&(s.options=e.currentCustomField.options.map(E=>E.name)),s.type=="Time"&&typeof s.default_answer=="object"){let E=s&&s.default_answer&&s.default_answer.HH?s.default_answer.HH:null,q=s&&s.default_answer&&s.default_answer.mm?s.default_answer.mm:null;s&&s.default_answer&&s.default_answer.ss&&s.default_answer.ss,s.default_answer=`${E}:${q}`}await(e.isEdit?e.updateCustomField:e.addCustomField)(s),v.value=!1,n.refreshData&&n.refreshData(),R()}function x(s){e.currentCustomField.options=[{name:s},...e.currentCustomField.options]}function _(s){if(e.isEdit&&e.currentCustomField.in_use)return;e.currentCustomField.options[s].name===e.currentCustomField.default_answer&&(e.currentCustomField.default_answer=null),e.currentCustomField.options.splice(s,1)}function N(s){e.currentCustomField.type=s.value}function R(){n.closeModal(),setTimeout(()=>{e.resetCurrentCustomField(),o.value.$reset()},300)}return(s,a)=>{const E=d("BaseIcon"),q=d("BaseInput"),w=d("BaseInputGroup"),J=d("BaseMultiselect"),re=d("BaseSwitch"),ne=d("BaseInputGrid"),X=d("BaseButton"),ue=d("BaseModal");return C(),F(ue,{show:t(V),onOpen:S},{header:u(()=>[O("div",Be,[B($(t(n).title)+" ",1),l(E,{name:"XIcon",class:"w-6 h-6 text-gray-500 cursor-pointer",onClick:R})])]),default:u(()=>[O("form",{action:"",onSubmit:se(P,["prevent"])},[O("div",Ve,[O("div",Se,[l(ne,{layout:"one-column"},{default:u(()=>[l(w,{label:s.$t("settings.custom_fields.name"),required:"",error:t(o).currentCustomField.name.$error&&t(o).currentCustomField.name.$errors[0].$message},{default:u(()=>[l(q,{ref:(r,j)=>{j.name=r},modelValue:t(e).currentCustomField.name,"onUpdate:modelValue":a[0]||(a[0]=r=>t(e).currentCustomField.name=r),invalid:t(o).currentCustomField.name.$error,onInput:a[1]||(a[1]=r=>t(o).currentCustomField.name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),l(w,{label:s.$t("settings.custom_fields.model"),error:t(o).currentCustomField.model_type.$error&&t(o).currentCustomField.model_type.$errors[0].$message,"help-text":t(e).currentCustomField.in_use?s.$t("settings.custom_fields.model_in_use"):"",required:""},{default:u(()=>[l(J,{modelValue:t(e).currentCustomField.model_type,"onUpdate:modelValue":a[2]||(a[2]=r=>t(e).currentCustomField.model_type=r),options:t(f),"can-deselect":!1,invalid:t(o).currentCustomField.model_type.$error,searchable:!0,disabled:t(e).currentCustomField.in_use,onInput:a[3]||(a[3]=r=>t(o).currentCustomField.model_type.$touch())},null,8,["modelValue","options","invalid","disabled"])]),_:1},8,["label","error","help-text"]),l(w,{class:"flex items-center space-x-4",label:s.$t("settings.custom_fields.required")},{default:u(()=>[l(re,{modelValue:t(I),"onUpdate:modelValue":a[4]||(a[4]=r=>oe(I)?I.value=r:null)},null,8,["modelValue"])]),_:1},8,["label"]),l(w,{label:s.$t("settings.custom_fields.type"),error:t(o).currentCustomField.type.$error&&t(o).currentCustomField.type.$errors[0].$message,"help-text":t(e).currentCustomField.in_use?s.$t("settings.custom_fields.type_in_use"):"",required:""},{default:u(()=>[l(J,{modelValue:t(p),"onUpdate:modelValue":[a[5]||(a[5]=r=>oe(p)?p.value=r:p=r),N],options:t(c),invalid:t(o).currentCustomField.type.$error,disabled:t(e).currentCustomField.in_use,searchable:!0,"can-deselect":!1,object:""},null,8,["modelValue","options","invalid","disabled"])]),_:1},8,["label","error","help-text"]),l(w,{label:s.$t("settings.custom_fields.label"),required:"",error:t(o).currentCustomField.label.$error&&t(o).currentCustomField.label.$errors[0].$message},{default:u(()=>[l(q,{modelValue:t(e).currentCustomField.label,"onUpdate:modelValue":a[6]||(a[6]=r=>t(e).currentCustomField.label=r),invalid:t(o).currentCustomField.label.$error,onInput:a[7]||(a[7]=r=>t(o).currentCustomField.label.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),t(g)?(C(),F(w,{key:0,label:s.$t("settings.custom_fields.options")},{default:u(()=>[l(Ie,{onOnAdd:x}),(C(!0),z(Ce,null,ve(t(e).currentCustomField.options,(r,j)=>(C(),z("div",{key:j,class:"flex items-center mt-5"},[l(q,{modelValue:r.name,"onUpdate:modelValue":Q=>r.name=Q,class:"w-64"},null,8,["modelValue","onUpdate:modelValue"]),l(E,{name:"MinusCircleIcon",class:G(["ml-1 cursor-pointer",t(e).currentCustomField.in_use?"text-gray-300":"text-red-300"]),onClick:Q=>_(j)},null,8,["class","onClick"])]))),128))]),_:1},8,["label"])):M("",!0),l(w,{label:s.$t("settings.custom_fields.default_value"),class:"relative"},{default:u(()=>[(C(),F(be(t(y)),{modelValue:t(e).currentCustomField.default_answer,"onUpdate:modelValue":a[8]||(a[8]=r=>t(e).currentCustomField.default_answer=r),options:t(e).currentCustomField.options,"default-date-time":t(e).currentCustomField.dateTimeValue},null,8,["modelValue","options","default-date-time"]))]),_:1},8,["label"]),t(b)?M("",!0):(C(),F(w,{key:1,label:s.$t("settings.custom_fields.placeholder")},{default:u(()=>[l(q,{modelValue:t(e).currentCustomField.placeholder,"onUpdate:modelValue":a[9]||(a[9]=r=>t(e).currentCustomField.placeholder=r)},null,8,["modelValue"])]),_:1},8,["label"])),l(w,{label:s.$t("settings.custom_fields.order"),error:t(o).currentCustomField.order.$error&&t(o).currentCustomField.order.$errors[0].$message,required:""},{default:u(()=>[l(q,{modelValue:t(e).currentCustomField.order,"onUpdate:modelValue":a[10]||(a[10]=r=>t(e).currentCustomField.order=r),type:"number",invalid:t(o).currentCustomField.order.$error,onInput:a[11]||(a[11]=r=>t(o).currentCustomField.order.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"])]),_:1})])]),O("div",Ee,[l(X,{class:"mr-3",type:"button",variant:"primary-outline",onClick:R},{default:u(()=>[B($(s.$t("general.cancel")),1)]),_:1}),l(X,{variant:"primary",loading:t(v),disabled:t(v),type:"submit"},{left:u(r=>[t(v)?M("",!0):(C(),F(E,{key:0,class:G(r.class),name:"SaveIcon"},null,8,["class"]))]),default:u(()=>[B(" "+$(t(e).isEdit?s.$t("general.update"):s.$t("general.save")),1)]),_:1},8,["loading","disabled"])])],40,De)]),_:1},8,["show"])}}},ke={class:"text-xs text-gray-500"},Ue={setup(m){const n=Y(),e=K(),i=ae(),v=te("utils"),{t:f}=H(),c=L(null),p=D(()=>[{key:"name",label:f("settings.custom_fields.name"),thClass:"extra",tdClass:"font-medium text-gray-900"},{key:"model_type",label:f("settings.custom_fields.model")},{key:"type",label:f("settings.custom_fields.type")},{key:"is_required",label:f("settings.custom_fields.required")},{key:"actions",label:"",tdClass:"text-right text-sm font-medium",sortable:!1}]);async function V({page:y,filter:I,sort:h}){let o={orderByField:h.fieldName||"created_at",orderBy:h.order||"desc",page:y},S=await e.fetchCustomFields(o);return{data:S.data.data,pagination:{totalPages:S.data.meta.last_page,currentPage:y,limit:5,totalCount:S.data.meta.total}}}function b(){n.openModal({title:f("settings.custom_fields.add_custom_field"),componentName:"CustomFieldModal",size:"sm",refreshData:c.value&&c.value.refresh})}async function g(){c.value&&c.value.refresh()}return(y,I)=>{const h=d("BaseIcon"),o=d("BaseButton"),S=d("BaseBadge"),P=d("BaseTable"),x=d("BaseSettingCard");return C(),F(x,{title:y.$t("settings.menu_title.custom_fields"),description:y.$t("settings.custom_fields.section_description")},{action:u(()=>[t(i).hasAbilities(t(U).CREATE_CUSTOM_FIELDS)?(C(),F(o,{key:0,variant:"primary-outline",onClick:b},{left:u(_=>[l(h,{class:G(_.class),name:"PlusIcon"},null,8,["class"]),B(" "+$(y.$t("settings.custom_fields.add_custom_field")),1)]),_:1})):M("",!0)]),default:u(()=>[l(qe),l(P,{ref:(_,N)=>{N.table=_,c.value=_},data:V,columns:t(p),class:"mt-16"},ge({"cell-name":u(({row:_})=>[B($(_.data.name)+" ",1),O("span",ke," ("+$(_.data.slug)+")",1)]),"cell-is_required":u(({row:_})=>[l(S,{"bg-color":t(v).getBadgeStatusColor(_.data.is_required?"YES":"NO").bgColor,color:t(v).getBadgeStatusColor(_.data.is_required?"YES":"NO").color},{default:u(()=>[B($(_.data.is_required?y.$t("settings.custom_fields.yes"):y.$t("settings.custom_fields.no").replace("_"," ")),1)]),_:2},1032,["bg-color","color"])]),_:2},[t(i).hasAbilities([t(U).DELETE_CUSTOM_FIELDS,t(U).EDIT_CUSTOM_FIELDS])?{name:"cell-actions",fn:u(({row:_})=>[l(we,{row:_.data,table:c.value,"load-data":g},null,8,["row","table"])])}:void 0]),1032,["columns"])]),_:1},8,["title","description"])}}};export{Ue as default}; diff --git a/crater/public/build/assets/CustomerIndexDropdown.bf4b48d6.js b/crater/public/build/assets/CustomerIndexDropdown.bf4b48d6.js new file mode 100644 index 0000000..aa10995 --- /dev/null +++ b/crater/public/build/assets/CustomerIndexDropdown.bf4b48d6.js @@ -0,0 +1 @@ +import{l as S,u as b,j as C,e as x,g}from"./main.465728e1.js";import{J as E,G as j,aN as T,ah as N,r as l,o as a,l as s,w as t,u as e,f as n,i as p,t as f,j as y}from"./vendor.d12b5734.js";const V={props:{row:{type:Object,default:null},table:{type:Object,default:null},loadData:{type:Function,default:()=>{}}},setup(i){const w=i,_=S();b();const v=C(),m=x(),{t:u}=E(),h=j();T(),N("utils");function B(r){v.openDialog({title:u("general.are_you_sure"),message:u("customers.confirm_delete",1),yesLabel:u("general.ok"),noLabel:u("general.cancel"),variant:"danger",hideNoButton:!1,size:"lg"}).then(c=>{c&&_.deleteCustomer({ids:[r]}).then(o=>{if(o.data.success)return w.loadData&&w.loadData(),!0})})}return(r,c)=>{const o=l("BaseIcon"),I=l("BaseButton"),d=l("BaseDropdownItem"),D=l("router-link"),k=l("BaseDropdown");return a(),s(k,{"content-loading":e(_).isFetchingViewData},{activator:t(()=>[e(h).name==="customers.view"?(a(),s(I,{key:0,variant:"primary"},{default:t(()=>[n(o,{name:"DotsHorizontalIcon",class:"h-5 text-white"})]),_:1})):(a(),s(o,{key:1,name:"DotsHorizontalIcon",class:"h-5 text-gray-500"}))]),default:t(()=>[e(m).hasAbilities(e(g).EDIT_CUSTOMER)?(a(),s(D,{key:0,to:`/admin/customers/${i.row.id}/edit`},{default:t(()=>[n(d,null,{default:t(()=>[n(o,{name:"PencilIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),p(" "+f(r.$t("general.edit")),1)]),_:1})]),_:1},8,["to"])):y("",!0),e(h).name!=="customers.view"&&e(m).hasAbilities(e(g).VIEW_CUSTOMER)?(a(),s(D,{key:1,to:`customers/${i.row.id}/view`},{default:t(()=>[n(d,null,{default:t(()=>[n(o,{name:"EyeIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),p(" "+f(r.$t("general.view")),1)]),_:1})]),_:1},8,["to"])):y("",!0),e(m).hasAbilities(e(g).DELETE_CUSTOMER)?(a(),s(d,{key:2,onClick:c[0]||(c[0]=$=>B(i.row.id))},{default:t(()=>[n(o,{name:"TrashIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),p(" "+f(r.$t("general.delete")),1)]),_:1})):y("",!0)]),_:1},8,["content-loading"])}}};export{V as _}; diff --git a/crater/public/build/assets/CustomerSettings.295ae76d.js b/crater/public/build/assets/CustomerSettings.295ae76d.js new file mode 100644 index 0000000..7f8880a --- /dev/null +++ b/crater/public/build/assets/CustomerSettings.295ae76d.js @@ -0,0 +1 @@ +import{G as R,J as G,B as p,k as C,L as c,M as k,N as S,Q as L,P,T as A,r as v,o as g,e as D,f as u,w as i,h as _,t as h,u as e,x as b,l as y,m as O,j as T,i as z,U as J}from"./vendor.d12b5734.js";import{a as Q,u as H}from"./global.dc565c4e.js";import"./auth.c88ceb4c.js";import"./main.465728e1.js";const K=["onSubmit"],W={class:"font-bold text-left"},X={class:"mt-2 text-sm leading-snug text-left text-gray-500",style:{"max-width":"680px"}},Y={class:"grid gap-6 sm:grid-col-1 md:grid-cols-2 mt-6"},Z=_("span",null,null,-1),te={setup(ee){const r=Q();H(),R();const{t:m,tm:U}=G();let f=p([]),d=p(!1),w=p(null),n=p(!1),l=p(!1);const I=p(!1);r.userForm.avatar&&f.value.push({image:r.userForm.avatar});const x=C(()=>({userForm:{name:{required:c.withMessage(m("validation.required"),k),minLength:c.withMessage(m("validation.name_min_length",{count:3}),S(3))},email:{required:c.withMessage(m("validation.required"),k),email:c.withMessage(m("validation.email_incorrect"),L)},password:{minLength:c.withMessage(m("validation.password_min_length",{count:8}),S(8))},confirm_password:{sameAsPassword:c.withMessage(m("validation.password_incorrect"),P(r.userForm.password))}}})),o=A(x,C(()=>r));function M(t,s){w.value=s}function q(){w.value=null,I.value=!0}function N(){if(o.value.userForm.$touch(),o.value.userForm.$invalid)return!0;d.value=!0;let t=new FormData;t.append("name",r.userForm.name),t.append("email",r.userForm.email),r.userForm.password!=null&&r.userForm.password!==void 0&&r.userForm.password!==""&&t.append("password",r.userForm.password),w.value&&t.append("customer_avatar",w.value),t.append("is_customer_avatar_removed",I.value),r.updateCurrentUser({data:t,message:U("settings.account_settings.updated_message")}).then(s=>{s.data.data&&(d.value=!1,r.$patch(B=>{B.userForm.password="",B.userForm.confirm_password=""}),w.value=null,I.value=!1)}).catch(s=>{d.value=!1})}return(t,s)=>{const B=v("BaseFileUploader"),F=v("BaseInputGroup"),V=v("BaseInput"),$=v("BaseIcon"),j=v("BaseButton"),E=v("BaseCard");return g(),D("form",{class:"relative h-full mt-4",onSubmit:J(N,["prevent"])},[u(E,null,{default:i(()=>[_("div",null,[_("h6",W,h(t.$t("settings.account_settings.account_settings")),1),_("p",X,h(t.$t("settings.account_settings.section_description")),1)]),_("div",Y,[u(F,{label:t.$tc("settings.account_settings.profile_picture")},{default:i(()=>[u(B,{modelValue:e(f),"onUpdate:modelValue":s[0]||(s[0]=a=>b(f)?f.value=a:f=a),avatar:!0,accept:"image/*",onChange:M,onRemove:q},null,8,["modelValue"])]),_:1},8,["label"]),Z,u(F,{label:t.$tc("settings.account_settings.name"),error:e(o).userForm.name.$error&&e(o).userForm.name.$errors[0].$message,required:""},{default:i(()=>[u(V,{modelValue:e(r).userForm.name,"onUpdate:modelValue":s[1]||(s[1]=a=>e(r).userForm.name=a),invalid:e(o).userForm.name.$error,onInput:s[2]||(s[2]=a=>e(o).userForm.name.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),u(F,{label:t.$tc("settings.account_settings.email"),error:e(o).userForm.email.$error&&e(o).userForm.email.$errors[0].$message,required:""},{default:i(()=>[u(V,{modelValue:e(r).userForm.email,"onUpdate:modelValue":s[3]||(s[3]=a=>e(r).userForm.email=a),invalid:e(o).userForm.email.$error,onInput:s[4]||(s[4]=a=>e(o).userForm.email.$touch())},null,8,["modelValue","invalid"])]),_:1},8,["label","error"]),u(F,{error:e(o).userForm.password.$error&&e(o).userForm.password.$errors[0].$message,label:t.$tc("settings.account_settings.password")},{default:i(()=>[u(V,{modelValue:e(r).userForm.password,"onUpdate:modelValue":s[7]||(s[7]=a=>e(r).userForm.password=a),type:e(n)?"text":"password",invalid:e(o).userForm.password.$error,onInput:s[8]||(s[8]=a=>e(o).userForm.password.$touch())},{right:i(()=>[e(n)?(g(),y($,{key:0,name:"EyeOffIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:s[5]||(s[5]=a=>b(n)?n.value=!e(n):n=!e(n))})):(g(),y($,{key:1,name:"EyeIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:s[6]||(s[6]=a=>b(n)?n.value=!e(n):n=!e(n))}))]),_:1},8,["modelValue","type","invalid"])]),_:1},8,["error","label"]),u(F,{label:t.$tc("settings.account_settings.confirm_password"),error:e(o).userForm.confirm_password.$error&&e(o).userForm.confirm_password.$errors[0].$message},{default:i(()=>[u(V,{modelValue:e(r).userForm.confirm_password,"onUpdate:modelValue":s[11]||(s[11]=a=>e(r).userForm.confirm_password=a),type:e(l)?"text":"password",invalid:e(o).userForm.confirm_password.$error,onInput:s[12]||(s[12]=a=>e(o).userForm.confirm_password.$touch())},{right:i(()=>[e(l)?(g(),y($,{key:0,name:"EyeOffIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:s[9]||(s[9]=a=>b(l)?l.value=!e(l):l=!e(l))})):(g(),y($,{key:1,name:"EyeIcon",class:"w-5 h-5 mr-1 text-gray-500 cursor-pointer",onClick:s[10]||(s[10]=a=>b(l)?l.value=!e(l):l=!e(l))}))]),_:1},8,["modelValue","type","invalid"])]),_:1},8,["label","error"])]),u(j,{loading:e(d),disabled:e(d),class:"mt-6"},{left:i(a=>[e(d)?T("",!0):(g(),y($,{key:0,name:"SaveIcon",class:O(a.class)},null,8,["class"]))]),default:i(()=>[z(" "+h(t.$t("general.save")),1)]),_:1},8,["loading","disabled"])]),_:1})],40,K)}}};export{te as default}; diff --git a/crater/public/build/assets/CustomizationSetting.31d8c655.js b/crater/public/build/assets/CustomizationSetting.31d8c655.js new file mode 100644 index 0000000..8440c75 --- /dev/null +++ b/crater/public/build/assets/CustomizationSetting.31d8c655.js @@ -0,0 +1 @@ +var ut=Object.defineProperty,rt=Object.defineProperties;var dt=Object.getOwnPropertyDescriptors;var et=Object.getOwnPropertySymbols;var ct=Object.prototype.hasOwnProperty,_t=Object.prototype.propertyIsEnumerable;var st=(v,o,i)=>o in v?ut(v,o,{enumerable:!0,configurable:!0,writable:!0,value:i}):v[o]=i,x=(v,o)=>{for(var i in o||(o={}))ct.call(o,i)&&st(v,i,o[i]);if(et)for(var i of et(o))_t.call(o,i)&&st(v,i,o[i]);return v},W=(v,o)=>rt(v,dt(o));import{b as N,d as Z,i as pt,k as gt,p as yt,c as ft,j as vt}from"./main.465728e1.js";import{J as j,B as z,k as F,C as bt,H as at,$ as St,r as d,o as $,e as D,h as c,t as b,f as t,w as r,U as Y,m as G,i as k,F as L,y as $t,l as E,u as e,j as R,ah as M,a0 as T,L as X,O as nt,aT as it,T as ot,x as H}from"./vendor.d12b5734.js";import{D as Bt,d as ht}from"./DragIcon.2da3872a.js";import{u as zt}from"./payment.93619753.js";import{_ as Vt}from"./ItemUnitModal.031bb625.js";const It={class:"text-gray-900 text-lg font-medium"},xt={class:"mt-1 text-sm text-gray-500"},wt={class:"overflow-x-auto"},Ct={class:"w-full mt-6 table-fixed"},Dt=c("colgroup",null,[c("col",{style:{width:"4%"}}),c("col",{style:{width:"45%"}}),c("col",{style:{width:"27%"}}),c("col",{style:{width:"24%"}})],-1),Ut=c("thead",null,[c("tr",null,[c("th",{class:"px-5 py-3 text-sm not-italic font-medium leading-5 text-left text-gray-700 border-t border-b border-gray-200 border-solid"}),c("th",{class:"px-5 py-3 text-sm not-italic font-medium leading-5 text-left text-gray-700 border-t border-b border-gray-200 border-solid"}," Component "),c("th",{class:"px-5 py-3 text-sm not-italic font-medium leading-5 text-left text-gray-700 border-t border-b border-gray-200 border-solid"}," Parameter "),c("th",{class:"px-5 py-3 text-sm not-italic font-medium leading-5 text-left text-gray-700 border-t border-b border-gray-200 border-solid"})])],-1),Ft={class:"relative"},kt={class:"text-gray-300 cursor-move handle align-middle"},Et={class:"px-5 py-4"},Nt={class:"block text-sm not-italic font-medium text-primary-800 whitespace-nowrap mr-2 min-w-[200px]"},Mt={class:"text-xs text-gray-500 mt-1"},Tt={class:"px-5 py-4 text-left align-middle"},Gt={class:"px-5 py-4 text-right align-middle pt-10"},qt=k(" Remove "),Lt={colspan:"2",class:"px-5 py-4"},Rt={class:"px-5 py-4 text-right align-middle",colspan:"2"},tt={props:{type:{type:String,required:!0},typeStore:{type:Object,required:!0},defaultSeries:{type:String,default:"INV"}},setup(v){const o=v,{t:i}=j(),p=N(),g=Z(),u=z([]),a=z(!1),m=z([{label:i("settings.customization.series"),description:i("settings.customization.series_description"),name:"SERIES",paramLabel:i("settings.customization.series_param_label"),value:o.defaultSeries,inputDisabled:!1,inputType:"text",allowMultiple:!1},{label:i("settings.customization.sequence"),description:i("settings.customization.sequence_description"),name:"SEQUENCE",paramLabel:i("settings.customization.sequence_param_label"),value:"6",inputDisabled:!1,inputType:"number",allowMultiple:!1},{label:i("settings.customization.delimiter"),description:i("settings.customization.delimiter_description"),name:"DELIMITER",paramLabel:i("settings.customization.delimiter_param_label"),value:"-",inputDisabled:!1,inputType:"text",allowMultiple:!0},{label:i("settings.customization.customer_series"),description:i("settings.customization.customer_series_description"),name:"CUSTOMER_SERIES",paramLabel:"",value:"",inputDisabled:!0,inputType:"text",allowMultiple:!1},{label:i("settings.customization.customer_sequence"),description:i("settings.customization.customer_sequence_description"),name:"CUSTOMER_SEQUENCE",paramLabel:i("settings.customization.customer_sequence_param_label"),value:"6",inputDisabled:!1,inputType:"number",allowMultiple:!1},{label:i("settings.customization.date_format"),description:i("settings.customization.date_format_description"),name:"DATE_FORMAT",paramLabel:i("settings.customization.date_format_param_label"),value:"Y",inputDisabled:!1,inputType:"text",allowMultiple:!0},{label:i("settings.customization.random_sequence"),description:i("settings.customization.random_sequence_description"),name:"RANDOM_SEQUENCE",paramLabel:i("settings.customization.random_sequence_param_label"),value:"6",inputDisabled:!1,inputType:"number",allowMultiple:!1}]),s=F(()=>m.value.filter(function(f){return!u.value.some(function(V){return f.allowMultiple?!1:f.name==V.name})})),_=z(""),n=z(!1),l=z(!1),y=F(()=>{let f="";return u.value.forEach(V=>{let q=`{{${V.name}`;V.value&&(q+=`:${V.value}`),f+=`${q}}}`}),f});bt(u,f=>{U()}),B();async function B(){let f={format:p.selectedCompanySettings[`${o.type}_number_format`]};l.value=!0,(await g.fetchPlaceholders(f)).data.placeholders.forEach(q=>{var O;let J=m.value.find(K=>K.name===q.name);const Q=(O=q.value)!=null?O:"";u.value.push(W(x({},J),{value:Q,id:at.raw()}))}),l.value=!1,U()}function C(f){return u.value.find(V=>V.name===f.name)}function h(f){C(f)&&!f.allowMultiple||(u.value.push(W(x({},f),{id:at.raw()})),U())}function S(f){u.value=u.value.filter(function(V){return f.id!==V.id})}function w(f,V){switch(V.name){case"SERIES":f.length>=6&&(f=f.substring(0,6));break;case"DELIMITER":f.length>=1&&(f=f.substring(0,1));break}setTimeout(()=>{V.value=f,U()},100)}const U=St(()=>{P()},500);async function P(){if(!y.value){_.value="";return}let f={key:o.type,format:y.value};n.value=!0;let V=await o.typeStore.getNextNumber(f);n.value=!1,V.data&&(_.value=V.data.nextNumber)}async function lt(){if(n.value||l.value)return;a.value=!0;let f={settings:{}};return f.settings[o.type+"_number_format"]=y.value,await p.updateCompanySettings({data:f,message:`settings.customization.${o.type}s.${o.type}_settings_updated`}),a.value=!1,!0}return(f,V)=>{const q=d("BaseInput"),J=d("BaseInputGroup"),Q=d("BaseIcon"),O=d("BaseButton"),K=d("BaseDropdownItem"),mt=d("BaseDropdown");return $(),D(L,null,[c("h6",It,b(f.$t(`settings.customization.${v.type}s.${v.type}_number_format`)),1),c("p",xt,b(f.$t(`settings.customization.${v.type}s.${v.type}_number_format_description`)),1),c("div",wt,[c("table",Ct,[Dt,Ut,t(e(ht),{modelValue:u.value,"onUpdate:modelValue":V[1]||(V[1]=I=>u.value=I),class:"divide-y divide-gray-200","item-key":"id",tag:"tbody",handle:".handle",filter:".ignore-element"},{item:r(({element:I})=>[c("tr",Ft,[c("td",kt,[t(Bt)]),c("td",Et,[c("label",Nt,b(I.label),1),c("p",Mt,b(I.description),1)]),c("td",Tt,[t(J,{label:I.paramLabel,class:"lg:col-span-3",required:""},{default:r(()=>[t(q,{modelValue:I.value,"onUpdate:modelValue":[A=>I.value=A,A=>w(A,I)],disabled:I.inputDisabled,type:I.inputType},null,8,["modelValue","onUpdate:modelValue","disabled","type"])]),_:2},1032,["label"])]),c("td",Gt,[t(O,{variant:"white",onClick:Y(A=>S(I),["prevent"])},{left:r(A=>[t(Q,{name:"XIcon",class:G(["!sm:m-0",A.class])},null,8,["class"])]),default:r(()=>[qt]),_:2},1032,["onClick"])])])]),footer:r(()=>[c("tr",null,[c("td",Lt,[t(J,{label:f.$t(`settings.customization.${v.type}s.preview_${v.type}_number`)},{default:r(()=>[t(q,{modelValue:_.value,"onUpdate:modelValue":V[0]||(V[0]=I=>_.value=I),disabled:"",loading:n.value},null,8,["modelValue","loading"])]),_:1},8,["label"])]),c("td",Rt,[t(mt,{"wrapper-class":"flex items-center justify-end mt-5"},{activator:r(()=>[t(O,{variant:"primary-outline"},{left:r(I=>[t(Q,{class:G(I.class),name:"PlusIcon"},null,8,["class"])]),default:r(()=>[k(" "+b(f.$t("settings.customization.add_new_component")),1)]),_:1})]),default:r(()=>[($(!0),D(L,null,$t(e(s),I=>($(),E(K,{key:I.label,onClick:Y(A=>h(I),["prevent"])},{default:r(()=>[k(b(I.label),1)]),_:2},1032,["onClick"]))),128))]),_:1})])])]),_:1},8,["modelValue"])])]),t(O,{loading:a.value,disabled:a.value,variant:"primary",type:"submit",class:"mt-4",onClick:lt},{left:r(I=>[a.value?R("",!0):($(),E(Q,{key:0,class:G(I.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(f.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],64)}}},At={setup(v){const o=pt();return(i,p)=>($(),E(tt,{type:"invoice","type-store":e(o),"default-series":"INV"},null,8,["type-store"]))}},Yt={class:"text-gray-900 text-lg font-medium"},Ot={class:"mt-1 text-sm text-gray-500"},jt={setup(v){const{t:o,tm:i}=j(),p=N(),g=Z(),u=M("utils"),a=T({retrospective_edits:null});u.mergeSettings(a,x({},p.selectedCompanySettings)),F(()=>g.config.retrospective_edits.map(s=>(s.title=o(s.key),s)));async function m(){let s={settings:x({},a)};return await p.updateCompanySettings({data:s,message:"settings.customization.invoices.invoice_settings_updated"}),!0}return(s,_)=>{const n=d("BaseRadio"),l=d("BaseInputGroup");return $(),D(L,null,[c("h6",Yt,b(s.$tc("settings.customization.invoices.retrospective_edits")),1),c("p",Ot,b(s.$t("settings.customization.invoices.retrospective_edits_description")),1),t(l,{required:""},{default:r(()=>[t(n,{id:"allow",modelValue:e(a).retrospective_edits,"onUpdate:modelValue":[_[0]||(_[0]=y=>e(a).retrospective_edits=y),m],label:s.$t("settings.customization.invoices.allow"),size:"sm",name:"filter",value:"allow",class:"mt-2"},null,8,["modelValue","label"]),t(n,{id:"disable_on_invoice_partial_paid",modelValue:e(a).retrospective_edits,"onUpdate:modelValue":[_[1]||(_[1]=y=>e(a).retrospective_edits=y),m],label:s.$t("settings.customization.invoices.disable_on_invoice_partial_paid"),size:"sm",name:"filter",value:"disable_on_invoice_partial_paid",class:"mt-2"},null,8,["modelValue","label"]),t(n,{id:"disable_on_invoice_paid",modelValue:e(a).retrospective_edits,"onUpdate:modelValue":[_[2]||(_[2]=y=>e(a).retrospective_edits=y),m],label:s.$t("settings.customization.invoices.disable_on_invoice_paid"),size:"sm",name:"filter",value:"disable_on_invoice_paid",class:"my-2"},null,8,["modelValue","label"]),t(n,{id:"disable_on_invoice_sent",modelValue:e(a).retrospective_edits,"onUpdate:modelValue":[_[3]||(_[3]=y=>e(a).retrospective_edits=y),m],label:s.$t("settings.customization.invoices.disable_on_invoice_sent"),size:"sm",name:"filter",value:"disable_on_invoice_sent"},null,8,["modelValue","label"])]),_:1})],64)}}},Pt=["onSubmit"],Qt={class:"text-gray-900 text-lg font-medium"},Ht={class:"mt-1 text-sm text-gray-500 mb-2"},Jt={class:"w-full sm:w-1/2 md:w-1/4 lg:w-1/5"},Xt={setup(v){const{t:o}=j(),i=N(),p=M("utils");let g=z(!1);const u=T({invoice_set_due_date_automatically:null,invoice_due_date_days:null});p.mergeSettings(u,x({},i.selectedCompanySettings));const a=F({get:()=>u.invoice_set_due_date_automatically==="YES",set:async n=>{const l=n?"YES":"NO";u.invoice_set_due_date_automatically=l}}),m=F(()=>({dueDateSettings:{invoice_due_date_days:{required:X.withMessage(o("validation.required"),nt(a.value)),numeric:X.withMessage(o("validation.numbers_only"),it)}}})),s=ot(m,{dueDateSettings:u});async function _(){if(s.value.dueDateSettings.$touch(),s.value.dueDateSettings.$invalid)return!1;g.value=!0;let n={settings:x({},u)};return a.value||delete n.settings.invoice_due_date_days,await i.updateCompanySettings({data:n,message:"settings.customization.invoices.invoice_settings_updated"}),g.value=!1,!0}return(n,l)=>{const y=d("BaseSwitchSection"),B=d("BaseInput"),C=d("BaseInputGroup"),h=d("BaseIcon"),S=d("BaseButton");return $(),D("form",{onSubmit:Y(_,["prevent"])},[c("h6",Qt,b(n.$t("settings.customization.invoices.due_date")),1),c("p",Ht,b(n.$t("settings.customization.invoices.due_date_description")),1),t(y,{modelValue:e(a),"onUpdate:modelValue":l[0]||(l[0]=w=>H(a)?a.value=w:null),title:n.$t("settings.customization.invoices.set_due_date_automatically"),description:n.$t("settings.customization.invoices.set_due_date_automatically_description")},null,8,["modelValue","title","description"]),e(a)?($(),E(C,{key:0,label:n.$t("settings.customization.invoices.due_date_days"),error:e(s).dueDateSettings.invoice_due_date_days.$error&&e(s).dueDateSettings.invoice_due_date_days.$errors[0].$message,class:"mt-2 mb-4"},{default:r(()=>[c("div",Jt,[t(B,{modelValue:e(u).invoice_due_date_days,"onUpdate:modelValue":l[1]||(l[1]=w=>e(u).invoice_due_date_days=w),invalid:e(s).dueDateSettings.invoice_due_date_days.$error,type:"number",onInput:l[2]||(l[2]=w=>e(s).dueDateSettings.invoice_due_date_days.$touch())},null,8,["modelValue","invalid"])])]),_:1},8,["label","error"])):R("",!0),t(S,{loading:e(g),disabled:e(g),variant:"primary",type:"submit",class:"mt-4"},{left:r(w=>[e(g)?R("",!0):($(),E(h,{key:0,class:G(w.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(n.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],40,Pt)}}},Kt=["onSubmit"],Wt={class:"text-gray-900 text-lg font-medium"},Zt={class:"mt-1 text-sm text-gray-500 mb-2"},te={setup(v){const o=N(),i=M("utils"),p=z(["customer","customerCustom","invoice","invoiceCustom","company"]),g=z(["billing","customer","customerCustom","invoiceCustom"]),u=z(["shipping","customer","customerCustom","invoiceCustom"]),a=z(["company","invoiceCustom"]);let m=z(!1);const s=T({invoice_mail_body:null,invoice_company_address_format:null,invoice_shipping_address_format:null,invoice_billing_address_format:null});i.mergeSettings(s,x({},o.selectedCompanySettings));async function _(){m.value=!0;let n={settings:x({},s)};return await o.updateCompanySettings({data:n,message:"settings.customization.invoices.invoice_settings_updated"}),m.value=!1,!0}return(n,l)=>{const y=d("BaseCustomInput"),B=d("BaseInputGroup"),C=d("BaseIcon"),h=d("BaseButton");return $(),D("form",{onSubmit:Y(_,["prevent"])},[c("h6",Wt,b(n.$t("settings.customization.invoices.default_formats")),1),c("p",Zt,b(n.$t("settings.customization.invoices.default_formats_description")),1),t(B,{label:n.$t("settings.customization.invoices.default_invoice_email_body"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).invoice_mail_body,"onUpdate:modelValue":l[0]||(l[0]=S=>e(s).invoice_mail_body=S),fields:p.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.invoices.company_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).invoice_company_address_format,"onUpdate:modelValue":l[1]||(l[1]=S=>e(s).invoice_company_address_format=S),fields:a.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.invoices.shipping_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).invoice_shipping_address_format,"onUpdate:modelValue":l[2]||(l[2]=S=>e(s).invoice_shipping_address_format=S),fields:u.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.invoices.billing_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).invoice_billing_address_format,"onUpdate:modelValue":l[3]||(l[3]=S=>e(s).invoice_billing_address_format=S),fields:g.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(h,{loading:e(m),disabled:e(m),variant:"primary",type:"submit",class:"mt-4"},{left:r(S=>[e(m)?R("",!0):($(),E(C,{key:0,class:G(S.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(n.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],40,Kt)}}},ee={class:"divide-y divide-gray-200"},se={setup(v){const o=M("utils"),i=N(),p=T({invoice_email_attachment:null});o.mergeSettings(p,x({},i.selectedCompanySettings));const g=F({get:()=>p.invoice_email_attachment==="YES",set:async u=>{const a=u?"YES":"NO";let m={settings:{invoice_email_attachment:a}};p.invoice_email_attachment=a,await i.updateCompanySettings({data:m,message:"general.setting_updated"})}});return(u,a)=>{const m=d("BaseDivider"),s=d("BaseSwitchSection");return $(),D(L,null,[t(At),t(m,{class:"my-8"}),t(Xt),t(m,{class:"my-8"}),t(jt),t(m,{class:"my-8"}),t(te),t(m,{class:"mt-6 mb-2"}),c("ul",ee,[t(s,{modelValue:e(g),"onUpdate:modelValue":a[0]||(a[0]=_=>H(g)?g.value=_:null),title:u.$t("settings.customization.invoices.invoice_email_attachment"),description:u.$t("settings.customization.invoices.invoice_email_attachment_setting_description")},null,8,["modelValue","title","description"])])],64)}}},ae={setup(v){const o=gt();return(i,p)=>($(),E(tt,{type:"estimate","type-store":e(o),"default-series":"EST"},null,8,["type-store"]))}},ne=["onSubmit"],ie={class:"text-gray-900 text-lg font-medium"},oe={class:"mt-1 text-sm text-gray-500 mb-2"},le={class:"w-full sm:w-1/2 md:w-1/4 lg:w-1/5"},me={setup(v){const{t:o}=j(),i=N(),p=M("utils");let g=z(!1);const u=T({estimate_set_expiry_date_automatically:null,estimate_expiry_date_days:null});p.mergeSettings(u,x({},i.selectedCompanySettings));const a=F({get:()=>u.estimate_set_expiry_date_automatically==="YES",set:async n=>{const l=n?"YES":"NO";u.estimate_set_expiry_date_automatically=l}}),m=F(()=>({expiryDateSettings:{estimate_expiry_date_days:{required:X.withMessage(o("validation.required"),nt(a.value)),numeric:X.withMessage(o("validation.numbers_only"),it)}}})),s=ot(m,{expiryDateSettings:u});async function _(){if(s.value.expiryDateSettings.$touch(),s.value.expiryDateSettings.$invalid)return!1;g.value=!0;let n={settings:x({},u)};return a.value||delete n.settings.estimate_expiry_date_days,await i.updateCompanySettings({data:n,message:"settings.customization.estimates.estimate_settings_updated"}),g.value=!1,!0}return(n,l)=>{const y=d("BaseSwitchSection"),B=d("BaseInput"),C=d("BaseInputGroup"),h=d("BaseIcon"),S=d("BaseButton");return $(),D("form",{onSubmit:Y(_,["prevent"])},[c("h6",ie,b(n.$t("settings.customization.estimates.expiry_date")),1),c("p",oe,b(n.$t("settings.customization.estimates.expiry_date_description")),1),t(y,{modelValue:e(a),"onUpdate:modelValue":l[0]||(l[0]=w=>H(a)?a.value=w:null),title:n.$t("settings.customization.estimates.set_expiry_date_automatically"),description:n.$t("settings.customization.estimates.set_expiry_date_automatically_description")},null,8,["modelValue","title","description"]),e(a)?($(),E(C,{key:0,label:n.$t("settings.customization.estimates.expiry_date_days"),error:e(s).expiryDateSettings.estimate_expiry_date_days.$error&&e(s).expiryDateSettings.estimate_expiry_date_days.$errors[0].$message,class:"mt-2 mb-4"},{default:r(()=>[c("div",le,[t(B,{modelValue:e(u).estimate_expiry_date_days,"onUpdate:modelValue":l[1]||(l[1]=w=>e(u).estimate_expiry_date_days=w),invalid:e(s).expiryDateSettings.estimate_expiry_date_days.$error,type:"number",onInput:l[2]||(l[2]=w=>e(s).expiryDateSettings.estimate_expiry_date_days.$touch())},null,8,["modelValue","invalid"])])]),_:1},8,["label","error"])):R("",!0),t(S,{loading:e(g),disabled:e(g),variant:"primary",type:"submit",class:"mt-4"},{left:r(w=>[e(g)?R("",!0):($(),E(h,{key:0,class:G(w.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(n.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],40,ne)}}},ue=["onSubmit"],re={class:"text-gray-900 text-lg font-medium"},de={class:"mt-1 text-sm text-gray-500 mb-2"},ce={setup(v){const o=N(),i=M("utils"),p=z(["customer","customerCustom","estimate","estimateCustom","company"]),g=z(["billing","customer","customerCustom","estimateCustom"]),u=z(["shipping","customer","customerCustom","estimateCustom"]),a=z(["company","estimateCustom"]);let m=z(!1);const s=T({estimate_mail_body:null,estimate_company_address_format:null,estimate_shipping_address_format:null,estimate_billing_address_format:null});i.mergeSettings(s,x({},o.selectedCompanySettings));async function _(){m.value=!0;let n={settings:x({},s)};return await o.updateCompanySettings({data:n,message:"settings.customization.estimates.estimate_settings_updated"}),m.value=!1,!0}return(n,l)=>{const y=d("BaseCustomInput"),B=d("BaseInputGroup"),C=d("BaseIcon"),h=d("BaseButton");return $(),D("form",{onSubmit:Y(_,["prevent"])},[c("h6",re,b(n.$t("settings.customization.estimates.default_formats")),1),c("p",de,b(n.$t("settings.customization.estimates.default_formats_description")),1),t(B,{label:n.$t("settings.customization.estimates.default_estimate_email_body"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).estimate_mail_body,"onUpdate:modelValue":l[0]||(l[0]=S=>e(s).estimate_mail_body=S),fields:p.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.estimates.company_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).estimate_company_address_format,"onUpdate:modelValue":l[1]||(l[1]=S=>e(s).estimate_company_address_format=S),fields:a.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.estimates.shipping_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).estimate_shipping_address_format,"onUpdate:modelValue":l[2]||(l[2]=S=>e(s).estimate_shipping_address_format=S),fields:u.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(B,{label:n.$t("settings.customization.estimates.billing_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(y,{modelValue:e(s).estimate_billing_address_format,"onUpdate:modelValue":l[3]||(l[3]=S=>e(s).estimate_billing_address_format=S),fields:g.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(h,{loading:e(m),disabled:e(m),variant:"primary",type:"submit",class:"mt-4"},{left:r(S=>[e(m)?R("",!0):($(),E(C,{key:0,class:G(S.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(n.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],40,ue)}}},_e={class:"text-gray-900 text-lg font-medium"},pe={class:"mt-1 text-sm text-gray-500"},ge={setup(v){const{t:o,tm:i}=j(),p=N(),g=Z(),u=M("utils"),a=T({estimate_convert_action:null});u.mergeSettings(a,x({},p.selectedCompanySettings)),F(()=>g.config.estimate_convert_action.map(s=>(s.title=o(s.key),s)));async function m(){let s={settings:x({},a)};return await p.updateCompanySettings({data:s,message:"settings.customization.estimates.estimate_settings_updated"}),!0}return(s,_)=>{const n=d("BaseRadio"),l=d("BaseInputGroup");return $(),D(L,null,[c("h6",_e,b(s.$tc("settings.customization.estimates.convert_estimate_options")),1),c("p",pe,b(s.$t("settings.customization.estimates.convert_estimate_description")),1),t(l,{required:""},{default:r(()=>[t(n,{id:"no_action",modelValue:e(a).estimate_convert_action,"onUpdate:modelValue":[_[0]||(_[0]=y=>e(a).estimate_convert_action=y),m],label:s.$t("settings.customization.estimates.no_action"),size:"sm",name:"filter",value:"no_action",class:"mt-2"},null,8,["modelValue","label"]),t(n,{id:"delete_estimate",modelValue:e(a).estimate_convert_action,"onUpdate:modelValue":[_[1]||(_[1]=y=>e(a).estimate_convert_action=y),m],label:s.$t("settings.customization.estimates.delete_estimate"),size:"sm",name:"filter",value:"delete_estimate",class:"my-2"},null,8,["modelValue","label"]),t(n,{id:"mark_estimate_as_accepted",modelValue:e(a).estimate_convert_action,"onUpdate:modelValue":[_[2]||(_[2]=y=>e(a).estimate_convert_action=y),m],label:s.$t("settings.customization.estimates.mark_estimate_as_accepted"),size:"sm",name:"filter",value:"mark_estimate_as_accepted"},null,8,["modelValue","label"])]),_:1})],64)}}},ye={class:"divide-y divide-gray-200"},fe={setup(v){const o=M("utils"),i=N(),p=T({estimate_email_attachment:null});o.mergeSettings(p,x({},i.selectedCompanySettings));const g=F({get:()=>p.estimate_email_attachment==="YES",set:async u=>{const a=u?"YES":"NO";let m={settings:{estimate_email_attachment:a}};p.estimate_email_attachment=a,await i.updateCompanySettings({data:m,message:"general.setting_updated"})}});return(u,a)=>{const m=d("BaseDivider"),s=d("BaseSwitchSection");return $(),D(L,null,[t(ae),t(m,{class:"my-8"}),t(me),t(m,{class:"my-8"}),t(ge),t(m,{class:"my-8"}),t(ce),t(m,{class:"mt-6 mb-2"}),c("ul",ye,[t(s,{modelValue:e(g),"onUpdate:modelValue":a[0]||(a[0]=_=>H(g)?g.value=_:null),title:u.$t("settings.customization.estimates.estimate_email_attachment"),description:u.$t("settings.customization.estimates.estimate_email_attachment_setting_description")},null,8,["modelValue","title","description"])])],64)}}},ve={setup(v){const o=zt();return(i,p)=>($(),E(tt,{type:"payment","type-store":e(o),"default-series":"PAY"},null,8,["type-store"]))}},be=["onSubmit"],Se={class:"text-gray-900 text-lg font-medium"},$e={class:"mt-1 text-sm text-gray-500 mb-2"},Be={setup(v){const o=N(),i=M("utils"),p=z(["customer","customerCustom","company","payment","paymentCustom"]),g=z(["billing","customer","customerCustom","paymentCustom"]),u=z(["company","paymentCustom"]);let a=z(!1);const m=T({payment_mail_body:null,payment_company_address_format:null,payment_from_customer_address_format:null});i.mergeSettings(m,x({},o.selectedCompanySettings));async function s(){a.value=!0;let _={settings:x({},m)};return await o.updateCompanySettings({data:_,message:"settings.customization.payments.payment_settings_updated"}),a.value=!1,!0}return(_,n)=>{const l=d("BaseCustomInput"),y=d("BaseInputGroup"),B=d("BaseIcon"),C=d("BaseButton");return $(),D("form",{onSubmit:Y(s,["prevent"])},[c("h6",Se,b(_.$t("settings.customization.payments.default_formats")),1),c("p",$e,b(_.$t("settings.customization.payments.default_formats_description")),1),t(y,{label:_.$t("settings.customization.payments.default_payment_email_body"),class:"mt-6 mb-4"},{default:r(()=>[t(l,{modelValue:e(m).payment_mail_body,"onUpdate:modelValue":n[0]||(n[0]=h=>e(m).payment_mail_body=h),fields:p.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(y,{label:_.$t("settings.customization.payments.company_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(l,{modelValue:e(m).payment_company_address_format,"onUpdate:modelValue":n[1]||(n[1]=h=>e(m).payment_company_address_format=h),fields:u.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(y,{label:_.$t("settings.customization.payments.from_customer_address_format"),class:"mt-6 mb-4"},{default:r(()=>[t(l,{modelValue:e(m).payment_from_customer_address_format,"onUpdate:modelValue":n[2]||(n[2]=h=>e(m).payment_from_customer_address_format=h),fields:g.value},null,8,["modelValue","fields"])]),_:1},8,["label"]),t(C,{loading:e(a),disabled:e(a),variant:"primary",type:"submit",class:"mt-4"},{left:r(h=>[e(a)?R("",!0):($(),E(B,{key:0,class:G(h.class),name:"SaveIcon"},null,8,["class"]))]),default:r(()=>[k(" "+b(_.$t("settings.customization.save")),1)]),_:1},8,["loading","disabled"])],40,be)}}},he={class:"divide-y divide-gray-200"},ze={setup(v){const o=M("utils"),i=N(),p=T({payment_email_attachment:null});o.mergeSettings(p,x({},i.selectedCompanySettings));const g=F({get:()=>p.payment_email_attachment==="YES",set:async u=>{const a=u?"YES":"NO";let m={settings:{payment_email_attachment:a}};p.payment_email_attachment=a,await i.updateCompanySettings({data:m,message:"general.setting_updated"})}});return(u,a)=>{const m=d("BaseDivider"),s=d("BaseSwitchSection");return $(),D(L,null,[t(ve),t(m,{class:"my-8"}),t(Be),t(m,{class:"mt-6 mb-2"}),c("ul",he,[t(s,{modelValue:e(g),"onUpdate:modelValue":a[0]||(a[0]=_=>H(g)?g.value=_:null),title:u.$t("settings.customization.payments.payment_email_attachment"),description:u.$t("settings.customization.payments.payment_email_attachment_setting_description")},null,8,["modelValue","title","description"])])],64)}}},Ve={class:"flex flex-wrap justify-end mt-2 lg:flex-nowrap"},Ie={class:"inline-block"},xe={setup(v){const{t:o}=j(),i=z(null),p=yt(),g=ft(),u=vt(),a=F(()=>[{key:"name",label:o("settings.customization.items.unit_name"),thClass:"extra",tdClass:"font-medium text-gray-900"},{key:"actions",label:"",tdClass:"text-right text-sm font-medium",sortable:!1}]);async function m({page:l,filter:y,sort:B}){let C={orderByField:B.fieldName||"created_at",orderBy:B.order||"desc",page:l},h=await p.fetchItemUnits(C);return{data:h.data.data,pagination:{totalPages:h.data.meta.last_page,currentPage:l,totalCount:h.data.meta.total,limit:5}}}async function s(){g.openModal({title:o("settings.customization.items.add_item_unit"),componentName:"ItemUnitModal",refreshData:i.value.refresh,size:"sm"})}async function _(l){p.fetchItemUnit(l.data.id),g.openModal({title:o("settings.customization.items.edit_item_unit"),componentName:"ItemUnitModal",id:l.data.id,data:l.data,refreshData:i.value&&i.value.refresh})}function n(l){u.openDialog({title:o("general.are_you_sure"),message:o("settings.customization.items.item_unit_confirm_delete"),yesLabel:o("general.yes"),noLabel:o("general.no"),variant:"danger",hideNoButton:!1,size:"lg"}).then(async y=>{y&&(await p.deleteItemUnit(l.data.id),i.value&&i.value.refresh())})}return(l,y)=>{const B=d("BaseIcon"),C=d("BaseButton"),h=d("BaseDropdownItem"),S=d("BaseDropdown"),w=d("BaseTable");return $(),D(L,null,[t(Vt),c("div",Ve,[t(C,{variant:"primary-outline",onClick:s},{left:r(U=>[t(B,{class:G(U.class),name:"PlusIcon"},null,8,["class"])]),default:r(()=>[k(" "+b(l.$t("settings.customization.items.add_item_unit")),1)]),_:1})]),t(w,{ref:(U,P)=>{P.table=U,i.value=U},class:"mt-10",data:m,columns:e(a)},{"cell-actions":r(({row:U})=>[t(S,null,{activator:r(()=>[c("div",Ie,[t(B,{name:"DotsHorizontalIcon",class:"text-gray-500"})])]),default:r(()=>[t(h,{onClick:P=>_(U)},{default:r(()=>[t(B,{name:"PencilIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),k(" "+b(l.$t("general.edit")),1)]),_:2},1032,["onClick"]),t(h,{onClick:P=>n(U)},{default:r(()=>[t(B,{name:"TrashIcon",class:"w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"}),k(" "+b(l.$t("general.delete")),1)]),_:2},1032,["onClick"])]),_:2},1024)]),_:1},8,["columns"])],64)}}},we={class:"relative"},Ne={setup(v){return(o,i)=>{const p=d("BaseTab"),g=d("BaseTabGroup"),u=d("BaseCard");return $(),D("div",we,[t(u,{"container-class":"px-4 py-5 sm:px-8 sm:py-2"},{default:r(()=>[t(g,null,{default:r(()=>[t(p,{"tab-panel-container":"py-4 mt-px",title:o.$t("settings.customization.invoices.title")},{default:r(()=>[t(se)]),_:1},8,["title"]),t(p,{"tab-panel-container":"py-4 mt-px",title:o.$t("settings.customization.estimates.title")},{default:r(()=>[t(fe)]),_:1},8,["title"]),t(p,{"tab-panel-container":"py-4 mt-px",title:o.$t("settings.customization.payments.title")},{default:r(()=>[t(ze)]),_:1},8,["title"]),t(p,{"tab-panel-container":"py-4 mt-px",title:o.$t("settings.customization.items.title")},{default:r(()=>[t(xe)]),_:1},8,["title"])]),_:1})]),_:1})])}}};export{Ne as default}; diff --git a/crater/public/build/assets/Dashboard.db3b8908.js b/crater/public/build/assets/Dashboard.db3b8908.js new file mode 100644 index 0000000..a3e93c2 --- /dev/null +++ b/crater/public/build/assets/Dashboard.db3b8908.js @@ -0,0 +1 @@ +import{D as I,_ as L,a as M}from"./EstimateIcon.7f89fb19.js";import{o as m,e as v,m as $,h as c,a as V,r as i,l as h,w as s,f as t,g as F,t as u,aj as T,ah as w,u as n,i as _,J as z,G as A,k as D}from"./vendor.d12b5734.js";import{u as C}from"./global.dc565c4e.js";import{h as Z}from"./auth.c88ceb4c.js";import{_ as k}from"./main.465728e1.js";import S from"./BaseTable.ec8995dc.js";const q=c("circle",{cx:"25",cy:"25",r:"25",fill:"#EAF1FB"},null,-1),N=c("path",{d:"M17.8 17.8C17.1635 17.8 16.5531 18.0529 16.103 18.503C15.6529 18.9531 15.4 19.5635 15.4 20.2V21.4H34.6V20.2C34.6 19.5635 34.3472 18.9531 33.8971 18.503C33.447 18.0529 32.8365 17.8 32.2 17.8H17.8Z",fill:"currentColor"},null,-1),G=c("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M34.6 23.8H15.4V29.8C15.4 30.4366 15.6529 31.047 16.103 31.4971C16.5531 31.9472 17.1635 32.2 17.8 32.2H32.2C32.8365 32.2 33.447 31.9472 33.8971 31.4971C34.3472 31.047 34.6 30.4366 34.6 29.8V23.8ZM17.8 28.6C17.8 28.2818 17.9265 27.9766 18.1515 27.7515C18.3765 27.5265 18.6818 27.4 19 27.4H20.2C20.5183 27.4 20.8235 27.5265 21.0486 27.7515C21.2736 27.9766 21.4 28.2818 21.4 28.6C21.4 28.9183 21.2736 29.2235 21.0486 29.4486C20.8235 29.6736 20.5183 29.8 20.2 29.8H19C18.6818 29.8 18.3765 29.6736 18.1515 29.4486C17.9265 29.2235 17.8 28.9183 17.8 28.6ZM23.8 27.4C23.4818 27.4 23.1765 27.5265 22.9515 27.7515C22.7265 27.9766 22.6 28.2818 22.6 28.6C22.6 28.9183 22.7265 29.2235 22.9515 29.4486C23.1765 29.6736 23.4818 29.8 23.8 29.8H25C25.3183 29.8 25.6235 29.6736 25.8486 29.4486C26.0736 29.2235 26.2 28.9183 26.2 28.6C26.2 28.2818 26.0736 27.9766 25.8486 27.7515C25.6235 27.5265 25.3183 27.4 25 27.4H23.8Z",fill:"currentColor"},null,-1),O=[q,N,G],J={props:{colorClass:{type:String,default:"text-primary-500"}},setup(r){return(a,o)=>(m(),v("svg",{width:"50",height:"50",viewBox:"0 0 50 50",fill:"none",xmlns:"http://www.w3.org/2000/svg",class:$(r.colorClass)},O,2))}},{defineStore:R}=window.pinia,P=R({id:"dashboard",state:()=>({recentInvoices:[],recentEstimates:[],invoiceCount:0,estimateCount:0,paymentCount:0,totalDueAmount:[],isDashboardDataLoaded:!1}),actions:{loadData(r){const a=C();return new Promise((o,d)=>{V.get(`/api/v1/${a.companySlug}/customer/dashboard`,{data:r}).then(e=>{this.totalDueAmount=e.data.due_amount,this.estimateCount=e.data.estimate_count,this.invoiceCount=e.data.invoice_count,this.paymentCount=e.data.payment_count,this.recentInvoices=e.data.recentInvoices,this.recentEstimates=e.data.recentEstimates,a.getDashboardDataLoaded=!0,o(e)}).catch(e=>{Z(e),d(e)})})}}}),K={},Q={class:"flex items-center"};function U(r,a){const o=i("BaseContentPlaceholdersText"),d=i("BaseContentPlaceholdersBox"),e=i("BaseContentPlaceholders");return m(),h(e,{rounded:!0,class:"relative flex justify-between w-full p-3 bg-white rounded shadow lg:col-span-3 xl:p-4"},{default:s(()=>[c("div",null,[t(o,{class:"h-5 -mb-1 w-14 xl:mb-6 xl:h-7",lines:1}),t(o,{class:"h-3 w-28 xl:h-4",lines:1})]),c("div",Q,[t(d,{circle:!0,class:"w-10 h-10 xl:w-12 xl:h-12"})])]),_:1})}var W=k(K,[["render",U]]);const X={},Y={class:"flex items-center"};function ee(r,a){const o=i("BaseContentPlaceholdersText"),d=i("BaseContentPlaceholdersBox"),e=i("BaseContentPlaceholders");return m(),h(e,{rounded:!0,class:"relative flex justify-between w-full p-3 bg-white rounded shadow lg:col-span-2 xl:p-4"},{default:s(()=>[c("div",null,[t(o,{class:"w-12 h-5 -mb-1 xl:mb-6 xl:h-7",lines:1}),t(o,{class:"w-20 h-3 xl:h-4",lines:1})]),c("div",Y,[t(d,{circle:!0,class:"w-10 h-10 xl:w-12 xl:h-12"})])]),_:1})}var te=k(X,[["render",ee]]);const ae={class:"text-xl font-semibold leading-tight text-black xl:text-3xl"},se={class:"block mt-1 text-sm leading-tight text-gray-500 xl:text-lg"},oe={class:"flex items-center"},f={props:{iconComponent:{type:Object,required:!0},loading:{type:Boolean,default:!1},route:{type:Object,required:!0},label:{type:String,required:!0},large:{type:Boolean,default:!1}},setup(r){return(a,o)=>{const d=i("router-link");return r.loading?r.large?(m(),h(W,{key:1})):(m(),h(te,{key:2})):(m(),h(d,{key:0,class:$(["relative flex justify-between p-3 bg-white rounded shadow hover:bg-gray-50 xl:p-4 lg:col-span-2",{"lg:!col-span-3":r.large}]),to:r.route},{default:s(()=>[c("div",null,[c("span",ae,[F(a.$slots,"default")]),c("span",se,u(r.label),1)]),c("div",oe,[(m(),h(T(r.iconComponent),{class:"w-10 h-10 xl:w-12 xl:h-12"}))])]),_:3},8,["class","to"]))}}},ne={class:"grid gap-6 sm:grid-cols-2 lg:grid-cols-9 xl:gap-8"},le={setup(r){w("utils");const a=C(),o=P();return o.loadData(),(d,e)=>{const g=i("BaseFormatMoney");return m(),v("div",ne,[t(f,{"icon-component":I,loading:!n(a).getDashboardDataLoaded,route:{name:"invoices.dashboard"},large:!0,label:d.$t("dashboard.cards.due_amount")},{default:s(()=>[t(g,{amount:n(o).totalDueAmount,currency:n(a).currency},null,8,["amount","currency"])]),_:1},8,["loading","route","label"]),t(f,{"icon-component":L,loading:!n(a).getDashboardDataLoaded,route:{name:"invoices.dashboard"},label:d.$t("dashboard.cards.invoices")},{default:s(()=>[_(u(n(o).invoiceCount),1)]),_:1},8,["loading","route","label"]),t(f,{"icon-component":M,loading:!n(a).getDashboardDataLoaded,route:{name:"estimates.dashboard"},label:d.$t("dashboard.cards.estimates")},{default:s(()=>[_(u(n(o).estimateCount),1)]),_:1},8,["loading","route","label"]),t(f,{"icon-component":J,loading:!n(a).getDashboardDataLoaded,route:{name:"payments.dashboard"},label:d.$t("dashboard.cards.payments")},{default:s(()=>[_(u(n(o).paymentCount),1)]),_:1},8,["loading","route","label"])])}}},ce={class:"grid grid-cols-1 gap-6 mt-10 xl:grid-cols-2"},re={class:"due-invoices"},de={class:"relative z-10 flex items-center justify-between mb-3"},ie={class:"mb-0 text-xl font-semibold leading-normal"},ue={class:"recent-estimates"},me={class:"relative z-10 flex items-center justify-between mb-3"},_e={class:"mb-0 text-xl font-semibold leading-normal"},he={setup(r){const a=C(),o=P(),{tm:d,t:e}=z();w("utils"),A();const g=D(()=>[{key:"formattedDueDate",label:e("dashboard.recent_invoices_card.due_on")},{key:"invoice_number",label:e("invoices.number")},{key:"paid_status",label:e("invoices.status")},{key:"due_amount",label:e("dashboard.recent_invoices_card.amount_due")}]),j=D(()=>[{key:"formattedEstimateDate",label:e("dashboard.recent_estimate_card.date")},{key:"estimate_number",label:e("estimates.number")},{key:"status",label:e("estimates.status")},{key:"total",label:e("dashboard.recent_estimate_card.amount_due")}]);return(b,p)=>{const x=i("BaseButton"),y=i("router-link"),E=i("BasePaidStatusBadge"),B=i("BaseFormatMoney"),H=i("BaseEstimateStatusBadge");return m(),v("div",ce,[c("div",re,[c("div",de,[c("h6",ie,u(b.$t("dashboard.recent_invoices_card.title")),1),t(x,{size:"sm",variant:"primary-outline",onClick:p[0]||(p[0]=l=>b.$router.push({name:"invoices.dashboard"}))},{default:s(()=>[_(u(b.$t("dashboard.recent_invoices_card.view_all")),1)]),_:1})]),t(S,{data:n(o).recentInvoices,columns:n(g),loading:!n(a).getDashboardDataLoaded},{"cell-invoice_number":s(({row:l})=>[t(y,{to:{path:`/${n(a).companySlug}/customer/invoices/${l.data.id}/view`},class:"font-medium text-primary-500"},{default:s(()=>[_(u(l.data.invoice_number),1)]),_:2},1032,["to"])]),"cell-paid_status":s(({row:l})=>[t(E,{status:l.data.paid_status},{default:s(()=>[_(u(l.data.paid_status),1)]),_:2},1032,["status"])]),"cell-due_amount":s(({row:l})=>[t(B,{amount:l.data.due_amount,currency:n(a).currency},null,8,["amount","currency"])]),_:1},8,["data","columns","loading"])]),c("div",ue,[c("div",me,[c("h6",_e,u(b.$t("dashboard.recent_estimate_card.title")),1),t(x,{variant:"primary-outline",size:"sm",onClick:p[1]||(p[1]=l=>b.$router.push({name:"estimates.dashboard"}))},{default:s(()=>[_(u(b.$t("dashboard.recent_estimate_card.view_all")),1)]),_:1})]),t(S,{data:n(o).recentEstimates,columns:n(j),loading:!n(a).getDashboardDataLoaded},{"cell-estimate_number":s(({row:l})=>[t(y,{to:{path:`/${n(a).companySlug}/customer/estimates/${l.data.id}/view`},class:"font-medium text-primary-500"},{default:s(()=>[_(u(l.data.estimate_number),1)]),_:2},1032,["to"])]),"cell-status":s(({row:l})=>[t(H,{status:l.data.status,class:"px-3 py-1"},{default:s(()=>[_(u(l.data.status),1)]),_:2},1032,["status"])]),"cell-total":s(({row:l})=>[t(B,{amount:l.data.total,currency:n(a).currency},null,8,["amount","currency"])]),_:1},8,["data","columns","loading"])])])}}},xe={setup(r){return(a,o)=>{const d=i("BasePage");return m(),h(d,null,{default:s(()=>[t(le),t(he)]),_:1})}}};export{xe as default}; diff --git a/crater/public/build/assets/Dashboard.f55bd37e.js b/crater/public/build/assets/Dashboard.f55bd37e.js new file mode 100644 index 0000000..89c3dfb --- /dev/null +++ b/crater/public/build/assets/Dashboard.f55bd37e.js @@ -0,0 +1 @@ +import{D as L,_ as F,a as R}from"./EstimateIcon.7f89fb19.js";import{o as c,e as C,m as j,h as t,r,l as p,w as i,f as a,g as W,t as _,aj as z,a as q,d as H,ah as V,u as e,j as v,i as y,B as D,C as U,J as Z,k as M,V as N,G,aN as J,D as Y}from"./vendor.d12b5734.js";import{_ as T,h as K,b as O,e as E,g as h}from"./main.465728e1.js";import{_ as Q}from"./LineChart.8ef63104.js";import{_ as X}from"./InvoiceIndexDropdown.c4bcaa08.js";import{_ as tt}from"./EstimateIndexDropdown.8917d9cc.js";const et=t("circle",{cx:"25",cy:"25",r:"25",fill:"#EAF1FB"},null,-1),at=t("path",{d:"M28.2656 23.0547C27.3021 24.0182 26.1302 24.5 24.75 24.5C23.3698 24.5 22.1849 24.0182 21.1953 23.0547C20.2318 22.0651 19.75 20.8802 19.75 19.5C19.75 18.1198 20.2318 16.9479 21.1953 15.9844C22.1849 14.9948 23.3698 14.5 24.75 14.5C26.1302 14.5 27.3021 14.9948 28.2656 15.9844C29.2552 16.9479 29.75 18.1198 29.75 19.5C29.75 20.8802 29.2552 22.0651 28.2656 23.0547ZM28.2656 25.75C29.6979 25.75 30.9219 26.2708 31.9375 27.3125C32.9792 28.3281 33.5 29.5521 33.5 30.9844V32.625C33.5 33.1458 33.3177 33.5885 32.9531 33.9531C32.5885 34.3177 32.1458 34.5 31.625 34.5H17.875C17.3542 34.5 16.9115 34.3177 16.5469 33.9531C16.1823 33.5885 16 33.1458 16 32.625V30.9844C16 29.5521 16.5078 28.3281 17.5234 27.3125C18.5651 26.2708 19.8021 25.75 21.2344 25.75H21.8984C22.8099 26.1667 23.7604 26.375 24.75 26.375C25.7396 26.375 26.6901 26.1667 27.6016 25.75H28.2656Z",fill:"currentColor"},null,-1),st=[et,at],ot={props:{colorClass:{type:String,default:"text-primary-500"}},setup(d){return(o,s)=>(c(),C("svg",{width:"50",height:"50",viewBox:"0 0 50 50",class:j(d.colorClass),fill:"none",xmlns:"http://www.w3.org/2000/svg"},st,2))}},lt={},nt={class:"flex items-center"};function ct(d,o){const s=r("BaseContentPlaceholdersText"),n=r("BaseContentPlaceholdersBox"),u=r("BaseContentPlaceholders");return c(),p(u,{rounded:!0,class:"relative flex justify-between w-full p-3 bg-white rounded shadow lg:col-span-3 xl:p-4"},{default:i(()=>[t("div",null,[a(s,{class:"h-5 -mb-1 w-14 xl:mb-6 xl:h-7",lines:1}),a(s,{class:"h-3 w-28 xl:h-4",lines:1})]),t("div",nt,[a(n,{circle:!0,class:"w-10 h-10 xl:w-12 xl:h-12"})])]),_:1})}var rt=T(lt,[["render",ct]]);const it={},dt={class:"flex items-center"};function ut(d,o){const s=r("BaseContentPlaceholdersText"),n=r("BaseContentPlaceholdersBox"),u=r("BaseContentPlaceholders");return c(),p(u,{rounded:!0,class:"relative flex justify-between w-full p-3 bg-white rounded shadow lg:col-span-2 xl:p-4"},{default:i(()=>[t("div",null,[a(s,{class:"w-12 h-5 -mb-1 xl:mb-6 xl:h-7",lines:1}),a(s,{class:"w-20 h-3 xl:h-4",lines:1})]),t("div",dt,[a(n,{circle:!0,class:"w-10 h-10 xl:w-12 xl:h-12"})])]),_:1})}var mt=T(it,[["render",ut]]);const _t={class:"text-xl font-semibold leading-tight text-black xl:text-3xl"},ht={class:"block mt-1 text-sm leading-tight text-gray-500 xl:text-lg"},pt={class:"flex items-center"},B={props:{iconComponent:{type:Object,required:!0},loading:{type:Boolean,default:!1},route:{type:String,required:!0},label:{type:String,required:!0},large:{type:Boolean,default:!1}},setup(d){return(o,s)=>{const n=r("router-link");return d.loading?d.large?(c(),p(rt,{key:1})):(c(),p(mt,{key:2})):(c(),p(n,{key:0,class:j(["relative flex justify-between p-3 bg-white rounded shadow hover:bg-gray-50 xl:p-4 lg:col-span-2",{"lg:!col-span-3":d.large}]),to:d.route},{default:i(()=>[t("div",null,[t("span",_t,[W(o.$slots,"default")]),t("span",ht,_(d.label),1)]),t("div",pt,[(c(),p(z(d.iconComponent),{class:"w-10 h-10 xl:w-12 xl:h-12"}))])]),_:3},8,["class","to"]))}}},S=(d=!1)=>(d?window.pinia.defineStore:H)({id:"dashboard",state:()=>({stats:{totalAmountDue:0,totalCustomerCount:0,totalInvoiceCount:0,totalEstimateCount:0},chartData:{months:[],invoiceTotals:[],expenseTotals:[],receiptTotals:[],netIncomeTotals:[]},totalSales:null,totalReceipts:null,totalExpenses:null,totalNetIncome:null,recentDueInvoices:[],recentEstimates:[],isDashboardDataLoaded:!1}),actions:{loadData(s){return new Promise((n,u)=>{q.get("/api/v1/dashboard",{params:s}).then(l=>{this.stats.totalAmountDue=l.data.total_amount_due,this.stats.totalCustomerCount=l.data.total_customer_count,this.stats.totalInvoiceCount=l.data.total_invoice_count,this.stats.totalEstimateCount=l.data.total_estimate_count,this.chartData&&l.data.chart_data&&(this.chartData.months=l.data.chart_data.months,this.chartData.invoiceTotals=l.data.chart_data.invoice_totals,this.chartData.expenseTotals=l.data.chart_data.expense_totals,this.chartData.receiptTotals=l.data.chart_data.receipt_totals,this.chartData.netIncomeTotals=l.data.chart_data.net_income_totals),this.totalSales=l.data.total_sales,this.totalReceipts=l.data.total_receipts,this.totalExpenses=l.data.total_expenses,this.totalNetIncome=l.data.total_net_income,this.recentDueInvoices=l.data.recent_due_invoices,this.recentEstimates=l.data.recent_estimates,this.isDashboardDataLoaded=!0,n(l)}).catch(l=>{K(l),u(l)})})}}})(),bt={class:"grid gap-6 sm:grid-cols-2 lg:grid-cols-9 xl:gap-8"},xt={setup(d){V("utils");const o=S(),s=O(),n=E();return(u,l)=>{const f=r("BaseFormatMoney");return c(),C("div",bt,[e(n).hasAbilities(e(h).VIEW_INVOICE)?(c(),p(B,{key:0,"icon-component":L,loading:!e(o).isDashboardDataLoaded,route:"/admin/invoices",large:!0,label:u.$t("dashboard.cards.due_amount")},{default:i(()=>[a(f,{amount:e(o).stats.totalAmountDue,currency:e(s).selectedCompanyCurrency},null,8,["amount","currency"])]),_:1},8,["loading","label"])):v("",!0),e(n).hasAbilities(e(h).VIEW_CUSTOMER)?(c(),p(B,{key:1,"icon-component":ot,loading:!e(o).isDashboardDataLoaded,route:"/admin/customers",label:u.$t("dashboard.cards.customers")},{default:i(()=>[y(_(e(o).stats.totalCustomerCount),1)]),_:1},8,["loading","label"])):v("",!0),e(n).hasAbilities(e(h).VIEW_INVOICE)?(c(),p(B,{key:2,"icon-component":F,loading:!e(o).isDashboardDataLoaded,route:"/admin/invoices",label:u.$t("dashboard.cards.invoices")},{default:i(()=>[y(_(e(o).stats.totalInvoiceCount),1)]),_:1},8,["loading","label"])):v("",!0),e(n).hasAbilities(e(h).VIEW_ESTIMATE)?(c(),p(B,{key:3,"icon-component":R,loading:!e(o).isDashboardDataLoaded,route:"/admin/estimates",label:u.$t("dashboard.cards.estimates")},{default:i(()=>[y(_(e(o).stats.totalEstimateCount),1)]),_:1},8,["loading","label"])):v("",!0)])}}},ft={},gt={class:"grid grid-cols-1 col-span-10 px-4 py-5 lg:col-span-7 xl:col-span-8 sm:p-8"},yt={class:"flex items-center justify-between mb-2 xl:mb-4"},Ct={class:"grid grid-cols-3 col-span-10 text-center border-t border-l border-gray-200 border-solid lg:border-t-0 lg:text-right lg:col-span-3 xl:col-span-2 lg:grid-cols-1"},vt={class:"flex flex-col items-center justify-center p-6 lg:justify-end lg:items-end"},wt={class:"flex flex-col items-center justify-center p-6 lg:justify-end lg:items-end"},$t={class:"flex flex-col items-center justify-center p-6 lg:justify-end lg:items-end"},Dt={class:"flex flex-col items-center justify-center col-span-3 p-6 border-t border-gray-200 border-solid lg:justify-end lg:items-end lg:col-span-1"};function Et(d,o){const s=r("BaseContentPlaceholdersText"),n=r("BaseContentPlaceholdersBox"),u=r("BaseContentPlaceholders");return c(),p(u,{class:"grid grid-cols-10 mt-8 bg-white rounded shadow"},{default:i(()=>[t("div",gt,[t("div",yt,[a(s,{class:"h-10 w-36",lines:1}),a(s,{class:"h-10 w-36 !mt-0",lines:1})]),a(n,{class:"h-80 xl:h-72 sm:w-full"})]),t("div",Ct,[t("div",vt,[a(s,{class:"h-3 w-14 xl:h-4",lines:1}),a(s,{class:"w-20 h-5 xl:h-6",lines:1})]),t("div",wt,[a(s,{class:"h-3 w-14 xl:h-4",lines:1}),a(s,{class:"w-20 h-5 xl:h-6",lines:1})]),t("div",$t,[a(s,{class:"h-3 w-14 xl:h-4",lines:1}),a(s,{class:"w-20 h-5 xl:h-6",lines:1})]),t("div",Dt,[a(s,{class:"h-3 w-14 xl:h-4",lines:1}),a(s,{class:"w-20 h-5 xl:h-6",lines:1})])])]),_:1})}var Bt=T(ft,[["render",Et]]);const It={key:0,class:"grid grid-cols-10 mt-8 bg-white rounded shadow"},Tt={class:"grid grid-cols-1 col-span-10 px-4 py-5 lg:col-span-7 xl:col-span-8 sm:p-6"},St={class:"flex justify-between mt-1 mb-4 flex-col md:flex-row"},kt={class:"flex items-center sw-section-title h-10"},At={class:"w-full my-2 md:m-0 md:w-40 h-10"},Pt={class:"grid grid-cols-3 col-span-10 text-center border-t border-l border-gray-200 border-solid lg:border-t-0 lg:text-right lg:col-span-3 xl:col-span-2 lg:grid-cols-1"},jt={class:"p-6"},Vt={class:"text-xs leading-5 lg:text-sm"},Mt=t("br",null,null,-1),Nt={class:"block mt-1 text-xl font-semibold leading-8 lg:text-2xl"},Ot={class:"p-6"},Lt={class:"text-xs leading-5 lg:text-sm"},Ft=t("br",null,null,-1),Rt={class:"block mt-1 text-xl font-semibold leading-8 lg:text-2xl text-green-400"},Wt={class:"p-6"},zt={class:"text-xs leading-5 lg:text-sm"},qt=t("br",null,null,-1),Ht={class:"block mt-1 text-xl font-semibold leading-8 lg:text-2xl text-red-400"},Ut={class:"col-span-3 p-6 border-t border-gray-200 border-solid lg:col-span-1"},Zt={class:"text-xs leading-5 lg:text-sm"},Gt=t("br",null,null,-1),Jt={class:"block mt-1 text-xl font-semibold leading-8 lg:text-2xl text-primary-500"},Yt={setup(d){const o=S(),s=O();V("utils");const n=E(),u=D(["This year","Previous year"]),l=D("This year");U(l,b=>{b==="Previous year"?f({previous_year:!0}):f()},{immediate:!0});async function f(b){n.hasAbilities(h.DASHBOARD)&&await o.loadData(b)}return(b,w)=>{const I=r("BaseIcon"),g=r("BaseMultiselect"),x=r("BaseFormatMoney");return c(),C("div",null,[e(o).isDashboardDataLoaded?(c(),C("div",It,[t("div",Tt,[t("div",St,[t("h6",kt,[a(I,{name:"ChartSquareBarIcon",class:"text-primary-400 mr-1"}),y(" "+_(b.$t("dashboard.monthly_chart.title")),1)]),t("div",At,[a(g,{modelValue:l.value,"onUpdate:modelValue":w[0]||(w[0]=$=>l.value=$),options:u.value,"allow-empty":!1,"show-labels":!1,placeholder:b.$t("dashboard.select_year"),"can-deselect":!1},null,8,["modelValue","options","placeholder"])])]),a(Q,{invoices:e(o).chartData.invoiceTotals,expenses:e(o).chartData.expenseTotals,receipts:e(o).chartData.receiptTotals,income:e(o).chartData.netIncomeTotals,labels:e(o).chartData.months,class:"sm:w-full"},null,8,["invoices","expenses","receipts","income","labels"])]),t("div",Pt,[t("div",jt,[t("span",Vt,_(b.$t("dashboard.chart_info.total_sales")),1),Mt,t("span",Nt,[a(x,{amount:e(o).totalSales,currency:e(s).selectedCompanyCurrency},null,8,["amount","currency"])])]),t("div",Ot,[t("span",Lt,_(b.$t("dashboard.chart_info.total_receipts")),1),Ft,t("span",Rt,[a(x,{amount:e(o).totalReceipts,currency:e(s).selectedCompanyCurrency},null,8,["amount","currency"])])]),t("div",Wt,[t("span",zt,_(b.$t("dashboard.chart_info.total_expense")),1),qt,t("span",Ht,[a(x,{amount:e(o).totalExpenses,currency:e(s).selectedCompanyCurrency},null,8,["amount","currency"])])]),t("div",Ut,[t("span",Zt,_(b.$t("dashboard.chart_info.net_income")),1),Gt,t("span",Jt,[a(x,{amount:e(o).totalNetIncome,currency:e(s).selectedCompanyCurrency},null,8,["amount","currency"])])])])])):(c(),p(Bt,{key:1}))])}}},Kt={class:"grid grid-cols-1 gap-6 mt-10 xl:grid-cols-2"},Qt={key:0,class:"due-invoices"},Xt={class:"relative z-10 flex items-center justify-between mb-3"},te={class:"mb-0 text-xl font-semibold leading-normal"},ee={key:1,class:"recent-estimates"},ae={class:"relative z-10 flex items-center justify-between mb-3"},se={class:"mb-0 text-xl font-semibold leading-normal"},oe={setup(d){const o=S(),{t:s}=Z(),n=E(),u=D(null),l=D(null),f=M(()=>[{key:"formattedDueDate",label:s("dashboard.recent_invoices_card.due_on")},{key:"user",label:s("dashboard.recent_invoices_card.customer")},{key:"due_amount",label:s("dashboard.recent_invoices_card.amount_due")},{key:"actions",tdClass:"text-right text-sm font-medium pl-0",thClass:"text-right pl-0",sortable:!1}]),b=M(()=>[{key:"formattedEstimateDate",label:s("dashboard.recent_estimate_card.date")},{key:"user",label:s("dashboard.recent_estimate_card.customer")},{key:"total",label:s("dashboard.recent_estimate_card.amount_due")},{key:"actions",tdClass:"text-right text-sm font-medium pl-0",thClass:"text-right pl-0",sortable:!1}]);function w(){return n.hasAbilities([h.DELETE_INVOICE,h.EDIT_INVOICE,h.VIEW_INVOICE,h.SEND_INVOICE])}function I(){return n.hasAbilities([h.CREATE_ESTIMATE,h.EDIT_ESTIMATE,h.VIEW_ESTIMATE,h.SEND_ESTIMATE])}return(g,x)=>{const $=r("BaseButton"),k=r("router-link"),A=r("BaseFormatMoney"),P=r("BaseTable");return c(),C("div",null,[t("div",Kt,[e(n).hasAbilities(e(h).VIEW_INVOICE)?(c(),C("div",Qt,[t("div",Xt,[t("h6",te,_(g.$t("dashboard.recent_invoices_card.title")),1),a($,{size:"sm",variant:"primary-outline",onClick:x[0]||(x[0]=m=>g.$router.push("/admin/invoices"))},{default:i(()=>[y(_(g.$t("dashboard.recent_invoices_card.view_all")),1)]),_:1})]),a(P,{data:e(o).recentDueInvoices,columns:e(f),loading:!e(o).isDashboardDataLoaded},N({"cell-user":i(({row:m})=>[a(k,{to:{path:`invoices/${m.data.id}/view`},class:"font-medium text-primary-500"},{default:i(()=>[y(_(m.data.customer.name),1)]),_:2},1032,["to"])]),"cell-due_amount":i(({row:m})=>[a(A,{amount:m.data.due_amount,currency:m.data.customer.currency},null,8,["amount","currency"])]),_:2},[w()?{name:"cell-actions",fn:i(({row:m})=>[a(X,{row:m.data,table:u.value},null,8,["row","table"])])}:void 0]),1032,["data","columns","loading"])])):v("",!0),e(n).hasAbilities(e(h).VIEW_ESTIMATE)?(c(),C("div",ee,[t("div",ae,[t("h6",se,_(g.$t("dashboard.recent_estimate_card.title")),1),a($,{variant:"primary-outline",size:"sm",onClick:x[1]||(x[1]=m=>g.$router.push("/admin/estimates"))},{default:i(()=>[y(_(g.$t("dashboard.recent_estimate_card.view_all")),1)]),_:1})]),a(P,{data:e(o).recentEstimates,columns:e(b),loading:!e(o).isDashboardDataLoaded},N({"cell-user":i(({row:m})=>[a(k,{to:{path:`estimates/${m.data.id}/view`},class:"font-medium text-primary-500"},{default:i(()=>[y(_(m.data.customer.name),1)]),_:2},1032,["to"])]),"cell-total":i(({row:m})=>[a(A,{amount:m.data.total,currency:m.data.customer.currency},null,8,["amount","currency"])]),_:2},[I()?{name:"cell-actions",fn:i(({row:m})=>[a(tt,{row:m,table:l.value},null,8,["row","table"])])}:void 0]),1032,["data","columns","loading"])])):v("",!0)])])}}},ue={setup(d){const o=G(),s=E(),n=J();return Y(()=>{o.meta.ability&&!s.hasAbilities(o.meta.ability)?n.push({name:"account.settings"}):o.meta.isOwner&&!s.currentUser.is_owner&&n.push({name:"account.settings"})}),(u,l)=>{const f=r("BasePage");return c(),p(f,null,{default:i(()=>[a(xt),a(Yt),a(oe)]),_:1})}}};export{ue as default}; diff --git a/crater/public/build/assets/DateTimeType.6886ff98.js b/crater/public/build/assets/DateTimeType.6886ff98.js new file mode 100644 index 0000000..385b5d8 --- /dev/null +++ b/crater/public/build/assets/DateTimeType.6886ff98.js @@ -0,0 +1 @@ +import{I as r,k as d,r as m,o as p,l as c,u as i,x as f}from"./vendor.d12b5734.js";const k={props:{modelValue:{type:String,default:r().format("YYYY-MM-DD hh:MM")}},emits:["update:modelValue"],setup(t,{emit:l}){const s=t,e=d({get:()=>s.modelValue,set:o=>{l("update:modelValue",o)}});return(o,a)=>{const u=m("BaseDatePicker");return p(),c(u,{modelValue:i(e),"onUpdate:modelValue":a[0]||(a[0]=n=>f(e)?e.value=n:null),"enable-time":""},null,8,["modelValue"])}}};export{k as default}; diff --git a/crater/public/build/assets/DateType.12fc8765.js b/crater/public/build/assets/DateType.12fc8765.js new file mode 100644 index 0000000..ca38123 --- /dev/null +++ b/crater/public/build/assets/DateType.12fc8765.js @@ -0,0 +1 @@ +import{I as r,k as d,r as m,o as p,l as c,u as f,x as i}from"./vendor.d12b5734.js";const k={props:{modelValue:{type:[String,Date],default:r().format("YYYY-MM-DD")}},emits:["update:modelValue"],setup(t,{emit:l}){const s=t,e=d({get:()=>s.modelValue,set:o=>{l("update:modelValue",o)}});return(o,a)=>{const u=m("BaseDatePicker");return p(),c(u,{modelValue:f(e),"onUpdate:modelValue":a[0]||(a[0]=n=>i(e)?e.value=n:null)},null,8,["modelValue"])}}};export{k as default}; diff --git a/crater/public/build/assets/DragIcon.2da3872a.js b/crater/public/build/assets/DragIcon.2da3872a.js new file mode 100644 index 0000000..1b70f7a --- /dev/null +++ b/crater/public/build/assets/DragIcon.2da3872a.js @@ -0,0 +1,10 @@ +import{aU as $r,aV as Br,aQ as Kr,aW as Hr,o as Wr,e as Xr,h as Yr}from"./vendor.d12b5734.js";import{_ as Vr}from"./main.465728e1.js";var gr={exports:{}};/**! + * Sortable 1.14.0 + * @author RubaXa + * @author owenm + * @license MIT + */function pr(l,r){var n=Object.keys(l);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(l);r&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(l,e).enumerable})),n.push.apply(n,i)}return n}function Bt(l){for(var r=1;r=0)&&(n[e]=l[e]);return n}function Qr(l,r){if(l==null)return{};var n=Jr(l,r),i,e;if(Object.getOwnPropertySymbols){var f=Object.getOwnPropertySymbols(l);for(e=0;e=0)&&(!Object.prototype.propertyIsEnumerable.call(l,i)||(n[i]=l[i]))}return n}function Zr(l){return kr(l)||qr(l)||_r(l)||tn()}function kr(l){if(Array.isArray(l))return Ze(l)}function qr(l){if(typeof Symbol!="undefined"&&l[Symbol.iterator]!=null||l["@@iterator"]!=null)return Array.from(l)}function _r(l,r){if(!!l){if(typeof l=="string")return Ze(l,r);var n=Object.prototype.toString.call(l).slice(8,-1);if(n==="Object"&&l.constructor&&(n=l.constructor.name),n==="Map"||n==="Set")return Array.from(l);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Ze(l,r)}}function Ze(l,r){(r==null||r>l.length)&&(r=l.length);for(var n=0,i=new Array(r);n"&&(r=r.substring(1)),l)try{if(l.matches)return l.matches(r);if(l.msMatchesSelector)return l.msMatchesSelector(r);if(l.webkitMatchesSelector)return l.webkitMatchesSelector(r)}catch{return!1}return!1}}function nn(l){return l.host&&l!==document&&l.host.nodeType?l.host:l.parentNode}function wt(l,r,n,i){if(l){n=n||document;do{if(r!=null&&(r[0]===">"?l.parentNode===n&&Ne(l,r):Ne(l,r))||i&&l===n)return l;if(l===n)break}while(l=nn(l))}return null}var br=/\s+/g;function lt(l,r,n){if(l&&r)if(l.classList)l.classList[n?"add":"remove"](r);else{var i=(" "+l.className+" ").replace(br," ").replace(" "+r+" "," ");l.className=(i+(n?" "+r:"")).replace(br," ")}}function L(l,r,n){var i=l&&l.style;if(i){if(n===void 0)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(l,""):l.currentStyle&&(n=l.currentStyle),r===void 0?n:n[r];!(r in i)&&r.indexOf("webkit")===-1&&(r="-webkit-"+r),i[r]=n+(typeof n=="string"?"":"px")}}function qt(l,r){var n="";if(typeof l=="string")n=l;else do{var i=L(l,"transform");i&&i!=="none"&&(n=i+" "+n)}while(!r&&(l=l.parentNode));var e=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return e&&new e(n)}function Er(l,r,n){if(l){var i=l.getElementsByTagName(r),e=0,f=i.length;if(n)for(;e=f:t=e<=f,!t)return i;if(i===Kt())break;i=zt(i,!1)}return!1}function ne(l,r,n,i){for(var e=0,f=0,t=l.children;f2&&arguments[2]!==void 0?arguments[2]:{},e=i.evt,f=Qr(i,cn);ye.pluginEvent.bind(K)(r,n,Bt({dragEl:A,parentEl:ct,ghostEl:z,rootEl:at,nextEl:_t,lastDownEl:je,cloneEl:dt,cloneHidden:Jt,dragStarted:Ee,putSortable:Et,activeSortable:K.active,originalEvent:e,oldIndex:ae,oldDraggableIndex:be,newIndex:Rt,newDraggableIndex:Qt,hideGhostForTarget:jr,unhideGhostForTarget:Fr,cloneNowHidden:function(){Jt=!0},cloneNowShown:function(){Jt=!1},dispatchSortableEvent:function(o){It({sortable:n,name:o,originalEvent:e})}},f))};function It(l){Se(Bt({putSortable:Et,cloneEl:dt,targetEl:A,rootEl:at,oldIndex:ae,oldDraggableIndex:be,newIndex:Rt,newDraggableIndex:Qt},l))}var A,ct,z,at,_t,je,dt,Jt,ae,Rt,be,Qt,Fe,Et,ie=!1,we=!1,Le=[],te,Lt,rr,nr,Dr,Cr,Ee,le,xe,Oe=!1,Ue=!1,Ge,Ot,or=[],ar=!1,$e=[],Be=typeof document!="undefined",Ke=yr,Ar=ge||Xt?"cssFloat":"float",dn=Be&&!rn&&!yr&&"draggable"in document.createElement("div"),Rr=function(){if(!!Be){if(Xt)return!1;var l=document.createElement("x");return l.style.cssText="pointer-events:auto",l.style.pointerEvents==="auto"}}(),Mr=function(r,n){var i=L(r),e=parseInt(i.width)-parseInt(i.paddingLeft)-parseInt(i.paddingRight)-parseInt(i.borderLeftWidth)-parseInt(i.borderRightWidth),f=ne(r,0,n),t=ne(r,1,n),o=f&&L(f),a=t&&L(t),s=o&&parseInt(o.marginLeft)+parseInt(o.marginRight)+ot(f).width,c=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+ot(t).width;if(i.display==="flex")return i.flexDirection==="column"||i.flexDirection==="column-reverse"?"vertical":"horizontal";if(i.display==="grid")return i.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(f&&o.float&&o.float!=="none"){var u=o.float==="left"?"left":"right";return t&&(a.clear==="both"||a.clear===u)?"vertical":"horizontal"}return f&&(o.display==="block"||o.display==="flex"||o.display==="table"||o.display==="grid"||s>=e&&i[Ar]==="none"||t&&i[Ar]==="none"&&s+c>e)?"vertical":"horizontal"},vn=function(r,n,i){var e=i?r.left:r.top,f=i?r.right:r.bottom,t=i?r.width:r.height,o=i?n.left:n.top,a=i?n.right:n.bottom,s=i?n.width:n.height;return e===o||f===a||e+t/2===o+s/2},hn=function(r,n){var i;return Le.some(function(e){var f=e[xt].options.emptyInsertThreshold;if(!(!f||ke(e))){var t=ot(e),o=r>=t.left-f&&r<=t.right+f,a=n>=t.top-f&&n<=t.bottom+f;if(o&&a)return i=e}}),i},Nr=function(r){function n(f,t){return function(o,a,s,c){var u=o.options.group.name&&a.options.group.name&&o.options.group.name===a.options.group.name;if(f==null&&(t||u))return!0;if(f==null||f===!1)return!1;if(t&&f==="clone")return f;if(typeof f=="function")return n(f(o,a,s,c),t)(o,a,s,c);var d=(t?o:a).options.group.name;return f===!0||typeof f=="string"&&f===d||f.join&&f.indexOf(d)>-1}}var i={},e=r.group;(!e||Me(e)!="object")&&(e={name:e}),i.name=e.name,i.checkPull=n(e.pull,!0),i.checkPut=n(e.put),i.revertClone=e.revertClone,r.group=i},jr=function(){!Rr&&z&&L(z,"display","none")},Fr=function(){!Rr&&z&&L(z,"display","")};Be&&document.addEventListener("click",function(l){if(we)return l.preventDefault(),l.stopPropagation&&l.stopPropagation(),l.stopImmediatePropagation&&l.stopImmediatePropagation(),we=!1,!1},!0);var ee=function(r){if(A){r=r.touches?r.touches[0]:r;var n=hn(r.clientX,r.clientY);if(n){var i={};for(var e in r)r.hasOwnProperty(e)&&(i[e]=r[e]);i.target=i.rootEl=n,i.preventDefault=void 0,i.stopPropagation=void 0,n[xt]._onDragOver(i)}}},gn=function(r){A&&A.parentNode[xt]._isOutsideThisEl(r.target)};function K(l,r){if(!(l&&l.nodeType&&l.nodeType===1))throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(l));this.el=l,this.options=r=Nt({},r),l[xt]=this;var n={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(l.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Mr(l,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,o){t.setData("Text",o.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:K.supportPointer!==!1&&"PointerEvent"in window&&!pe,emptyInsertThreshold:5};ye.initializePlugins(this,l,n);for(var i in n)!(i in r)&&(r[i]=n[i]);Nr(r);for(var e in this)e.charAt(0)==="_"&&typeof this[e]=="function"&&(this[e]=this[e].bind(this));this.nativeDraggable=r.forceFallback?!1:dn,this.nativeDraggable&&(this.options.touchStartThreshold=1),r.supportPointer?Z(l,"pointerdown",this._onTapStart):(Z(l,"mousedown",this._onTapStart),Z(l,"touchstart",this._onTapStart)),this.nativeDraggable&&(Z(l,"dragover",this),Z(l,"dragenter",this)),Le.push(this.el),r.store&&r.store.get&&this.sort(r.store.get(this)||[]),Nt(this,sn())}K.prototype={constructor:K,_isOutsideThisEl:function(r){!this.el.contains(r)&&r!==this.el&&(le=null)},_getDirection:function(r,n){return typeof this.options.direction=="function"?this.options.direction.call(this,r,n,A):this.options.direction},_onTapStart:function(r){if(!!r.cancelable){var n=this,i=this.el,e=this.options,f=e.preventOnFilter,t=r.type,o=r.touches&&r.touches[0]||r.pointerType&&r.pointerType==="touch"&&r,a=(o||r).target,s=r.target.shadowRoot&&(r.path&&r.path[0]||r.composedPath&&r.composedPath()[0])||a,c=e.filter;if(On(i),!A&&!(/mousedown|pointerdown/.test(t)&&r.button!==0||e.disabled)&&!s.isContentEditable&&!(!this.nativeDraggable&&pe&&a&&a.tagName.toUpperCase()==="SELECT")&&(a=wt(a,e.draggable,i,!1),!(a&&a.animated)&&je!==a)){if(ae=ut(a),be=ut(a,e.draggable),typeof c=="function"){if(c.call(this,r,a,this)){It({sortable:n,rootEl:s,name:"filter",targetEl:a,toEl:i,fromEl:i}),Dt("filter",n,{evt:r}),f&&r.cancelable&&r.preventDefault();return}}else if(c&&(c=c.split(",").some(function(u){if(u=wt(s,u.trim(),i,!1),u)return It({sortable:n,rootEl:u,name:"filter",targetEl:a,fromEl:i,toEl:i}),Dt("filter",n,{evt:r}),!0}),c)){f&&r.cancelable&&r.preventDefault();return}e.handle&&!wt(s,e.handle,i,!1)||this._prepareDragStart(r,o,a)}}},_prepareDragStart:function(r,n,i){var e=this,f=e.el,t=e.options,o=f.ownerDocument,a;if(i&&!A&&i.parentNode===f){var s=ot(i);if(at=f,A=i,ct=A.parentNode,_t=A.nextSibling,je=i,Fe=t.group,K.dragged=A,te={target:A,clientX:(n||r).clientX,clientY:(n||r).clientY},Dr=te.clientX-s.left,Cr=te.clientY-s.top,this._lastX=(n||r).clientX,this._lastY=(n||r).clientY,A.style["will-change"]="all",a=function(){if(Dt("delayEnded",e,{evt:r}),K.eventCanceled){e._onDrop();return}e._disableDelayedDragEvents(),!mr&&e.nativeDraggable&&(A.draggable=!0),e._triggerDragStart(r,n),It({sortable:e,name:"choose",originalEvent:r}),lt(A,t.chosenClass,!0)},t.ignore.split(",").forEach(function(c){Er(A,c.trim(),ir)}),Z(o,"dragover",ee),Z(o,"mousemove",ee),Z(o,"touchmove",ee),Z(o,"mouseup",e._onDrop),Z(o,"touchend",e._onDrop),Z(o,"touchcancel",e._onDrop),mr&&this.nativeDraggable&&(this.options.touchStartThreshold=4,A.draggable=!0),Dt("delayStart",this,{evt:r}),t.delay&&(!t.delayOnTouchOnly||n)&&(!this.nativeDraggable||!(ge||Xt))){if(K.eventCanceled){this._onDrop();return}Z(o,"mouseup",e._disableDelayedDrag),Z(o,"touchend",e._disableDelayedDrag),Z(o,"touchcancel",e._disableDelayedDrag),Z(o,"mousemove",e._delayedDragTouchMoveHandler),Z(o,"touchmove",e._delayedDragTouchMoveHandler),t.supportPointer&&Z(o,"pointermove",e._delayedDragTouchMoveHandler),e._dragStartTimer=setTimeout(a,t.delay)}else a()}},_delayedDragTouchMoveHandler:function(r){var n=r.touches?r.touches[0]:r;Math.max(Math.abs(n.clientX-this._lastX),Math.abs(n.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){A&&ir(A),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var r=this.el.ownerDocument;Q(r,"mouseup",this._disableDelayedDrag),Q(r,"touchend",this._disableDelayedDrag),Q(r,"touchcancel",this._disableDelayedDrag),Q(r,"mousemove",this._delayedDragTouchMoveHandler),Q(r,"touchmove",this._delayedDragTouchMoveHandler),Q(r,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(r,n){n=n||r.pointerType=="touch"&&r,!this.nativeDraggable||n?this.options.supportPointer?Z(document,"pointermove",this._onTouchMove):n?Z(document,"touchmove",this._onTouchMove):Z(document,"mousemove",this._onTouchMove):(Z(A,"dragend",this),Z(at,"dragstart",this._onDragStart));try{document.selection?We(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch{}},_dragStarted:function(r,n){if(ie=!1,at&&A){Dt("dragStarted",this,{evt:n}),this.nativeDraggable&&Z(document,"dragover",gn);var i=this.options;!r&<(A,i.dragClass,!1),lt(A,i.ghostClass,!0),K.active=this,r&&this._appendGhost(),It({sortable:this,name:"start",originalEvent:n})}else this._nulling()},_emulateDragOver:function(){if(Lt){this._lastX=Lt.clientX,this._lastY=Lt.clientY,jr();for(var r=document.elementFromPoint(Lt.clientX,Lt.clientY),n=r;r&&r.shadowRoot&&(r=r.shadowRoot.elementFromPoint(Lt.clientX,Lt.clientY),r!==n);)n=r;if(A.parentNode[xt]._isOutsideThisEl(r),n)do{if(n[xt]){var i=void 0;if(i=n[xt]._onDragOver({clientX:Lt.clientX,clientY:Lt.clientY,target:r,rootEl:n}),i&&!this.options.dragoverBubble)break}r=n}while(n=n.parentNode);Fr()}},_onTouchMove:function(r){if(te){var n=this.options,i=n.fallbackTolerance,e=n.fallbackOffset,f=r.touches?r.touches[0]:r,t=z&&qt(z,!0),o=z&&t&&t.a,a=z&&t&&t.d,s=Ke&&Ot&&Or(Ot),c=(f.clientX-te.clientX+e.x)/(o||1)+(s?s[0]-or[0]:0)/(o||1),u=(f.clientY-te.clientY+e.y)/(a||1)+(s?s[1]-or[1]:0)/(a||1);if(!K.active&&!ie){if(i&&Math.max(Math.abs(f.clientX-this._lastX),Math.abs(f.clientY-this._lastY))=0&&(It({rootEl:ct,name:"add",toEl:ct,fromEl:at,originalEvent:r}),It({sortable:this,name:"remove",toEl:ct,originalEvent:r}),It({rootEl:ct,name:"sort",toEl:ct,fromEl:at,originalEvent:r}),It({sortable:this,name:"sort",toEl:ct,originalEvent:r})),Et&&Et.save()):Rt!==ae&&Rt>=0&&(It({sortable:this,name:"update",toEl:ct,originalEvent:r}),It({sortable:this,name:"sort",toEl:ct,originalEvent:r})),K.active&&((Rt==null||Rt===-1)&&(Rt=ae,Qt=be),It({sortable:this,name:"end",toEl:ct,originalEvent:r}),this.save()))),this._nulling()},_nulling:function(){Dt("nulling",this),at=A=ct=z=_t=dt=je=Jt=te=Lt=Ee=Rt=Qt=ae=be=le=xe=Et=Fe=K.dragged=K.ghost=K.clone=K.active=null,$e.forEach(function(r){r.checked=!0}),$e.length=rr=nr=0},handleEvent:function(r){switch(r.type){case"drop":case"dragend":this._onDrop(r);break;case"dragenter":case"dragover":A&&(this._onDragOver(r),pn(r));break;case"selectstart":r.preventDefault();break}},toArray:function(){for(var r=[],n,i=this.el.children,e=0,f=i.length,t=this.options;ei.right+e||l.clientX<=i.right&&l.clientY>i.bottom&&l.clientX>=i.left:l.clientX>i.right&&l.clientY>i.top||l.clientX<=i.right&&l.clientY>i.bottom+e}function bn(l,r,n,i,e,f,t,o){var a=i?l.clientY:l.clientX,s=i?n.height:n.width,c=i?n.top:n.left,u=i?n.bottom:n.right,d=!1;if(!t){if(o&&Gec+s*f/2:au-Ge)return-xe}else if(a>c+s*(1-e)/2&&au-s*f/2)?a>c+s/2?1:-1:0}function En(l){return ut(A)1&&(V.forEach(function(o){f.addAnimationState({target:o,rect:Ct?ot(o):t}),tr(o),o.fromRect=t,i.removeAnimationState(o)}),Ct=!1,Cn(!this.options.removeCloneOnHide,e))},dragOverCompleted:function(n){var i=n.sortable,e=n.isOwner,f=n.insertion,t=n.activeSortable,o=n.parentEl,a=n.putSortable,s=this.options;if(f){if(e&&t._hideClone(),De=!1,s.animation&&V.length>1&&(Ct||!e&&!t.options.sort&&!a)){var c=ot(et,!1,!0,!0);V.forEach(function(d){d!==et&&(Pr(d,c),o.appendChild(d))}),Ct=!0}if(!e)if(Ct||ze(),V.length>1){var u=Ve;t._showClone(i),t.options.animation&&!Ve&&u&&Mt.forEach(function(d){t.addAnimationState({target:d,rect:Ce}),d.fromRect=Ce,d.thisAnimationDuration=null})}else t._showClone(i)}},dragOverAnimationCapture:function(n){var i=n.dragRect,e=n.isOwner,f=n.activeSortable;if(V.forEach(function(o){o.thisAnimationDuration=null}),f.options.animation&&!e&&f.multiDrag.isMultiDrag){Ce=Nt({},i);var t=qt(et,!0);Ce.top-=t.f,Ce.left-=t.e}},dragOverAnimationComplete:function(){Ct&&(Ct=!1,ze())},drop:function(n){var i=n.originalEvent,e=n.rootEl,f=n.parentEl,t=n.sortable,o=n.dispatchSortableEvent,a=n.oldIndex,s=n.putSortable,c=s||this.sortable;if(!!i){var u=this.options,d=f.children;if(!se)if(u.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),lt(et,u.selectedClass,!~V.indexOf(et)),~V.indexOf(et))V.splice(V.indexOf(et),1),Pe=null,Se({sortable:t,rootEl:e,name:"deselect",targetEl:et,originalEvt:i});else{if(V.push(et),Se({sortable:t,rootEl:e,name:"select",targetEl:et,originalEvt:i}),i.shiftKey&&Pe&&t.el.contains(Pe)){var v=ut(Pe),h=ut(et);if(~v&&~h&&v!==h){var g,p;for(h>v?(p=v,g=h):(p=h,g=v+1);p1){var S=ot(et),b=ut(et,":not(."+this.options.selectedClass+")");if(!De&&u.animation&&(et.thisAnimationDuration=null),c.captureAnimationState(),!De&&(u.animation&&(et.fromRect=S,V.forEach(function(x){if(x.thisAnimationDuration=null,x!==et){var P=Ct?ot(x):S;x.fromRect=P,c.addAnimationState({target:x,rect:P})}})),ze(),V.forEach(function(x){d[b]?f.insertBefore(x,d[b]):f.appendChild(x),b++}),a===ut(et))){var I=!1;V.forEach(function(x){if(x.sortableIndex!==ut(x)){I=!0;return}}),I&&o("update")}V.forEach(function(x){tr(x)}),c.animateAll()}Ut=c}(e===f||s&&s.lastPutMode!=="clone")&&Mt.forEach(function(x){x.parentNode&&x.parentNode.removeChild(x)})}},nullingGlobal:function(){this.isMultiDrag=se=!1,Mt.length=0},destroyGlobal:function(){this._deselectMultiDrag(),Q(document,"pointerup",this._deselectMultiDrag),Q(document,"mouseup",this._deselectMultiDrag),Q(document,"touchend",this._deselectMultiDrag),Q(document,"keydown",this._checkKeyDown),Q(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(n){if(!(typeof se!="undefined"&&se)&&Ut===this.sortable&&!(n&&wt(n.target,this.options.draggable,this.sortable.el,!1))&&!(n&&n.button!==0))for(;V.length;){var i=V[0];lt(i,this.options.selectedClass,!1),V.shift(),Se({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:i,originalEvt:n})}},_checkKeyDown:function(n){n.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(n){n.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},Nt(l,{pluginName:"multiDrag",utils:{select:function(n){var i=n.parentNode[xt];!i||!i.options.multiDrag||~V.indexOf(n)||(Ut&&Ut!==i&&(Ut.multiDrag._deselectMultiDrag(),Ut=i),lt(n,i.options.selectedClass,!0),V.push(n))},deselect:function(n){var i=n.parentNode[xt],e=V.indexOf(n);!i||!i.options.multiDrag||!~e||(lt(n,i.options.selectedClass,!1),V.splice(e,1))}},eventProperties:function(){var n=this,i=[],e=[];return V.forEach(function(f){i.push({multiDragElement:f,index:f.sortableIndex});var t;Ct&&f!==et?t=-1:Ct?t=ut(f,":not(."+n.options.selectedClass+")"):t=ut(f),e.push({multiDragElement:f,index:t})}),{items:Zr(V),clones:[].concat(Mt),oldIndicies:i,newIndicies:e}},optionListeners:{multiDragKey:function(n){return n=n.toLowerCase(),n==="ctrl"?n="Control":n.length>1&&(n=n.charAt(0).toUpperCase()+n.substr(1)),n}}})}function Cn(l,r){V.forEach(function(n,i){var e=r.children[n.sortableIndex+(l?Number(i):0)];e?r.insertBefore(n,e):r.appendChild(n)})}function Ur(l,r){Mt.forEach(function(n,i){var e=r.children[n.sortableIndex+(l?Number(i):0)];e?r.insertBefore(n,e):r.appendChild(n)})}function ze(){V.forEach(function(l){l!==et&&l.parentNode&&l.parentNode.removeChild(l)})}K.mount(new Tn);K.mount(hr,vr);var An=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:K,MultiDrag:Dn,Sortable:K,Swap:In}),Rn=$r(An);(function(l,r){(function(i,e){l.exports=e(Br,Rn)})(typeof self!="undefined"?self:Kr,function(n,i){return function(e){var f={};function t(o){if(f[o])return f[o].exports;var a=f[o]={i:o,l:!1,exports:{}};return e[o].call(a.exports,a,a.exports,t),a.l=!0,a.exports}return t.m=e,t.c=f,t.d=function(o,a,s){t.o(o,a)||Object.defineProperty(o,a,{enumerable:!0,get:s})},t.r=function(o){typeof Symbol!="undefined"&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(o,"__esModule",{value:!0})},t.t=function(o,a){if(a&1&&(o=t(o)),a&8||a&4&&typeof o=="object"&&o&&o.__esModule)return o;var s=Object.create(null);if(t.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:o}),a&2&&typeof o!="string")for(var c in o)t.d(s,c,function(u){return o[u]}.bind(null,c));return s},t.n=function(o){var a=o&&o.__esModule?function(){return o.default}:function(){return o};return t.d(a,"a",a),a},t.o=function(o,a){return Object.prototype.hasOwnProperty.call(o,a)},t.p="",t(t.s="fb15")}({"00ee":function(e,f,t){var o=t("b622"),a=o("toStringTag"),s={};s[a]="z",e.exports=String(s)==="[object z]"},"0366":function(e,f,t){var o=t("1c0b");e.exports=function(a,s,c){if(o(a),s===void 0)return a;switch(c){case 0:return function(){return a.call(s)};case 1:return function(u){return a.call(s,u)};case 2:return function(u,d){return a.call(s,u,d)};case 3:return function(u,d,v){return a.call(s,u,d,v)}}return function(){return a.apply(s,arguments)}}},"057f":function(e,f,t){var o=t("fc6a"),a=t("241c").f,s={}.toString,c=typeof window=="object"&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],u=function(d){try{return a(d)}catch{return c.slice()}};e.exports.f=function(v){return c&&s.call(v)=="[object Window]"?u(v):a(o(v))}},"06cf":function(e,f,t){var o=t("83ab"),a=t("d1e7"),s=t("5c6c"),c=t("fc6a"),u=t("c04e"),d=t("5135"),v=t("0cfb"),h=Object.getOwnPropertyDescriptor;f.f=o?h:function(p,S){if(p=c(p),S=u(S,!0),v)try{return h(p,S)}catch{}if(d(p,S))return s(!a.f.call(p,S),p[S])}},"0cfb":function(e,f,t){var o=t("83ab"),a=t("d039"),s=t("cc12");e.exports=!o&&!a(function(){return Object.defineProperty(s("div"),"a",{get:function(){return 7}}).a!=7})},"13d5":function(e,f,t){var o=t("23e7"),a=t("d58f").left,s=t("a640"),c=t("ae40"),u=s("reduce"),d=c("reduce",{1:0});o({target:"Array",proto:!0,forced:!u||!d},{reduce:function(h){return a(this,h,arguments.length,arguments.length>1?arguments[1]:void 0)}})},"14c3":function(e,f,t){var o=t("c6b6"),a=t("9263");e.exports=function(s,c){var u=s.exec;if(typeof u=="function"){var d=u.call(s,c);if(typeof d!="object")throw TypeError("RegExp exec method returned something other than an Object or null");return d}if(o(s)!=="RegExp")throw TypeError("RegExp#exec called on incompatible receiver");return a.call(s,c)}},"159b":function(e,f,t){var o=t("da84"),a=t("fdbc"),s=t("17c2"),c=t("9112");for(var u in a){var d=o[u],v=d&&d.prototype;if(v&&v.forEach!==s)try{c(v,"forEach",s)}catch{v.forEach=s}}},"17c2":function(e,f,t){var o=t("b727").forEach,a=t("a640"),s=t("ae40"),c=a("forEach"),u=s("forEach");e.exports=!c||!u?function(v){return o(this,v,arguments.length>1?arguments[1]:void 0)}:[].forEach},"1be4":function(e,f,t){var o=t("d066");e.exports=o("document","documentElement")},"1c0b":function(e,f){e.exports=function(t){if(typeof t!="function")throw TypeError(String(t)+" is not a function");return t}},"1c7e":function(e,f,t){var o=t("b622"),a=o("iterator"),s=!1;try{var c=0,u={next:function(){return{done:!!c++}},return:function(){s=!0}};u[a]=function(){return this},Array.from(u,function(){throw 2})}catch{}e.exports=function(d,v){if(!v&&!s)return!1;var h=!1;try{var g={};g[a]=function(){return{next:function(){return{done:h=!0}}}},d(g)}catch{}return h}},"1d80":function(e,f){e.exports=function(t){if(t==null)throw TypeError("Can't call method on "+t);return t}},"1dde":function(e,f,t){var o=t("d039"),a=t("b622"),s=t("2d00"),c=a("species");e.exports=function(u){return s>=51||!o(function(){var d=[],v=d.constructor={};return v[c]=function(){return{foo:1}},d[u](Boolean).foo!==1})}},"23cb":function(e,f,t){var o=t("a691"),a=Math.max,s=Math.min;e.exports=function(c,u){var d=o(c);return d<0?a(d+u,0):s(d,u)}},"23e7":function(e,f,t){var o=t("da84"),a=t("06cf").f,s=t("9112"),c=t("6eeb"),u=t("ce4e"),d=t("e893"),v=t("94ca");e.exports=function(h,g){var p=h.target,S=h.global,b=h.stat,I,x,P,O,w,U;if(S?x=o:b?x=o[p]||u(p,{}):x=(o[p]||{}).prototype,x)for(P in g){if(w=g[P],h.noTargetGet?(U=a(x,P),O=U&&U.value):O=x[P],I=v(S?P:p+(b?".":"#")+P,h.forced),!I&&O!==void 0){if(typeof w==typeof O)continue;d(w,O)}(h.sham||O&&O.sham)&&s(w,"sham",!0),c(x,P,w,h)}}},"241c":function(e,f,t){var o=t("ca84"),a=t("7839"),s=a.concat("length","prototype");f.f=Object.getOwnPropertyNames||function(u){return o(u,s)}},"25f0":function(e,f,t){var o=t("6eeb"),a=t("825a"),s=t("d039"),c=t("ad6d"),u="toString",d=RegExp.prototype,v=d[u],h=s(function(){return v.call({source:"a",flags:"b"})!="/a/b"}),g=v.name!=u;(h||g)&&o(RegExp.prototype,u,function(){var S=a(this),b=String(S.source),I=S.flags,x=String(I===void 0&&S instanceof RegExp&&!("flags"in d)?c.call(S):I);return"/"+b+"/"+x},{unsafe:!0})},"2ca0":function(e,f,t){var o=t("23e7"),a=t("06cf").f,s=t("50c4"),c=t("5a34"),u=t("1d80"),d=t("ab13"),v=t("c430"),h="".startsWith,g=Math.min,p=d("startsWith"),S=!v&&!p&&!!function(){var b=a(String.prototype,"startsWith");return b&&!b.writable}();o({target:"String",proto:!0,forced:!S&&!p},{startsWith:function(I){var x=String(u(this));c(I);var P=s(g(arguments.length>1?arguments[1]:void 0,x.length)),O=String(I);return h?h.call(x,O,P):x.slice(P,P+O.length)===O}})},"2d00":function(e,f,t){var o=t("da84"),a=t("342f"),s=o.process,c=s&&s.versions,u=c&&c.v8,d,v;u?(d=u.split("."),v=d[0]+d[1]):a&&(d=a.match(/Edge\/(\d+)/),(!d||d[1]>=74)&&(d=a.match(/Chrome\/(\d+)/),d&&(v=d[1]))),e.exports=v&&+v},"342f":function(e,f,t){var o=t("d066");e.exports=o("navigator","userAgent")||""},"35a1":function(e,f,t){var o=t("f5df"),a=t("3f8c"),s=t("b622"),c=s("iterator");e.exports=function(u){if(u!=null)return u[c]||u["@@iterator"]||a[o(u)]}},"37e8":function(e,f,t){var o=t("83ab"),a=t("9bf2"),s=t("825a"),c=t("df75");e.exports=o?Object.defineProperties:function(d,v){s(d);for(var h=c(v),g=h.length,p=0,S;g>p;)a.f(d,S=h[p++],v[S]);return d}},"3bbe":function(e,f,t){var o=t("861d");e.exports=function(a){if(!o(a)&&a!==null)throw TypeError("Can't set "+String(a)+" as a prototype");return a}},"3ca3":function(e,f,t){var o=t("6547").charAt,a=t("69f3"),s=t("7dd0"),c="String Iterator",u=a.set,d=a.getterFor(c);s(String,"String",function(v){u(this,{type:c,string:String(v),index:0})},function(){var h=d(this),g=h.string,p=h.index,S;return p>=g.length?{value:void 0,done:!0}:(S=o(g,p),h.index+=S.length,{value:S,done:!1})})},"3f8c":function(e,f){e.exports={}},"4160":function(e,f,t){var o=t("23e7"),a=t("17c2");o({target:"Array",proto:!0,forced:[].forEach!=a},{forEach:a})},"428f":function(e,f,t){var o=t("da84");e.exports=o},"44ad":function(e,f,t){var o=t("d039"),a=t("c6b6"),s="".split;e.exports=o(function(){return!Object("z").propertyIsEnumerable(0)})?function(c){return a(c)=="String"?s.call(c,""):Object(c)}:Object},"44d2":function(e,f,t){var o=t("b622"),a=t("7c73"),s=t("9bf2"),c=o("unscopables"),u=Array.prototype;u[c]==null&&s.f(u,c,{configurable:!0,value:a(null)}),e.exports=function(d){u[c][d]=!0}},"44e7":function(e,f,t){var o=t("861d"),a=t("c6b6"),s=t("b622"),c=s("match");e.exports=function(u){var d;return o(u)&&((d=u[c])!==void 0?!!d:a(u)=="RegExp")}},"4930":function(e,f,t){var o=t("d039");e.exports=!!Object.getOwnPropertySymbols&&!o(function(){return!String(Symbol())})},"4d64":function(e,f,t){var o=t("fc6a"),a=t("50c4"),s=t("23cb"),c=function(u){return function(d,v,h){var g=o(d),p=a(g.length),S=s(h,p),b;if(u&&v!=v){for(;p>S;)if(b=g[S++],b!=b)return!0}else for(;p>S;S++)if((u||S in g)&&g[S]===v)return u||S||0;return!u&&-1}};e.exports={includes:c(!0),indexOf:c(!1)}},"4de4":function(e,f,t){var o=t("23e7"),a=t("b727").filter,s=t("1dde"),c=t("ae40"),u=s("filter"),d=c("filter");o({target:"Array",proto:!0,forced:!u||!d},{filter:function(h){return a(this,h,arguments.length>1?arguments[1]:void 0)}})},"4df4":function(e,f,t){var o=t("0366"),a=t("7b0b"),s=t("9bdd"),c=t("e95a"),u=t("50c4"),d=t("8418"),v=t("35a1");e.exports=function(g){var p=a(g),S=typeof this=="function"?this:Array,b=arguments.length,I=b>1?arguments[1]:void 0,x=I!==void 0,P=v(p),O=0,w,U,T,M,j,Y;if(x&&(I=o(I,b>2?arguments[2]:void 0,2)),P!=null&&!(S==Array&&c(P)))for(M=P.call(p),j=M.next,U=new S;!(T=j.call(M)).done;O++)Y=x?s(M,I,[T.value,O],!0):T.value,d(U,O,Y);else for(w=u(p.length),U=new S(w);w>O;O++)Y=x?I(p[O],O):p[O],d(U,O,Y);return U.length=O,U}},"4fad":function(e,f,t){var o=t("23e7"),a=t("6f53").entries;o({target:"Object",stat:!0},{entries:function(c){return a(c)}})},"50c4":function(e,f,t){var o=t("a691"),a=Math.min;e.exports=function(s){return s>0?a(o(s),9007199254740991):0}},"5135":function(e,f){var t={}.hasOwnProperty;e.exports=function(o,a){return t.call(o,a)}},"5319":function(e,f,t){var o=t("d784"),a=t("825a"),s=t("7b0b"),c=t("50c4"),u=t("a691"),d=t("1d80"),v=t("8aa5"),h=t("14c3"),g=Math.max,p=Math.min,S=Math.floor,b=/\$([$&'`]|\d\d?|<[^>]*>)/g,I=/\$([$&'`]|\d\d?)/g,x=function(P){return P===void 0?P:String(P)};o("replace",2,function(P,O,w,U){var T=U.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,M=U.REPLACE_KEEPS_$0,j=T?"$":"$0";return[function(R,X){var N=d(this),$=R==null?void 0:R[P];return $!==void 0?$.call(R,N,X):O.call(String(N),R,X)},function(C,R){if(!T&&M||typeof R=="string"&&R.indexOf(j)===-1){var X=w(O,C,this,R);if(X.done)return X.value}var N=a(C),$=String(this),_=typeof R=="function";_||(R=String(R));var rt=N.global;if(rt){var yt=N.unicode;N.lastIndex=0}for(var st=[];;){var ft=h(N,$);if(ft===null||(st.push(ft),!rt))break;var pt=String(ft[0]);pt===""&&(N.lastIndex=v($,c(N.lastIndex),yt))}for(var mt="",ht=0,nt=0;nt=ht&&(mt+=$.slice(ht,At)+St,ht=At+it.length)}return mt+$.slice(ht)}];function Y(C,R,X,N,$,_){var rt=X+C.length,yt=N.length,st=I;return $!==void 0&&($=s($),st=b),O.call(_,st,function(ft,pt){var mt;switch(pt.charAt(0)){case"$":return"$";case"&":return C;case"`":return R.slice(0,X);case"'":return R.slice(rt);case"<":mt=$[pt.slice(1,-1)];break;default:var ht=+pt;if(ht===0)return ft;if(ht>yt){var nt=S(ht/10);return nt===0?ft:nt<=yt?N[nt-1]===void 0?pt.charAt(1):N[nt-1]+pt.charAt(1):ft}mt=N[ht-1]}return mt===void 0?"":mt})}})},"5692":function(e,f,t){var o=t("c430"),a=t("c6cd");(e.exports=function(s,c){return a[s]||(a[s]=c!==void 0?c:{})})("versions",[]).push({version:"3.6.5",mode:o?"pure":"global",copyright:"\xA9 2020 Denis Pushkarev (zloirock.ru)"})},"56ef":function(e,f,t){var o=t("d066"),a=t("241c"),s=t("7418"),c=t("825a");e.exports=o("Reflect","ownKeys")||function(d){var v=a.f(c(d)),h=s.f;return h?v.concat(h(d)):v}},"5a34":function(e,f,t){var o=t("44e7");e.exports=function(a){if(o(a))throw TypeError("The method doesn't accept regular expressions");return a}},"5c6c":function(e,f){e.exports=function(t,o){return{enumerable:!(t&1),configurable:!(t&2),writable:!(t&4),value:o}}},"5db7":function(e,f,t){var o=t("23e7"),a=t("a2bf"),s=t("7b0b"),c=t("50c4"),u=t("1c0b"),d=t("65f0");o({target:"Array",proto:!0},{flatMap:function(h){var g=s(this),p=c(g.length),S;return u(h),S=d(g,0),S.length=a(S,g,g,p,0,1,h,arguments.length>1?arguments[1]:void 0),S}})},"6547":function(e,f,t){var o=t("a691"),a=t("1d80"),s=function(c){return function(u,d){var v=String(a(u)),h=o(d),g=v.length,p,S;return h<0||h>=g?c?"":void 0:(p=v.charCodeAt(h),p<55296||p>56319||h+1===g||(S=v.charCodeAt(h+1))<56320||S>57343?c?v.charAt(h):p:c?v.slice(h,h+2):(p-55296<<10)+(S-56320)+65536)}};e.exports={codeAt:s(!1),charAt:s(!0)}},"65f0":function(e,f,t){var o=t("861d"),a=t("e8b5"),s=t("b622"),c=s("species");e.exports=function(u,d){var v;return a(u)&&(v=u.constructor,typeof v=="function"&&(v===Array||a(v.prototype))?v=void 0:o(v)&&(v=v[c],v===null&&(v=void 0))),new(v===void 0?Array:v)(d===0?0:d)}},"69f3":function(e,f,t){var o=t("7f9a"),a=t("da84"),s=t("861d"),c=t("9112"),u=t("5135"),d=t("f772"),v=t("d012"),h=a.WeakMap,g,p,S,b=function(T){return S(T)?p(T):g(T,{})},I=function(T){return function(M){var j;if(!s(M)||(j=p(M)).type!==T)throw TypeError("Incompatible receiver, "+T+" required");return j}};if(o){var x=new h,P=x.get,O=x.has,w=x.set;g=function(T,M){return w.call(x,T,M),M},p=function(T){return P.call(x,T)||{}},S=function(T){return O.call(x,T)}}else{var U=d("state");v[U]=!0,g=function(T,M){return c(T,U,M),M},p=function(T){return u(T,U)?T[U]:{}},S=function(T){return u(T,U)}}e.exports={set:g,get:p,has:S,enforce:b,getterFor:I}},"6eeb":function(e,f,t){var o=t("da84"),a=t("9112"),s=t("5135"),c=t("ce4e"),u=t("8925"),d=t("69f3"),v=d.get,h=d.enforce,g=String(String).split("String");(e.exports=function(p,S,b,I){var x=I?!!I.unsafe:!1,P=I?!!I.enumerable:!1,O=I?!!I.noTargetGet:!1;if(typeof b=="function"&&(typeof S=="string"&&!s(b,"name")&&a(b,"name",S),h(b).source=g.join(typeof S=="string"?S:"")),p===o){P?p[S]=b:c(S,b);return}else x?!O&&p[S]&&(P=!0):delete p[S];P?p[S]=b:a(p,S,b)})(Function.prototype,"toString",function(){return typeof this=="function"&&v(this).source||u(this)})},"6f53":function(e,f,t){var o=t("83ab"),a=t("df75"),s=t("fc6a"),c=t("d1e7").f,u=function(d){return function(v){for(var h=s(v),g=a(h),p=g.length,S=0,b=[],I;p>S;)I=g[S++],(!o||c.call(h,I))&&b.push(d?[I,h[I]]:h[I]);return b}};e.exports={entries:u(!0),values:u(!1)}},"73d9":function(e,f,t){var o=t("44d2");o("flatMap")},"7418":function(e,f){f.f=Object.getOwnPropertySymbols},"746f":function(e,f,t){var o=t("428f"),a=t("5135"),s=t("e538"),c=t("9bf2").f;e.exports=function(u){var d=o.Symbol||(o.Symbol={});a(d,u)||c(d,u,{value:s.f(u)})}},"7839":function(e,f){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},"7b0b":function(e,f,t){var o=t("1d80");e.exports=function(a){return Object(o(a))}},"7c73":function(e,f,t){var o=t("825a"),a=t("37e8"),s=t("7839"),c=t("d012"),u=t("1be4"),d=t("cc12"),v=t("f772"),h=">",g="<",p="prototype",S="script",b=v("IE_PROTO"),I=function(){},x=function(T){return g+S+h+T+g+"/"+S+h},P=function(T){T.write(x("")),T.close();var M=T.parentWindow.Object;return T=null,M},O=function(){var T=d("iframe"),M="java"+S+":",j;return T.style.display="none",u.appendChild(T),T.src=String(M),j=T.contentWindow.document,j.open(),j.write(x("document.F=Object")),j.close(),j.F},w,U=function(){try{w=document.domain&&new ActiveXObject("htmlfile")}catch{}U=w?P(w):O();for(var T=s.length;T--;)delete U[p][s[T]];return U()};c[b]=!0,e.exports=Object.create||function(M,j){var Y;return M!==null?(I[p]=o(M),Y=new I,I[p]=null,Y[b]=M):Y=U(),j===void 0?Y:a(Y,j)}},"7dd0":function(e,f,t){var o=t("23e7"),a=t("9ed3"),s=t("e163"),c=t("d2bb"),u=t("d44e"),d=t("9112"),v=t("6eeb"),h=t("b622"),g=t("c430"),p=t("3f8c"),S=t("ae93"),b=S.IteratorPrototype,I=S.BUGGY_SAFARI_ITERATORS,x=h("iterator"),P="keys",O="values",w="entries",U=function(){return this};e.exports=function(T,M,j,Y,C,R,X){a(j,M,Y);var N=function(nt){if(nt===C&&st)return st;if(!I&&nt in rt)return rt[nt];switch(nt){case P:return function(){return new j(this,nt)};case O:return function(){return new j(this,nt)};case w:return function(){return new j(this,nt)}}return function(){return new j(this)}},$=M+" Iterator",_=!1,rt=T.prototype,yt=rt[x]||rt["@@iterator"]||C&&rt[C],st=!I&&yt||N(C),ft=M=="Array"&&rt.entries||yt,pt,mt,ht;if(ft&&(pt=s(ft.call(new T)),b!==Object.prototype&&pt.next&&(!g&&s(pt)!==b&&(c?c(pt,b):typeof pt[x]!="function"&&d(pt,x,U)),u(pt,$,!0,!0),g&&(p[$]=U))),C==O&&yt&&yt.name!==O&&(_=!0,st=function(){return yt.call(this)}),(!g||X)&&rt[x]!==st&&d(rt,x,st),p[M]=st,C)if(mt={values:N(O),keys:R?st:N(P),entries:N(w)},X)for(ht in mt)(I||_||!(ht in rt))&&v(rt,ht,mt[ht]);else o({target:M,proto:!0,forced:I||_},mt);return mt}},"7f9a":function(e,f,t){var o=t("da84"),a=t("8925"),s=o.WeakMap;e.exports=typeof s=="function"&&/native code/.test(a(s))},"825a":function(e,f,t){var o=t("861d");e.exports=function(a){if(!o(a))throw TypeError(String(a)+" is not an object");return a}},"83ab":function(e,f,t){var o=t("d039");e.exports=!o(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!=7})},"8418":function(e,f,t){var o=t("c04e"),a=t("9bf2"),s=t("5c6c");e.exports=function(c,u,d){var v=o(u);v in c?a.f(c,v,s(0,d)):c[v]=d}},"861d":function(e,f){e.exports=function(t){return typeof t=="object"?t!==null:typeof t=="function"}},"8875":function(e,f,t){var o,a,s;(function(c,u){a=[],o=u,s=typeof o=="function"?o.apply(f,a):o,s!==void 0&&(e.exports=s)})(typeof self!="undefined"?self:this,function(){function c(){var u=Object.getOwnPropertyDescriptor(document,"currentScript");if(!u&&"currentScript"in document&&document.currentScript||u&&u.get!==c&&document.currentScript)return document.currentScript;try{throw new Error}catch(w){var d=/.*at [^(]*\((.*):(.+):(.+)\)$/ig,v=/@([^@]*):(\d+):(\d+)\s*$/ig,h=d.exec(w.stack)||v.exec(w.stack),g=h&&h[1]||!1,p=h&&h[2]||!1,S=document.location.href.replace(document.location.hash,""),b,I,x,P=document.getElementsByTagName("script");g===S&&(b=document.documentElement.outerHTML,I=new RegExp("(?:[^\\n]+?\\n){0,"+(p-2)+"}[^<]* diff --git a/crater/resources/scripts/admin/components/SelectNotePopup.vue b/crater/resources/scripts/admin/components/SelectNotePopup.vue new file mode 100644 index 0000000..822a8e2 --- /dev/null +++ b/crater/resources/scripts/admin/components/SelectNotePopup.vue @@ -0,0 +1,209 @@ + + + diff --git a/crater/resources/scripts/admin/components/charts/LineChart.vue b/crater/resources/scripts/admin/components/charts/LineChart.vue new file mode 100644 index 0000000..f12c4d6 --- /dev/null +++ b/crater/resources/scripts/admin/components/charts/LineChart.vue @@ -0,0 +1,197 @@ + + + diff --git a/crater/resources/scripts/admin/components/currency-exchange-rate/ExchangeRateBulkUpdate.vue b/crater/resources/scripts/admin/components/currency-exchange-rate/ExchangeRateBulkUpdate.vue new file mode 100644 index 0000000..b30fa82 --- /dev/null +++ b/crater/resources/scripts/admin/components/currency-exchange-rate/ExchangeRateBulkUpdate.vue @@ -0,0 +1,119 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/CreateCustomFields.vue b/crater/resources/scripts/admin/components/custom-fields/CreateCustomFields.vue new file mode 100644 index 0000000..70c9f05 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/CreateCustomFields.vue @@ -0,0 +1,118 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/CreateCustomFieldsSingle.vue b/crater/resources/scripts/admin/components/custom-fields/CreateCustomFieldsSingle.vue new file mode 100644 index 0000000..8b2320e --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/CreateCustomFieldsSingle.vue @@ -0,0 +1,72 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/DateTimeType.vue b/crater/resources/scripts/admin/components/custom-fields/types/DateTimeType.vue new file mode 100644 index 0000000..f29a01a --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/DateTimeType.vue @@ -0,0 +1,24 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/DateType.vue b/crater/resources/scripts/admin/components/custom-fields/types/DateType.vue new file mode 100644 index 0000000..f195411 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/DateType.vue @@ -0,0 +1,24 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/DropdownType.vue b/crater/resources/scripts/admin/components/custom-fields/types/DropdownType.vue new file mode 100644 index 0000000..4309ef4 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/DropdownType.vue @@ -0,0 +1,45 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/InputType.vue b/crater/resources/scripts/admin/components/custom-fields/types/InputType.vue new file mode 100644 index 0000000..026e4f3 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/InputType.vue @@ -0,0 +1,23 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/NumberType.vue b/crater/resources/scripts/admin/components/custom-fields/types/NumberType.vue new file mode 100644 index 0000000..5c220c5 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/NumberType.vue @@ -0,0 +1,23 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/PhoneType.vue b/crater/resources/scripts/admin/components/custom-fields/types/PhoneType.vue new file mode 100644 index 0000000..5d3c20b --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/PhoneType.vue @@ -0,0 +1,23 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/SwitchType.vue b/crater/resources/scripts/admin/components/custom-fields/types/SwitchType.vue new file mode 100644 index 0000000..34c031a --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/SwitchType.vue @@ -0,0 +1,25 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/TextAreaType.vue b/crater/resources/scripts/admin/components/custom-fields/types/TextAreaType.vue new file mode 100644 index 0000000..51dc62b --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/TextAreaType.vue @@ -0,0 +1,31 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/TimeType.vue b/crater/resources/scripts/admin/components/custom-fields/types/TimeType.vue new file mode 100644 index 0000000..d8c6170 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/TimeType.vue @@ -0,0 +1,24 @@ + + + diff --git a/crater/resources/scripts/admin/components/custom-fields/types/UrlType.vue b/crater/resources/scripts/admin/components/custom-fields/types/UrlType.vue new file mode 100644 index 0000000..56aeba3 --- /dev/null +++ b/crater/resources/scripts/admin/components/custom-fields/types/UrlType.vue @@ -0,0 +1,23 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/CustomFieldIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/CustomFieldIndexDropdown.vue new file mode 100644 index 0000000..964de2d --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/CustomFieldIndexDropdown.vue @@ -0,0 +1,99 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/CustomerIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/CustomerIndexDropdown.vue new file mode 100644 index 0000000..cdc5733 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/CustomerIndexDropdown.vue @@ -0,0 +1,113 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/EstimateIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/EstimateIndexDropdown.vue new file mode 100644 index 0000000..15bb3ce --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/EstimateIndexDropdown.vue @@ -0,0 +1,337 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/ExpenseCategoryIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/ExpenseCategoryIndexDropdown.vue new file mode 100644 index 0000000..3fd07d4 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/ExpenseCategoryIndexDropdown.vue @@ -0,0 +1,105 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/ExpenseIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/ExpenseIndexDropdown.vue new file mode 100644 index 0000000..d979e2b --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/ExpenseIndexDropdown.vue @@ -0,0 +1,94 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/InvoiceIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/InvoiceIndexDropdown.vue new file mode 100755 index 0000000..35d8d90 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/InvoiceIndexDropdown.vue @@ -0,0 +1,261 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/ItemIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/ItemIndexDropdown.vue new file mode 100644 index 0000000..5a51d17 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/ItemIndexDropdown.vue @@ -0,0 +1,96 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/NoteIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/NoteIndexDropdown.vue new file mode 100644 index 0000000..569cfb0 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/NoteIndexDropdown.vue @@ -0,0 +1,109 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/PaymentIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/PaymentIndexDropdown.vue new file mode 100644 index 0000000..f23df97 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/PaymentIndexDropdown.vue @@ -0,0 +1,166 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/PaymentModeIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/PaymentModeIndexDropdown.vue new file mode 100644 index 0000000..d4bf918 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/PaymentModeIndexDropdown.vue @@ -0,0 +1,93 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/RecurringInvoiceIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/RecurringInvoiceIndexDropdown.vue new file mode 100644 index 0000000..adaafb9 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/RecurringInvoiceIndexDropdown.vue @@ -0,0 +1,131 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/RoleIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/RoleIndexDropdown.vue new file mode 100644 index 0000000..606c503 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/RoleIndexDropdown.vue @@ -0,0 +1,106 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/TaxTypeIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/TaxTypeIndexDropdown.vue new file mode 100644 index 0000000..73897a2 --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/TaxTypeIndexDropdown.vue @@ -0,0 +1,104 @@ + + + diff --git a/crater/resources/scripts/admin/components/dropdowns/UserIndexDropdown.vue b/crater/resources/scripts/admin/components/dropdowns/UserIndexDropdown.vue new file mode 100644 index 0000000..494d0bd --- /dev/null +++ b/crater/resources/scripts/admin/components/dropdowns/UserIndexDropdown.vue @@ -0,0 +1,87 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRow.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRow.vue new file mode 100644 index 0000000..8af4293 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRow.vue @@ -0,0 +1,504 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRowTax.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRowTax.vue new file mode 100644 index 0000000..cf3b129 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItemRowTax.vue @@ -0,0 +1,225 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItems.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItems.vue new file mode 100644 index 0000000..f5042e9 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateItems.vue @@ -0,0 +1,194 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateNotesField.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateNotesField.vue new file mode 100644 index 0000000..abdce0b --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateNotesField.vue @@ -0,0 +1,46 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotal.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotal.vue new file mode 100644 index 0000000..a14c429 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotal.vue @@ -0,0 +1,353 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotalTaxes.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotalTaxes.vue new file mode 100644 index 0000000..fcb5e1a --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/CreateTotalTaxes.vue @@ -0,0 +1,83 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/ExchangeRateConverter.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/ExchangeRateConverter.vue new file mode 100644 index 0000000..5c15ba8 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/ExchangeRateConverter.vue @@ -0,0 +1,178 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/SalesTax.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/SalesTax.vue new file mode 100644 index 0000000..6be475c --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/SalesTax.vue @@ -0,0 +1,215 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTaxPopup.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTaxPopup.vue new file mode 100644 index 0000000..af054a3 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTaxPopup.vue @@ -0,0 +1,231 @@ + + + diff --git a/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTemplateButton.vue b/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTemplateButton.vue new file mode 100644 index 0000000..c85f291 --- /dev/null +++ b/crater/resources/scripts/admin/components/estimate-invoice-common/SelectTemplateButton.vue @@ -0,0 +1,68 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/BackupModal.vue b/crater/resources/scripts/admin/components/modal-components/BackupModal.vue new file mode 100644 index 0000000..88e6fc0 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/BackupModal.vue @@ -0,0 +1,181 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/CategoryModal.vue b/crater/resources/scripts/admin/components/modal-components/CategoryModal.vue new file mode 100644 index 0000000..031280c --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/CategoryModal.vue @@ -0,0 +1,161 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/CompanyModal.vue b/crater/resources/scripts/admin/components/modal-components/CompanyModal.vue new file mode 100644 index 0000000..019a065 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/CompanyModal.vue @@ -0,0 +1,260 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/CustomerModal.vue b/crater/resources/scripts/admin/components/modal-components/CustomerModal.vue new file mode 100644 index 0000000..da158bf --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/CustomerModal.vue @@ -0,0 +1,666 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/DeleteCompanyModal.vue b/crater/resources/scripts/admin/components/modal-components/DeleteCompanyModal.vue new file mode 100644 index 0000000..661c972 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/DeleteCompanyModal.vue @@ -0,0 +1,157 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/ExchangeRateBulkUpdateModal.vue b/crater/resources/scripts/admin/components/modal-components/ExchangeRateBulkUpdateModal.vue new file mode 100644 index 0000000..614ba01 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/ExchangeRateBulkUpdateModal.vue @@ -0,0 +1,24 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/ExchangeRateProviderModal.vue b/crater/resources/scripts/admin/components/modal-components/ExchangeRateProviderModal.vue new file mode 100644 index 0000000..b466ca6 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/ExchangeRateProviderModal.vue @@ -0,0 +1,484 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/FileDiskModal.vue b/crater/resources/scripts/admin/components/modal-components/FileDiskModal.vue new file mode 100644 index 0000000..8227425 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/FileDiskModal.vue @@ -0,0 +1,151 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/ItemModal.vue b/crater/resources/scripts/admin/components/modal-components/ItemModal.vue new file mode 100644 index 0000000..5fd3f21 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/ItemModal.vue @@ -0,0 +1,263 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/ItemUnitModal.vue b/crater/resources/scripts/admin/components/modal-components/ItemUnitModal.vue new file mode 100644 index 0000000..b958435 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/ItemUnitModal.vue @@ -0,0 +1,143 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/MailTestModal.vue b/crater/resources/scripts/admin/components/modal-components/MailTestModal.vue new file mode 100644 index 0000000..c71d042 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/MailTestModal.vue @@ -0,0 +1,169 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/NoteModal.vue b/crater/resources/scripts/admin/components/modal-components/NoteModal.vue new file mode 100644 index 0000000..aa5695a --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/NoteModal.vue @@ -0,0 +1,281 @@ + + + + + diff --git a/crater/resources/scripts/admin/components/modal-components/PaymentModeModal.vue b/crater/resources/scripts/admin/components/modal-components/PaymentModeModal.vue new file mode 100644 index 0000000..88c017d --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/PaymentModeModal.vue @@ -0,0 +1,133 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/RolesModal.vue b/crater/resources/scripts/admin/components/modal-components/RolesModal.vue new file mode 100644 index 0000000..70ebd65 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/RolesModal.vue @@ -0,0 +1,299 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/SelectTemplateModal.vue b/crater/resources/scripts/admin/components/modal-components/SelectTemplateModal.vue new file mode 100644 index 0000000..b7280c3 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/SelectTemplateModal.vue @@ -0,0 +1,147 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/SendEstimateModal.vue b/crater/resources/scripts/admin/components/modal-components/SendEstimateModal.vue new file mode 100644 index 0000000..795cdfe --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/SendEstimateModal.vue @@ -0,0 +1,277 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue b/crater/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue new file mode 100644 index 0000000..63e7e6a --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue @@ -0,0 +1,290 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/SendPaymentModal.vue b/crater/resources/scripts/admin/components/modal-components/SendPaymentModal.vue new file mode 100644 index 0000000..6c78631 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/SendPaymentModal.vue @@ -0,0 +1,283 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/TaxTypeModal.vue b/crater/resources/scripts/admin/components/modal-components/TaxTypeModal.vue new file mode 100644 index 0000000..e7c1d76 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/TaxTypeModal.vue @@ -0,0 +1,259 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/TaxationAddressModal.vue b/crater/resources/scripts/admin/components/modal-components/TaxationAddressModal.vue new file mode 100644 index 0000000..0afdd99 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/TaxationAddressModal.vue @@ -0,0 +1,210 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/custom-fields/CustomFieldModal.vue b/crater/resources/scripts/admin/components/modal-components/custom-fields/CustomFieldModal.vue new file mode 100644 index 0000000..d1a1c78 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/custom-fields/CustomFieldModal.vue @@ -0,0 +1,423 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/custom-fields/OptionsCreate.vue b/crater/resources/scripts/admin/components/modal-components/custom-fields/OptionsCreate.vue new file mode 100644 index 0000000..0ec049b --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/custom-fields/OptionsCreate.vue @@ -0,0 +1,36 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/disks/DoSpacesDisk.vue b/crater/resources/scripts/admin/components/modal-components/disks/DoSpacesDisk.vue new file mode 100644 index 0000000..0dd83e4 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/disks/DoSpacesDisk.vue @@ -0,0 +1,330 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/disks/DropboxDisk.vue b/crater/resources/scripts/admin/components/modal-components/disks/DropboxDisk.vue new file mode 100644 index 0000000..75da1b9 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/disks/DropboxDisk.vue @@ -0,0 +1,299 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/disks/LocalDisk.vue b/crater/resources/scripts/admin/components/modal-components/disks/LocalDisk.vue new file mode 100644 index 0000000..7d539ff --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/disks/LocalDisk.vue @@ -0,0 +1,221 @@ + + + diff --git a/crater/resources/scripts/admin/components/modal-components/disks/S3Disk.vue b/crater/resources/scripts/admin/components/modal-components/disks/S3Disk.vue new file mode 100644 index 0000000..082c8e9 --- /dev/null +++ b/crater/resources/scripts/admin/components/modal-components/disks/S3Disk.vue @@ -0,0 +1,304 @@ + + + diff --git a/crater/resources/scripts/admin/layouts/LayoutBasic.vue b/crater/resources/scripts/admin/layouts/LayoutBasic.vue new file mode 100644 index 0000000..5ed3391 --- /dev/null +++ b/crater/resources/scripts/admin/layouts/LayoutBasic.vue @@ -0,0 +1,82 @@ + + + diff --git a/crater/resources/scripts/admin/layouts/LayoutInstallation.vue b/crater/resources/scripts/admin/layouts/LayoutInstallation.vue new file mode 100644 index 0000000..951367b --- /dev/null +++ b/crater/resources/scripts/admin/layouts/LayoutInstallation.vue @@ -0,0 +1,13 @@ + + + diff --git a/crater/resources/scripts/admin/layouts/LayoutLogin.vue b/crater/resources/scripts/admin/layouts/LayoutLogin.vue new file mode 100644 index 0000000..9154064 --- /dev/null +++ b/crater/resources/scripts/admin/layouts/LayoutLogin.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/crater/resources/scripts/admin/layouts/partials/TheSiteHeader.vue b/crater/resources/scripts/admin/layouts/partials/TheSiteHeader.vue new file mode 100644 index 0000000..d573a01 --- /dev/null +++ b/crater/resources/scripts/admin/layouts/partials/TheSiteHeader.vue @@ -0,0 +1,228 @@ + + + diff --git a/crater/resources/scripts/admin/layouts/partials/TheSiteSidebar.vue b/crater/resources/scripts/admin/layouts/partials/TheSiteSidebar.vue new file mode 100644 index 0000000..ee0f5b1 --- /dev/null +++ b/crater/resources/scripts/admin/layouts/partials/TheSiteSidebar.vue @@ -0,0 +1,179 @@ + + + diff --git a/crater/resources/scripts/admin/stores/auth.js b/crater/resources/scripts/admin/stores/auth.js new file mode 100644 index 0000000..363bd73 --- /dev/null +++ b/crater/resources/scripts/admin/stores/auth.js @@ -0,0 +1,70 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useAuthStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'auth', + state: () => ({ + status: '', + + loginData: { + email: '', + password: '', + remember: '', + }, + }), + + actions: { + login(data) { + return new Promise((resolve, reject) => { + axios.get('/sanctum/csrf-cookie').then((response) => { + if (response) { + axios + .post('/login', data) + .then((response) => { + resolve(response) + + setTimeout(() => { + this.loginData.email = '' + this.loginData.password = '' + }, 1000) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }) + }, + + logout() { + return new Promise((resolve, reject) => { + axios + .post('/auth/logout') + .then((response) => { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: 'Logged out successfully.', + }) + + window.router.push('/login') + // resetStore.clearPinia() + resolve(response) + }) + .catch((err) => { + handleError(err) + window.router.push('/') + reject(err) + }) + }) + }, + }, + })() +} \ No newline at end of file diff --git a/crater/resources/scripts/admin/stores/backup.js b/crater/resources/scripts/admin/stores/backup.js new file mode 100644 index 0000000..3716673 --- /dev/null +++ b/crater/resources/scripts/admin/stores/backup.js @@ -0,0 +1,76 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useBackupStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'backup', + + state: () => ({ + backups: [], + currentBackupData: { + option: 'full', + selected_disk: null, + }, + }), + + actions: { + fetchBackups(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/backups`, { params }) + .then((response) => { + this.backups = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + createBackup(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/backups`, data) + .then((response) => { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.backup.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + removeBackup(params) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/backups/${params.disk}`, { params }) + .then((response) => { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.backup.deleted_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/category.js b/crater/resources/scripts/admin/stores/category.js new file mode 100644 index 0000000..787f61b --- /dev/null +++ b/crater/resources/scripts/admin/stores/category.js @@ -0,0 +1,129 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useCategoryStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'category', + + state: () => ({ + categories: [], + currentCategory: { + id: null, + name: '', + description: '', + }, + editCategory: null + }), + + getters: { + isEdit: (state) => (state.currentCategory.id ? true : false), + }, + + actions: { + fetchCategories(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/categories`, { params }) + .then((response) => { + this.categories = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchCategory(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/categories/${id}`) + .then((response) => { + this.currentCategory = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addCategory(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/categories', data) + .then((response) => { + this.categories.push(response.data.data) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.expense_category.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCategory(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/categories/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.categories.findIndex( + (category) => category.id === response.data.data.id + ) + this.categories[pos] = data.categories + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t( + 'settings.expense_category.updated_message' + ), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteCategory(id) { + return new Promise((resolve) => { + axios + .delete(`/api/v1/categories/${id}`) + .then((response) => { + let index = this.categories.findIndex( + (category) => category.id === id + ) + this.categories.splice(index, 1) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.expense_category.deleted_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + console.error(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/company.js b/crater/resources/scripts/admin/stores/company.js new file mode 100644 index 0000000..1e10489 --- /dev/null +++ b/crater/resources/scripts/admin/stores/company.js @@ -0,0 +1,189 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' +import Ls from '@/scripts/services/ls' + +export const useCompanyStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'company', + + state: () => ({ + companies: [], + selectedCompany: null, + selectedCompanySettings: {}, + selectedCompanyCurrency: null, + }), + + actions: { + setSelectedCompany(data) { + window.Ls.set('selectedCompany', data.id) + this.selectedCompany = data + }, + + fetchBasicMailConfig() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/company/mail/config') + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCompany(data) { + return new Promise((resolve, reject) => { + axios + .put('/api/v1/company', data) + .then((response) => { + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.company_info.updated_message'), + }) + + this.selectedCompany = response.data.data + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCompanyLogo(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/company/upload-logo', data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addNewCompany(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/companies', data) + .then((response) => { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('company_switcher.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchCompany(params) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/current-company', params) + .then((response) => { + Object.assign(this.companyForm, response.data.data.address) + this.companyForm.name = response.data.data.name + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchUserCompanies() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/companies') + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchCompanySettings(settings) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/company/settings', { + params: { + settings, + }, + }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCompanySettings({ data, message }) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/company/settings', data) + .then((response) => { + Object.assign(this.selectedCompanySettings, data.settings) + + if (message) { + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t(message), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteCompany(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/companies/delete`, data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setDefaultCurrency(data) { + this.defaultCurrency = data.currency + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/custom-field.js b/crater/resources/scripts/admin/stores/custom-field.js new file mode 100644 index 0000000..9013ec2 --- /dev/null +++ b/crater/resources/scripts/admin/stores/custom-field.js @@ -0,0 +1,211 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import customFieldStub from '@/scripts/admin/stub/custom-field' +import utilities from '@/scripts/helpers/utilities' +import { util } from 'prettier' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useCustomFieldStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'custom-field', + + state: () => ({ + customFields: [], + isRequestOngoing: false, + + currentCustomField: { + ...customFieldStub, + }, + }), + + getters: { + isEdit() { + return this.currentCustomField.id ? true : false + }, + }, + + actions: { + resetCustomFields() { + this.customFields = [] + }, + + resetCurrentCustomField() { + this.currentCustomField = { + ...customFieldStub, + } + }, + + fetchCustomFields(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/custom-fields`, { params }) + .then((response) => { + this.customFields = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchNoteCustomFields(params) { + return new Promise((resolve, reject) => { + if (this.isRequestOngoing) { + resolve({ requestOnGoing: true }) + return true + } + + this.isRequestOngoing = true + + axios + .get(`/api/v1/custom-fields`, { params }) + .then((response) => { + this.customFields = response.data.data + this.isRequestOngoing = false + resolve(response) + }) + .catch((err) => { + this.isRequestOngoing = false + handleError(err) + reject(err) + }) + }) + }, + + fetchCustomField(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/custom-fields/${id}`) + .then((response) => { + this.currentCustomField = response.data.data + + if ( + this.currentCustomField.options && + this.currentCustomField.options.length + ) { + this.currentCustomField.options = + this.currentCustomField.options.map((option) => { + return (option = { name: option }) + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addCustomField(params) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/custom-fields`, params) + .then((response) => { + let data = { + ...response.data.data, + } + + if (data.options) { + data.options = data.options.map((option) => { + return { name: option ? option : '' } + }) + } + + this.customFields.push(data) + + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.custom_fields.added_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCustomField(params) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/custom-fields/${params.id}`, params) + .then((response) => { + let data = { + ...response.data.data, + } + + if (data.options) { + data.options = data.options.map((option) => { + return { name: option ? option : '' } + }) + } + + let pos = this.customFields.findIndex((_f) => _f.id === data.id) + + if (this.customFields[pos]) { + this.customFields[pos] = data + } + + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.custom_fields.updated_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteCustomFields(id) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/custom-fields/${id}`) + .then((response) => { + let index = this.customFields.findIndex( + (field) => field.id === id + ) + + this.customFields.splice(index, 1) + + if (response.data.error) { + notificationStore.showNotification({ + type: 'error', + message: global.t('settings.custom_fields.already_in_use'), + }) + } else { + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.custom_fields.deleted_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + // notificationStore.showNotification({ + // type: 'error', + // message: global.t('settings.custom_fields.already_in_use'), + // }) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/customer.js b/crater/resources/scripts/admin/stores/customer.js new file mode 100644 index 0000000..ffb3b58 --- /dev/null +++ b/crater/resources/scripts/admin/stores/customer.js @@ -0,0 +1,257 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useRoute } from 'vue-router' +import { handleError } from '@/scripts/helpers/error-handling' +import { useNotificationStore } from '@/scripts/stores/notification' +import { useGlobalStore } from '@/scripts/admin/stores/global' +import { useCompanyStore } from '@/scripts/admin/stores/company' +import addressStub from '@/scripts/admin/stub/address.js' +import customerStub from '@/scripts/admin/stub/customer' + +export const useCustomerStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'customer', + state: () => ({ + customers: [], + totalCustomers: 0, + selectAllField: false, + selectedCustomers: [], + selectedViewCustomer: {}, + isFetchingInitialSettings: false, + isFetchingViewData: false, + currentCustomer: { + ...customerStub(), + }, + editCustomer: null + }), + + getters: { + isEdit: (state) => (state.currentCustomer.id ? true : false), + }, + + actions: { + resetCurrentCustomer() { + this.currentCustomer = { + ...customerStub(), + } + }, + + copyAddress() { + this.currentCustomer.shipping = { + ...this.currentCustomer.billing, + type: 'shipping', + } + }, + + fetchCustomerInitialSettings(isEdit) { + const route = useRoute() + const globalStore = useGlobalStore() + const companyStore = useCompanyStore() + + this.isFetchingInitialSettings = true + let editActions = [] + if (isEdit) { + editActions = [this.fetchCustomer(route.params.id)] + } else { + this.currentCustomer.currency_id = + companyStore.selectedCompanyCurrency.id + } + + Promise.all([ + globalStore.fetchCurrencies(), + globalStore.fetchCountries(), + ...editActions, + ]) + .then(async ([res1, res2, res3]) => { + this.isFetchingInitialSettings = false + }) + .catch((error) => { + handleError(error) + }) + }, + + fetchCustomers(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/customers`, { params }) + .then((response) => { + this.customers = response.data.data + this.totalCustomers = response.data.meta.customer_total_count + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchViewCustomer(params) { + return new Promise((resolve, reject) => { + this.isFetchingViewData = true + axios + .get(`/api/v1/customers/${params.id}/stats`, { params }) + + .then((response) => { + this.selectedViewCustomer = {} + Object.assign(this.selectedViewCustomer, response.data.data) + this.setAddressStub(response.data.data) + this.isFetchingViewData = false + resolve(response) + }) + .catch((err) => { + this.isFetchingViewData = false + handleError(err) + reject(err) + }) + }) + }, + + fetchCustomer(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/customers/${id}`) + .then((response) => { + Object.assign(this.currentCustomer, response.data.data) + + this.setAddressStub(response.data.data) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addCustomer(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/customers', data) + .then((response) => { + this.customers.push(response.data.data) + + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('customers.created_message'), + }) + resolve(response) + }) + + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateCustomer(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/customers/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.customers.findIndex( + (customer) => customer.id === response.data.data.id + ) + this.customers[pos] = data + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('customers.updated_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteCustomer(id) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/customers/delete`, id) + .then((response) => { + let index = this.customers.findIndex( + (customer) => customer.id === id + ) + this.customers.splice(index, 1) + notificationStore.showNotification({ + type: 'success', + message: global.tc('customers.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleCustomers() { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/customers/delete`, { ids: this.selectedCustomers }) + .then((response) => { + this.selectedCustomers.forEach((customer) => { + let index = this.customers.findIndex( + (_customer) => _customer.id === customer.id + ) + this.customers.splice(index, 1) + }) + + notificationStore.showNotification({ + type: 'success', + message: global.tc('customers.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setSelectAllState(data) { + this.selectAllField = data + }, + + selectCustomer(data) { + this.selectedCustomers = data + if (this.selectedCustomers.length === this.customers.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllCustomers() { + if (this.selectedCustomers.length === this.customers.length) { + this.selectedCustomers = [] + this.selectAllField = false + } else { + let allCustomerIds = this.customers.map((customer) => customer.id) + this.selectedCustomers = allCustomerIds + this.selectAllField = true + } + }, + + setAddressStub(data) { + if (!data.billing) this.currentCustomer.billing = { ...addressStub } + if (!data.shipping) this.currentCustomer.shipping = { ...addressStub } + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/dashboard.js b/crater/resources/scripts/admin/stores/dashboard.js new file mode 100644 index 0000000..9375ac8 --- /dev/null +++ b/crater/resources/scripts/admin/stores/dashboard.js @@ -0,0 +1,87 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useGlobalStore } from '@/scripts/admin/stores/global' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useDashboardStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'dashboard', + + state: () => ({ + stats: { + totalAmountDue: 0, + totalCustomerCount: 0, + totalInvoiceCount: 0, + totalEstimateCount: 0, + }, + + chartData: { + months: [], + invoiceTotals: [], + expenseTotals: [], + receiptTotals: [], + netIncomeTotals: [], + }, + + totalSales: null, + totalReceipts: null, + totalExpenses: null, + totalNetIncome: null, + + recentDueInvoices: [], + recentEstimates: [], + + isDashboardDataLoaded: false, + }), + + actions: { + loadData(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/dashboard`, { params }) + .then((response) => { + // Stats + this.stats.totalAmountDue = response.data.total_amount_due + this.stats.totalCustomerCount = response.data.total_customer_count + this.stats.totalInvoiceCount = response.data.total_invoice_count + this.stats.totalEstimateCount = response.data.total_estimate_count + + // Dashboard Chart + if (this.chartData && response.data.chart_data) { + this.chartData.months = response.data.chart_data.months + this.chartData.invoiceTotals = + response.data.chart_data.invoice_totals + this.chartData.expenseTotals = + response.data.chart_data.expense_totals + this.chartData.receiptTotals = + response.data.chart_data.receipt_totals + this.chartData.netIncomeTotals = + response.data.chart_data.net_income_totals + } + + // Dashboard Chart Labels + this.totalSales = response.data.total_sales + this.totalReceipts = response.data.total_receipts + this.totalExpenses = response.data.total_expenses + this.totalNetIncome = response.data.total_net_income + + // Dashboard Table Data + this.recentDueInvoices = response.data.recent_due_invoices + this.recentEstimates = response.data.recent_estimates + + this.isDashboardDataLoaded = true + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/disk.js b/crater/resources/scripts/admin/stores/disk.js new file mode 100644 index 0000000..769197d --- /dev/null +++ b/crater/resources/scripts/admin/stores/disk.js @@ -0,0 +1,183 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useDiskStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'disk', + + state: () => ({ + disks: [], + diskDrivers: [], + diskConfigData: null, + selected_driver: 'local', + + doSpaceDiskConfig: { + name: '', + selected_driver: 'doSpaces', + key: '', + secret: '', + region: '', + bucket: '', + endpoint: '', + root: '', + }, + + dropBoxDiskConfig: { + name: '', + selected_driver: 'dropbox', + token: '', + key: '', + secret: '', + app: '', + }, + + localDiskConfig: { + name: '', + selected_driver: 'local', + root: '', + }, + + s3DiskConfigData: { + name: '', + selected_driver: 's3', + key: '', + secret: '', + region: '', + bucket: '', + root: '', + }, + }), + + getters: { + getDiskDrivers: (state) => state.diskDrivers, + }, + + actions: { + fetchDiskEnv(data) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/disks/${data.disk}`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchDisks(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/disks`, { params }) + .then((response) => { + this.disks = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchDiskDrivers() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/disk/drivers`) + .then((response) => { + this.diskConfigData = response.data + this.diskDrivers = response.data.drivers + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteFileDisk(id) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/disks/${id}`) + .then((response) => { + if (response.data.success) { + let index = this.disks.findIndex( + (category) => category.id === id + ) + this.disks.splice(index, 1) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.disk.deleted_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateDisk(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/disks/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.disks.findIndex( + (disk) => disk.id === response.data.data + ) + this.disks[pos] = data.disks + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.disk.success_set_default_disk'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + createDisk(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/disks`, data) + .then((response) => { + if (response.data) { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.disk.success_create'), + }) + } + this.disks.push(response.data) + resolve(response) + }) + .catch((err) => { + /* notificationStore.showNotification({ + type: 'error', + message: global.t('settings.disk.invalid_disk_credentials'), + }) */ + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/estimate.js b/crater/resources/scripts/admin/stores/estimate.js new file mode 100644 index 0000000..5cedf78 --- /dev/null +++ b/crater/resources/scripts/admin/stores/estimate.js @@ -0,0 +1,568 @@ +import axios from 'axios' +import moment from 'moment' +import Guid from 'guid' +import _ from 'lodash' +import { defineStore } from 'pinia' +import { useRoute } from 'vue-router' +import { useCompanyStore } from './company' +import { useCustomerStore } from './customer' +import { useNotificationStore } from '@/scripts/stores/notification' +import { useItemStore } from './item' +import { useTaxTypeStore } from './tax-type' +import { handleError } from '@/scripts/helpers/error-handling' +import estimateStub from '../stub/estimate' +import estimateItemStub from '../stub/estimate-item' +import taxStub from '../stub/tax' +import { useUserStore } from './user' + +export const useEstimateStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'estimate', + + state: () => ({ + templates: [], + + estimates: [], + selectAllField: false, + selectedEstimates: [], + totalEstimateCount: 0, + isFetchingInitialSettings: false, + showExchangeRate: false, + + newEstimate: { + ...estimateStub(), + }, + }), + + getters: { + getSubTotal() { + return this.newEstimate.items.reduce(function (a, b) { + return a + b['total'] + }, 0) + }, + getTotalSimpleTax() { + return _.sumBy(this.newEstimate.taxes, function (tax) { + if (!tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalCompoundTax() { + return _.sumBy(this.newEstimate.taxes, function (tax) { + if (tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalTax() { + if ( + this.newEstimate.tax_per_item === 'NO' || + this.newEstimate.tax_per_item === null + ) { + return this.getTotalSimpleTax + this.getTotalCompoundTax + } + return _.sumBy(this.newEstimate.items, function (tax) { + return tax.tax + }) + }, + + getSubtotalWithDiscount() { + return this.getSubTotal - this.newEstimate.discount_val + }, + + getTotal() { + return this.getSubtotalWithDiscount + this.getTotalTax + }, + + isEdit: (state) => (state.newEstimate.id ? true : false), + }, + + actions: { + resetCurrentEstimate() { + this.newEstimate = { + ...estimateStub(), + } + }, + + previewEstimate(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/estimates/${params.id}/send/preview`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchEstimates(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/estimates`, { params }) + .then((response) => { + this.estimates = response.data.data + this.totalEstimateCount = response.data.meta.estimate_total_count + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + getNextNumber(params, setState = false) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/next-number?key=estimate`, { params }) + .then((response) => { + if (setState) { + this.newEstimate.estimate_number = response.data.nextNumber + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchEstimate(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/estimates/${id}`) + .then((response) => { + Object.assign(this.newEstimate, response.data.data) + resolve(response) + }) + .catch((err) => { + console.log(err); + handleError(err) + reject(err) + }) + }) + }, + + addSalesTaxUs() { + const taxTypeStore = useTaxTypeStore() + let salesTax = { ...taxStub } + let found = this.newEstimate.taxes.find((_t) => _t.name === 'Sales Tax' && _t.type === 'MODULE') + if (found) { + for (const key in found) { + if (Object.prototype.hasOwnProperty.call(salesTax, key)) { + salesTax[key] = found[key] + } + } + salesTax.id = found.tax_type_id + console.log(salesTax, 'salesTax'); + + taxTypeStore.taxTypes.push(salesTax) + console.log(taxTypeStore.taxTypes); + } + }, + + sendEstimate(data) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/${data.id}/send`, data) + .then((response) => { + if (!data.is_preview) { + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.send_estimate_successfully'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addEstimate(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/estimates', data) + .then((response) => { + this.estimates = [...this.estimates, response.data.estimate] + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.created_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteEstimate(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/delete`, id) + .then((response) => { + let index = this.estimates.findIndex( + (estimate) => estimate.id === id + ) + + this.estimates.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleEstimates(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/delete`, { ids: this.selectedEstimates }) + .then((response) => { + this.selectedEstimates.forEach((estimate) => { + let index = this.estimates.findIndex( + (_est) => _est.id === estimate.id + ) + this.estimates.splice(index, 1) + }) + this.selectedEstimates = [] + + notificationStore.showNotification({ + type: 'success', + message: global.tc('estimates.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateEstimate(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/estimates/${data.id}`, data) + .then((response) => { + let pos = this.estimates.findIndex( + (estimate) => estimate.id === response.data.data.id + ) + this.estimates[pos] = response.data.data + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.updated_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + markAsAccepted(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/${data.id}/status`, data) + .then((response) => { + let pos = this.estimates.findIndex( + (estimate) => estimate.id === data.id + ) + if (this.estimates[pos]) { + this.estimates[pos].status = 'ACCEPTED' + + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.marked_as_accepted_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + markAsRejected(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/${data.id}/status`, data) + .then((response) => { + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.marked_as_rejected_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + markAsSent(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/${data.id}/status`, data) + .then((response) => { + let pos = this.estimates.findIndex( + (estimate) => estimate.id === data.id + ) + if (this.estimates[pos]) { + this.estimates[pos].status = 'SENT' + + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.mark_as_sent_successfully'), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + convertToInvoice(id) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/estimates/${id}/convert-to-invoice`) + .then((response) => { + notificationStore.showNotification({ + type: 'success', + message: global.t('estimates.conversion_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + searchEstimate(data) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/estimates?${data}`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + selectEstimate(data) { + this.selectedEstimates = data + if (this.selectedEstimates.length === this.estimates.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllEstimates() { + if (this.selectedEstimates.length === this.estimates.length) { + this.selectedEstimates = [] + this.selectAllField = false + } else { + let allEstimateIds = this.estimates.map((estimate) => estimate.id) + this.selectedEstimates = allEstimateIds + this.selectAllField = true + } + }, + + selectCustomer(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/customers/${id}`) + .then((response) => { + this.newEstimate.customer = response.data.data + this.newEstimate.customer_id = response.data.data.id + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + fetchEstimateTemplates(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/estimates/templates`, { params }) + .then((response) => { + this.templates = response.data.estimateTemplates + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setTemplate(data) { + this.newEstimate.template_name = data + }, + + resetSelectedCustomer() { + this.newEstimate.customer = null + this.newEstimate.customer_id = '' + }, + + selectNote(data) { + this.newEstimate.selectedNote = null + this.newEstimate.selectedNote = data + }, + + resetSelectedNote() { + this.newEstimate.selectedNote = null + }, + + addItem() { + this.newEstimate.items.push({ + ...estimateItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + }) + }, + + updateItem(data) { + Object.assign(this.newEstimate.items[data.index], { ...data }) + }, + + removeItem(index) { + this.newEstimate.items.splice(index, 1) + }, + + deselectItem(index) { + this.newEstimate.items[index] = { + ...estimateItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + } + }, + + async fetchEstimateInitialSettings(isEdit) { + const companyStore = useCompanyStore() + const customerStore = useCustomerStore() + const itemStore = useItemStore() + const taxTypeStore = useTaxTypeStore() + const route = useRoute() + const userStore = useUserStore() + + this.isFetchingInitialSettings = true + this.newEstimate.selectedCurrency = companyStore.selectedCompanyCurrency + + if (route.query.customer) { + let response = await customerStore.fetchCustomer(route.query.customer) + this.newEstimate.customer = response.data.data + this.newEstimate.customer_id = response.data.data.id + } + + let editActions = [] + + if (!isEdit) { + this.newEstimate.tax_per_item = + companyStore.selectedCompanySettings.tax_per_item + this.newEstimate.sales_tax_type = companyStore.selectedCompanySettings.sales_tax_type + this.newEstimate.sales_tax_address_type = companyStore.selectedCompanySettings.sales_tax_address_type + this.newEstimate.discount_per_item = + companyStore.selectedCompanySettings.discount_per_item + this.newEstimate.estimate_date = moment().format('YYYY-MM-DD') + if (companyStore.selectedCompanySettings.estimate_set_expiry_date_automatically === 'YES') { + this.newEstimate.expiry_date = moment() + .add(companyStore.selectedCompanySettings.estimate_expiry_date_days, 'days') + .format('YYYY-MM-DD') + } + } else { + editActions = [this.fetchEstimate(route.params.id)] + } + + Promise.all([ + itemStore.fetchItems({ + filter: {}, + orderByField: '', + orderBy: '', + }), + this.resetSelectedNote(), + this.fetchEstimateTemplates(), + this.getNextNumber(), + taxTypeStore.fetchTaxTypes({ limit: 'all' }), + ...editActions, + ]) + .then(async ([res1, res2, res3, res4, res5, res6, res7]) => { + // Create + if (!isEdit) { + if (res4.data) { + this.newEstimate.estimate_number = res4.data.nextNumber + } + + this.setTemplate(this.templates[0].name) + this.newEstimate.template_name = + userStore.currentUserSettings.default_estimate_template ? + userStore.currentUserSettings.default_estimate_template : this.newEstimate.template_name + } + + if (isEdit) { + this.addSalesTaxUs() + } + this.isFetchingInitialSettings = false + }) + .catch((err) => { + handleError(err) + this.isFetchingInitialSettings = false + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/exchange-rate.js b/crater/resources/scripts/admin/stores/exchange-rate.js new file mode 100644 index 0000000..814752d --- /dev/null +++ b/crater/resources/scripts/admin/stores/exchange-rate.js @@ -0,0 +1,249 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useExchangeRateStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + const notificationStore = useNotificationStore() + + return defineStoreFunc({ + id: 'exchange-rate', + + state: () => ({ + supportedCurrencies: [], + drivers: [], + activeUsedCurrencies: [], + providers: [], + currencies: null, + currentExchangeRate: { + id: null, + driver: '', + key: '', + active: true, + currencies: [], + }, + currencyConverter: { + type: '', + url: '', + }, + bulkCurrencies: [], + }), + getters: { + isEdit: (state) => (state.currentExchangeRate.id ? true : false), + }, + + actions: { + fetchProviders(params) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/exchange-rate-providers', { params }) + .then((response) => { + this.providers = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchDefaultProviders() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/config?key=exchange_rate_drivers`) + .then((response) => { + this.drivers = response.data.exchange_rate_drivers + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchProvider(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/exchange-rate-providers/${id}`) + .then((response) => { + this.currentExchangeRate = response.data.data + this.currencyConverter = response.data.data.driver_config + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addProvider(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/exchange-rate-providers', data) + .then((response) => { + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.exchange_rate.created_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + + reject(err) + }) + }) + }, + + updateProvider(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/exchange-rate-providers/${data.id}`, data) + .then((response) => { + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.exchange_rate.updated_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteExchangeRate(id) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/exchange-rate-providers/${id}`) + .then((response) => { + let index = this.drivers.findIndex((driver) => driver.id === id) + this.drivers.splice(index, 1) + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.exchange_rate.deleted_message'), + }) + } else { + notificationStore.showNotification({ + type: 'error', + message: global.t('settings.exchange_rate.error'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + fetchCurrencies(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/supported-currencies`, { params }) + .then((response) => { + this.supportedCurrencies = response.data.supportedCurrencies + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + fetchActiveCurrency(params) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/used-currencies', { params }) + .then((response) => { + this.activeUsedCurrencies = response.data.activeUsedCurrencies + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + fetchBulkCurrencies() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/currencies/used') + .then((response) => { + this.bulkCurrencies = response.data.currencies.map((_m) => { + _m.exchange_rate = null + return _m + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + updateBulkExchangeRate(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/currencies/bulk-update-exchange-rate', data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + getCurrentExchangeRate(currencyId) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/currencies/${currencyId}/exchange-rate`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + reject(err) + }) + }) + }, + getCurrencyConverterServers() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/config?key=currency_converter_servers') + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + checkForActiveProvider(currency_id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/currencies/${currency_id}/active-provider`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/expense.js b/crater/resources/scripts/admin/stores/expense.js new file mode 100644 index 0000000..706762f --- /dev/null +++ b/crater/resources/scripts/admin/stores/expense.js @@ -0,0 +1,240 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { handleError } from '@/scripts/helpers/error-handling' +import { useNotificationStore } from '@/scripts/stores/notification' +import expenseStub from '@/scripts/admin/stub/expense' +import utils from '@/scripts/helpers/utilities' + +export const useExpenseStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'expense', + + state: () => ({ + expenses: [], + totalExpenses: 0, + selectAllField: false, + selectedExpenses: [], + paymentModes: [], + showExchangeRate: false, + currentExpense: { + ...expenseStub, + }, + }), + + getters: { + getCurrentExpense: (state) => state.currentExpense, + getSelectedExpenses: (state) => state.selectedExpenses, + }, + + actions: { + resetCurrentExpenseData() { + this.currentExpense = { + ...expenseStub, + } + }, + + fetchExpenses(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/expenses`, { params }) + .then((response) => { + this.expenses = response.data.data + this.totalExpenses = response.data.meta.expense_total_count + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchExpense(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/expenses/${id}`) + .then((response) => { + if (response.data) { + Object.assign(this.currentExpense, response.data.data) + this.currentExpense.selectedCurrency = + response.data.data.currency + this.currentExpense.attachment_receipt = null + if (response.data.data.attachment_receipt_url) { + if ( + utils.isImageFile( + response.data.data.attachment_receipt_meta.mime_type + ) + ) { + this.currentExpense.receiptFiles = [ + { image: `/reports/expenses/${id}/receipt?${response.data.data.attachment_receipt_meta.uuid}` }, + ] + } else { + this.currentExpense.receiptFiles = [ + { + type: 'document', + name: response.data.data.attachment_receipt_meta + .file_name, + }, + ] + } + } else { + this.currentExpense.receiptFiles = [] + } + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addExpense(data) { + const formData = utils.toFormData(data) + + return new Promise((resolve, reject) => { + axios + .post('/api/v1/expenses', formData) + .then((response) => { + this.expenses.push(response.data) + + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('expenses.created_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateExpense({ id, data, isAttachmentReceiptRemoved }) { + const notificationStore = useNotificationStore() + + const formData = utils.toFormData(data) + + formData.append('_method', 'PUT') + formData.append('is_attachment_receipt_removed', isAttachmentReceiptRemoved) + + return new Promise((resolve) => { + axios.post(`/api/v1/expenses/${id}`, formData).then((response) => { + let pos = this.expenses.findIndex( + (expense) => expense.id === response.data.id + ) + + this.expenses[pos] = data.expense + + notificationStore.showNotification({ + type: 'success', + message: global.t('expenses.updated_message'), + }) + + resolve(response) + }) + }).catch((err) => { + handleError(err) + reject(err) + }) + }, + + setSelectAllState(data) { + this.selectAllField = data + }, + + selectExpense(data) { + this.selectedExpenses = data + if (this.selectedExpenses.length === this.expenses.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllExpenses(data) { + if (this.selectedExpenses.length === this.expenses.length) { + this.selectedExpenses = [] + this.selectAllField = false + } else { + let allExpenseIds = this.expenses.map((expense) => expense.id) + this.selectedExpenses = allExpenseIds + this.selectAllField = true + } + }, + + deleteExpense(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/expenses/delete`, id) + .then((response) => { + let index = this.expenses.findIndex( + (expense) => expense.id === id + ) + this.expenses.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.tc('expenses.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleExpenses() { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/expenses/delete`, { ids: this.selectedExpenses }) + .then((response) => { + this.selectedExpenses.forEach((expense) => { + let index = this.expenses.findIndex( + (_expense) => _expense.id === expense.id + ) + this.expenses.splice(index, 1) + }) + notificationStore.showNotification({ + type: 'success', + message: global.tc('expenses.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + fetchPaymentModes(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payment-methods`, { params }) + .then((response) => { + this.paymentModes = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/global.js b/crater/resources/scripts/admin/stores/global.js new file mode 100644 index 0000000..7e88596 --- /dev/null +++ b/crater/resources/scripts/admin/stores/global.js @@ -0,0 +1,243 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useCompanyStore } from './company' +import { useUserStore } from './user' +import { useModuleStore } from './module' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' +import _ from 'lodash' + +export const useGlobalStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'global', + state: () => ({ + // Global Configuration + config: null, + globalSettings: null, + + // Global Lists + timeZones: [], + dateFormats: [], + currencies: [], + countries: [], + languages: [], + fiscalYears: [], + + // Menus + mainMenu: [], + settingMenu: [], + + // Boolean Flags + isAppLoaded: false, + isSidebarOpen: false, + areCurrenciesLoading: false, + + downloadReport: null, + }), + + getters: { + menuGroups: (state) => { + return Object.values(_.groupBy(state.mainMenu, 'group')) + }, + }, + + actions: { + bootstrap() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/bootstrap') + .then((response) => { + const companyStore = useCompanyStore() + const userStore = useUserStore() + const moduleStore = useModuleStore() + + this.mainMenu = response.data.main_menu + this.settingMenu = response.data.setting_menu + + this.config = response.data.config + this.globalSettings = response.data.global_settings + + // user store + userStore.currentUser = response.data.current_user + userStore.currentUserSettings = + response.data.current_user_settings + userStore.currentAbilities = response.data.current_user_abilities + + // Module store + moduleStore.apiToken = response.data.global_settings.api_token + moduleStore.enableModules = response.data.modules + + // company store + companyStore.companies = response.data.companies + companyStore.selectedCompany = response.data.current_company + companyStore.setSelectedCompany(response.data.current_company) + companyStore.selectedCompanySettings = + response.data.current_company_settings + companyStore.selectedCompanyCurrency = + response.data.current_company_currency + + global.locale = + response.data.current_user_settings.language || 'en' + + this.isAppLoaded = true + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchCurrencies() { + return new Promise((resolve, reject) => { + if (this.currencies.length || this.areCurrenciesLoading) { + resolve(this.currencies) + } else { + this.areCurrenciesLoading = true + axios + .get('/api/v1/currencies') + .then((response) => { + this.currencies = response.data.data.filter((currency) => { + return (currency.name = `${currency.code} - ${currency.name}`) + }) + this.areCurrenciesLoading = false + resolve(response) + }) + .catch((err) => { + handleError(err) + this.areCurrenciesLoading = false + reject(err) + }) + } + }) + }, + + fetchConfig(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/config`, { params }) + .then((response) => { + if (response.data.languages) { + this.languages = response.data.languages + } else { + this.fiscalYears = response.data.fiscal_years + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchDateFormats() { + return new Promise((resolve, reject) => { + if (this.dateFormats.length) { + resolve(this.dateFormats) + } else { + axios + .get('/api/v1/date/formats') + .then((response) => { + this.dateFormats = response.data.date_formats + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }, + + fetchTimeZones() { + return new Promise((resolve, reject) => { + if (this.timeZones.length) { + resolve(this.timeZones) + } else { + axios + .get('/api/v1/timezones') + .then((response) => { + this.timeZones = response.data.time_zones + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }, + + fetchCountries() { + return new Promise((resolve, reject) => { + if (this.countries.length) { + resolve(this.countries) + } else { + axios + .get('/api/v1/countries') + .then((response) => { + this.countries = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }, + + fetchPlaceholders(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/number-placeholders`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setSidebarVisibility(val) { + this.isSidebarOpen = val + }, + + setIsAppLoaded(isAppLoaded) { + this.isAppLoaded = isAppLoaded + }, + + updateGlobalSettings({ data, message }) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/settings', data) + .then((response) => { + Object.assign(this.globalSettings, data.settings) + + if (message) { + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t(message), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/installation.js b/crater/resources/scripts/admin/stores/installation.js new file mode 100644 index 0000000..971ee10 --- /dev/null +++ b/crater/resources/scripts/admin/stores/installation.js @@ -0,0 +1,172 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useCompanyStore } from './company' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useInstallationStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + const companyStore = useCompanyStore() + + return defineStoreFunc({ + id: 'installation', + + state: () => ({ + currentDataBaseData: { + database_connection: 'mysql', + database_hostname: '127.0.0.1', + database_port: '3306', + database_name: null, + database_username: null, + database_password: null, + app_url: window.location.origin, + }, + }), + + actions: { + fetchInstallationRequirements() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/installation/requirements`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInstallationStep() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/installation/wizard-step`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addInstallationStep(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/installation/wizard-step`, data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInstallationPermissions() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/installation/permissions`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInstallationDatabase(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/installation/database/config`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addInstallationDatabase(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/installation/database/config`, data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addInstallationFinish() { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/installation/finish`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setInstallationDomain(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/installation/set-domain`, data) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + installationLogin() { + return new Promise((resolve, reject) => { + axios.get('/sanctum/csrf-cookie').then((response) => { + if (response) { + axios + .post('/api/v1/installation/login') + .then((response) => { + companyStore.setSelectedCompany(response.data.company) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }) + }, + + checkAutheticated() { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/auth/check`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/invoice.js b/crater/resources/scripts/admin/stores/invoice.js new file mode 100644 index 0000000..97bcebf --- /dev/null +++ b/crater/resources/scripts/admin/stores/invoice.js @@ -0,0 +1,518 @@ +import axios from 'axios' +import moment from 'moment' +import Guid from 'guid' +import _ from 'lodash' +import { defineStore } from 'pinia' +import { useRoute } from 'vue-router' +import { handleError } from '@/scripts/helpers/error-handling' +import invoiceItemStub from '../stub/invoice-item' +import taxStub from '../stub/tax' +import invoiceStub from '../stub/invoice' + +import { useNotificationStore } from '@/scripts/stores/notification' +import { useCustomerStore } from './customer' +import { useTaxTypeStore } from './tax-type' +import { useCompanyStore } from './company' +import { useItemStore } from './item' +import { useUserStore } from './user' + +export const useInvoiceStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + const notificationStore = useNotificationStore() + + return defineStoreFunc({ + id: 'invoice', + state: () => ({ + templates: [], + invoices: [], + selectedInvoices: [], + selectAllField: false, + invoiceTotalCount: 0, + showExchangeRate: false, + isFetchingInitialSettings: false, + isFetchingInvoice: false, + + newInvoice: { + ...invoiceStub(), + }, + }), + + getters: { + getInvoice: (state) => (id) => { + let invId = parseInt(id) + return state.invoices.find((invoice) => invoice.id === invId) + }, + + getSubTotal() { + return this.newInvoice.items.reduce(function (a, b) { + return a + b['total'] + }, 0) + }, + + getTotalSimpleTax() { + return _.sumBy(this.newInvoice.taxes, function (tax) { + if (!tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalCompoundTax() { + return _.sumBy(this.newInvoice.taxes, function (tax) { + if (tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalTax() { + if ( + this.newInvoice.tax_per_item === 'NO' || + this.newInvoice.tax_per_item === null + ) { + return this.getTotalSimpleTax + this.getTotalCompoundTax + } + return _.sumBy(this.newInvoice.items, function (tax) { + return tax.tax + }) + }, + + getSubtotalWithDiscount() { + return this.getSubTotal - this.newInvoice.discount_val + }, + + getTotal() { + return this.getSubtotalWithDiscount + this.getTotalTax + }, + + isEdit: (state) => (state.newInvoice.id ? true : false), + }, + + actions: { + resetCurrentInvoice() { + this.newInvoice = { + ...invoiceStub(), + } + }, + + previewInvoice(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/invoices/${params.id}/send/preview`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInvoices(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/invoices`, { params }) + .then((response) => { + this.invoices = response.data.data + this.invoiceTotalCount = response.data.meta.invoice_total_count + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInvoice(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/invoices/${id}`) + .then((response) => { + Object.assign(this.newInvoice, response.data.data) + this.newInvoice.customer = response.data.data.customer + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addSalesTaxUs() { + const taxTypeStore = useTaxTypeStore() + let salesTax = { ...taxStub } + let found = this.newInvoice.taxes.find((_t) => _t.name === 'Sales Tax' && _t.type === 'MODULE') + if (found) { + for (const key in found) { + if (Object.prototype.hasOwnProperty.call(salesTax, key)) { + salesTax[key] = found[key] + } + } + salesTax.id = found.tax_type_id + taxTypeStore.taxTypes.push(salesTax) + } + }, + + sendInvoice(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/invoices/${data.id}/send`, data) + .then((response) => { + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.invoice_sent_successfully'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addInvoice(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/invoices', data) + .then((response) => { + this.invoices = [...this.invoices, response.data.invoice] + + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.created_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteInvoice(id) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/invoices/delete`, id) + .then((response) => { + let index = this.invoices.findIndex( + (invoice) => invoice.id === id + ) + this.invoices.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleInvoices(id) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/invoices/delete`, { ids: this.selectedInvoices }) + .then((response) => { + this.selectedInvoices.forEach((invoice) => { + let index = this.invoices.findIndex( + (_inv) => _inv.id === invoice.id + ) + this.invoices.splice(index, 1) + }) + this.selectedInvoices = [] + + notificationStore.showNotification({ + type: 'success', + message: global.tc('invoices.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateInvoice(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/invoices/${data.id}`, data) + .then((response) => { + let pos = this.invoices.findIndex( + (invoice) => invoice.id === response.data.data.id + ) + this.invoices[pos] = response.data.data + + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.updated_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + cloneInvoice(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/invoices/${data.id}/clone`, data) + .then((response) => { + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.cloned_successfully'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + markAsSent(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/invoices/${data.id}/status`, data) + .then((response) => { + let pos = this.invoices.findIndex( + (invoices) => invoices.id === data.id + ) + + if (this.invoices[pos]) { + this.invoices[pos].status = 'SENT' + } + + notificationStore.showNotification({ + type: 'success', + message: global.t('invoices.mark_as_sent_successfully'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + getNextNumber(params, setState = false) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/next-number?key=invoice`, { params }) + .then((response) => { + if (setState) { + this.newInvoice.invoice_number = response.data.nextNumber + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + searchInvoice(data) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/invoices?${data}`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + selectInvoice(data) { + this.selectedInvoices = data + if (this.selectedInvoices.length === this.invoices.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllInvoices() { + if (this.selectedInvoices.length === this.invoices.length) { + this.selectedInvoices = [] + this.selectAllField = false + } else { + let allInvoiceIds = this.invoices.map((invoice) => invoice.id) + this.selectedInvoices = allInvoiceIds + this.selectAllField = true + } + }, + + selectCustomer(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/customers/${id}`) + .then((response) => { + this.newInvoice.customer = response.data.data + this.newInvoice.customer_id = response.data.data.id + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchInvoiceTemplates(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/invoices/templates`, { params }) + .then((response) => { + this.templates = response.data.invoiceTemplates + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + selectNote(data) { + this.newInvoice.selectedNote = null + this.newInvoice.selectedNote = data + }, + + setTemplate(data) { + this.newInvoice.template_name = data + }, + + resetSelectedCustomer() { + this.newInvoice.customer = null + this.newInvoice.customer_id = null + }, + + addItem() { + this.newInvoice.items.push({ + ...invoiceItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + }) + }, + + updateItem(data) { + Object.assign(this.newInvoice.items[data.index], { ...data }) + }, + + removeItem(index) { + this.newInvoice.items.splice(index, 1) + }, + + deselectItem(index) { + this.newInvoice.items[index] = { + ...invoiceItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + } + }, + + resetSelectedNote() { + this.newInvoice.selectedNote = null + }, + + // On Load actions + async fetchInvoiceInitialSettings(isEdit) { + const companyStore = useCompanyStore() + const customerStore = useCustomerStore() + const itemStore = useItemStore() + const taxTypeStore = useTaxTypeStore() + const route = useRoute() + const userStore = useUserStore() + + this.isFetchingInitialSettings = true + + this.newInvoice.selectedCurrency = companyStore.selectedCompanyCurrency + + if (route.query.customer) { + let response = await customerStore.fetchCustomer(route.query.customer) + this.newInvoice.customer = response.data.data + this.newInvoice.customer_id = response.data.data.id + } + + let editActions = [] + + if (!isEdit) { + this.newInvoice.tax_per_item = + companyStore.selectedCompanySettings.tax_per_item + this.newInvoice.sales_tax_type = companyStore.selectedCompanySettings.sales_tax_type + this.newInvoice.sales_tax_address_type = companyStore.selectedCompanySettings.sales_tax_address_type + this.newInvoice.discount_per_item = + companyStore.selectedCompanySettings.discount_per_item + this.newInvoice.invoice_date = moment().format('YYYY-MM-DD') + if (companyStore.selectedCompanySettings.invoice_set_due_date_automatically === 'YES') { + this.newInvoice.due_date = moment() + .add(companyStore.selectedCompanySettings.invoice_due_date_days, 'days') + .format('YYYY-MM-DD') + } + } else { + editActions = [this.fetchInvoice(route.params.id)] + } + + Promise.all([ + itemStore.fetchItems({ + filter: {}, + orderByField: '', + orderBy: '', + }), + this.resetSelectedNote(), + this.fetchInvoiceTemplates(), + this.getNextNumber(), + taxTypeStore.fetchTaxTypes({ limit: 'all' }), + ...editActions, + ]) + .then(async ([res1, res2, res3, res4, res5, res6]) => { + if (!isEdit) { + if (res4.data) { + this.newInvoice.invoice_number = res4.data.nextNumber + } + + if (res3.data) { + this.setTemplate(this.templates[0].name) + this.newInvoice.template_name = + userStore.currentUserSettings.default_invoice_template ? + userStore.currentUserSettings.default_invoice_template : this.newInvoice.template_name + } + } + if (isEdit) { + this.addSalesTaxUs() + } + + this.isFetchingInitialSettings = false + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/item.js b/crater/resources/scripts/admin/stores/item.js new file mode 100644 index 0000000..f075997 --- /dev/null +++ b/crater/resources/scripts/admin/stores/item.js @@ -0,0 +1,336 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useItemStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'item', + state: () => ({ + items: [], + totalItems: 0, + selectAllField: false, + selectedItems: [], + itemUnits: [], + currentItemUnit: { + id: null, + name: '', + }, + currentItem: { + name: '', + description: '', + price: 0, + unit_id: '', + unit: null, + taxes: [], + tax_per_item: false, + }, + }), + getters: { + isItemUnitEdit: (state) => (state.currentItemUnit.id ? true : false), + }, + actions: { + resetCurrentItem() { + this.currentItem = { + name: '', + description: '', + price: 0, + unit_id: '', + unit: null, + taxes: [], + } + }, + fetchItems(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/items`, { params }) + .then((response) => { + this.items = response.data.data + this.totalItems = response.data.meta.item_total_count + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchItem(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/items/${id}`) + .then((response) => { + if (response.data) { + Object.assign(this.currentItem, response.data.data) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addItem(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/items', data) + .then((response) => { + const notificationStore = useNotificationStore() + + this.items.push(response.data.data) + + notificationStore.showNotification({ + type: 'success', + message: global.t('items.created_message'), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateItem(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/items/${data.id}`, data) + .then((response) => { + if (response.data) { + const notificationStore = useNotificationStore() + + let pos = this.items.findIndex( + (item) => item.id === response.data.data.id + ) + + this.items[pos] = data.item + + notificationStore.showNotification({ + type: 'success', + message: global.t('items.updated_message'), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteItem(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/items/delete`, id) + .then((response) => { + let index = this.items.findIndex((item) => item.id === id) + this.items.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.tc('items.deleted_message', 1), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleItems() { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/items/delete`, { ids: this.selectedItems }) + .then((response) => { + this.selectedItems.forEach((item) => { + let index = this.items.findIndex( + (_item) => _item.id === item.id + ) + this.items.splice(index, 1) + }) + + notificationStore.showNotification({ + type: 'success', + message: global.tc('items.deleted_message', 2), + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + selectItem(data) { + this.selectedItems = data + if (this.selectedItems.length === this.items.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllItems(data) { + if (this.selectedItems.length === this.items.length) { + this.selectedItems = [] + this.selectAllField = false + } else { + let allItemIds = this.items.map((item) => item.id) + this.selectedItems = allItemIds + this.selectAllField = true + } + }, + + addItemUnit(data) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/units`, data) + .then((response) => { + this.itemUnits.push(response.data.data) + + if (response.data.data) { + notificationStore.showNotification({ + type: 'success', + message: global.t( + 'settings.customization.items.item_unit_added' + ), + }) + } + + if (response.data.errors) { + notificationStore.showNotification({ + type: 'error', + message: err.response.data.errors[0], + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateItemUnit(data) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/units/${data.id}`, data) + .then((response) => { + let pos = this.itemUnits.findIndex( + (unit) => unit.id === response.data.data.id + ) + + this.itemUnits[pos] = data + + if (response.data.data) { + notificationStore.showNotification({ + type: 'success', + message: global.t( + 'settings.customization.items.item_unit_updated' + ), + }) + } + + if (response.data.errors) { + notificationStore.showNotification({ + type: 'error', + message: err.response.data.errors[0], + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchItemUnits(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/units`, { params }) + .then((response) => { + this.itemUnits = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchItemUnit(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/units/${id}`) + .then((response) => { + this.currentItemUnit = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteItemUnit(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/units/${id}`) + .then((response) => { + if (!response.data.error) { + let index = this.itemUnits.findIndex((unit) => unit.id === id) + this.itemUnits.splice(index, 1) + } + + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t( + 'settings.customization.items.deleted_message' + ), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/mail-driver.js b/crater/resources/scripts/admin/stores/mail-driver.js new file mode 100644 index 0000000..f6086ef --- /dev/null +++ b/crater/resources/scripts/admin/stores/mail-driver.js @@ -0,0 +1,146 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useMailDriverStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'mail-driver', + + state: () => ({ + mailConfigData: null, + mail_driver: 'smtp', + mail_drivers: [], + + basicMailConfig: { + mail_driver: '', + mail_host: '', + from_mail: '', + from_name: '', + }, + + mailgunConfig: { + mail_driver: '', + mail_mailgun_domain: '', + mail_mailgun_secret: '', + mail_mailgun_endpoint: '', + from_mail: '', + from_name: '', + }, + + sesConfig: { + mail_driver: '', + mail_host: '', + mail_port: null, + mail_ses_key: '', + mail_ses_secret: '', + mail_encryption: 'tls', + from_mail: '', + from_name: '', + }, + + smtpConfig: { + mail_driver: '', + mail_host: '', + mail_port: null, + mail_username: '', + mail_password: '', + mail_encryption: 'tls', + from_mail: '', + from_name: '', + }, + }), + + actions: { + fetchMailDrivers() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/mail/drivers') + .then((response) => { + if (response.data) { + this.mail_drivers = response.data + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchMailConfig() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/mail/config') + .then((response) => { + if (response.data) { + this.mailConfigData = response.data + this.mail_driver = response.data.mail_driver + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateMailConfig(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/mail/config', data) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('wizard.success.' + response.data.success), + }) + } else { + notificationStore.showNotification({ + type: 'error', + message: global.t('wizard.errors.' + response.data.error), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + sendTestMail(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/mail/test', data) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('general.send_mail_successfully'), + }) + } else { + notificationStore.showNotification({ + type: 'error', + message: global.t('validation.something_went_wrong'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/module.js b/crater/resources/scripts/admin/stores/module.js new file mode 100644 index 0000000..eb32919 --- /dev/null +++ b/crater/resources/scripts/admin/stores/module.js @@ -0,0 +1,132 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { handleError } from '@/scripts/helpers/error-handling' +import { useNotificationStore } from '@/scripts/stores/notification' + +export const useModuleStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'modules', + + state: () => ({ + currentModule: {}, + modules: [], + apiToken: null, + currentUser: { + api_token: null, + }, + enableModules: [] + }), + + getters: { + salesTaxUSEnabled: (state) => (state.enableModules.includes('SalesTaxUS')), + }, + + actions: { + fetchModules(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/modules`) + .then((response) => { + this.modules = response.data.data + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchModule(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/modules/${id}`) + .then((response) => { + if (response.data.error === 'invalid_token') { + this.currentModule = {}, + this.modules = [], + this.apiToken = null, + this.currentUser.api_token = null, + window.router.push('/admin/modules') + } else { + this.currentModule = response.data + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + checkApiToken(token) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/modules/check?api_token=${token}`) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.error === 'invalid_token') { + notificationStore.showNotification({ + type: 'error', + message: global.t('modules.invalid_api_token'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + disableModule(module) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/modules/${module}/disable`) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('modules.module_disabled'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + enableModule(module) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/modules/${module}/enable`) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('modules.module_enabled'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/note.js b/crater/resources/scripts/admin/stores/note.js new file mode 100644 index 0000000..f05f257 --- /dev/null +++ b/crater/resources/scripts/admin/stores/note.js @@ -0,0 +1,117 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useNotesStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'notes', + + state: () => ({ + notes: [], + currentNote: { + id: null, + type: '', + name: '', + notes: '', + }, + }), + + getters: { + isEdit: (state) => (state.currentNote.id ? true : false), + }, + + actions: { + resetCurrentNote() { + this.currentNote = { + type: '', + name: '', + notes: '', + } + }, + + fetchNotes(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/notes`, { params }) + .then((response) => { + this.notes = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchNote(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/notes/${id}`) + .then((response) => { + this.currentNote = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addNote(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/notes', data) + .then((response) => { + this.notes.push(response.data) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateNote(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/notes/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.notes.findIndex( + (notes) => notes.id === response.data.data.id + ) + this.notes[pos] = data.notes + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteNote(id) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/notes/${id}`) + .then((response) => { + let index = this.notes.findIndex((note) => note.id === id) + this.notes.splice(index, 1) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/payment.js b/crater/resources/scripts/admin/stores/payment.js new file mode 100644 index 0000000..b6fb0fb --- /dev/null +++ b/crater/resources/scripts/admin/stores/payment.js @@ -0,0 +1,433 @@ +import axios from 'axios' +import moment from 'moment' +import { defineStore } from 'pinia' +import { useRoute } from 'vue-router' +import { useCompanyStore } from './company' +import { useNotificationStore } from '@/scripts/stores/notification' +import paymentStub from '../stub/payment' +import { handleError } from '@/scripts/helpers/error-handling' + +export const usePaymentStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'payment', + + state: () => ({ + payments: [], + paymentTotalCount: 0, + + selectAllField: false, + selectedPayments: [], + selectedNote: null, + showExchangeRate: false, + drivers: [], + providers: [], + + paymentProviders: { + id: null, + name: '', + driver: '', + active: false, + settings: { + key: '', + secret: '', + }, + }, + + currentPayment: { + ...paymentStub, + }, + + paymentModes: [], + currentPaymentMode: { + id: '', + name: null, + }, + + isFetchingInitialData: false, + }), + + getters: { + isEdit: (state) => (state.paymentProviders.id ? true : false), + }, + + actions: { + fetchPaymentInitialData(isEdit) { + const companyStore = useCompanyStore() + const route = useRoute() + + this.isFetchingInitialData = true + + let actions = [] + if (isEdit) { + actions = [this.fetchPayment(route.params.id)] + } + Promise.all([ + this.fetchPaymentModes({ limit: 'all' }), + this.getNextNumber(), + ...actions, + ]) + .then(async ([res1, res2, res3]) => { + if (isEdit) { + if (res3.data.data.invoice) { + this.currentPayment.maxPayableAmount = parseInt( + res3.data.data.invoice.due_amount + ) + } + } + + // On Create + else if (!isEdit && res2.data) { + this.currentPayment.payment_date = moment().format('YYYY-MM-DD') + this.currentPayment.payment_number = res2.data.nextNumber + this.currentPayment.currency = + companyStore.selectedCompanyCurrency + } + + this.isFetchingInitialData = false + }) + .catch((err) => { + handleError(err) + }) + }, + + fetchPayments(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payments`, { params }) + .then((response) => { + this.payments = response.data.data + this.paymentTotalCount = response.data.meta.payment_total_count + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchPayment(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payments/${id}`) + .then((response) => { + Object.assign(this.currentPayment, response.data.data) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addPayment(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/payments', data) + .then((response) => { + this.payments.push(response.data) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('payments.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updatePayment(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/payments/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.payments.findIndex( + (payment) => payment.id === response.data.data.id + ) + + this.payments[pos] = data.payment + + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('payments.updated_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deletePayment(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/payments/delete`, id) + .then((response) => { + let index = this.payments.findIndex( + (payment) => payment.id === id + ) + this.payments.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.t('payments.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultiplePayments() { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/payments/delete`, { ids: this.selectedPayments }) + .then((response) => { + this.selectedPayments.forEach((payment) => { + let index = this.payments.findIndex( + (_payment) => _payment.id === payment.id + ) + this.payments.splice(index, 1) + }) + notificationStore.showNotification({ + type: 'success', + message: global.tc('payments.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setSelectAllState(data) { + this.selectAllField = data + }, + + selectPayment(data) { + this.selectedPayments = data + if (this.selectedPayments.length === this.payments.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllPayments() { + if (this.selectedPayments.length === this.payments.length) { + this.selectedPayments = [] + this.selectAllField = false + } else { + let allPaymentIds = this.payments.map((payment) => payment.id) + this.selectedPayments = allPaymentIds + this.selectAllField = true + } + }, + + selectNote(data) { + this.selectedNote = null + this.selectedNote = data + }, + + resetSelectedNote(data) { + this.selectedNote = null + }, + + searchPayment(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payments`, { params }) + .then((response) => { + this.payments = response.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + previewPayment(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payments/${params.id}/send/preview`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + sendEmail(data) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/payments/${data.id}/send`, data) + .then((response) => { + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('payments.send_payment_successfully'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + getNextNumber(params, setState = false) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/next-number?key=payment`, { params }) + .then((response) => { + if (setState) { + this.currentPayment.payment_number = response.data.nextNumber + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + resetCurrentPayment() { + this.currentPayment = { + ...paymentStub, + } + }, + + fetchPaymentModes(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payment-methods`, { params }) + .then((response) => { + this.paymentModes = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchPaymentMode(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/payment-methods/${id}`) + .then((response) => { + this.currentPaymentMode = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addPaymentMode(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/payment-methods`, data) + .then((response) => { + this.paymentModes.push(response.data.data) + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.payment_modes.payment_mode_added'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updatePaymentMode(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/payment-methods/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.paymentModes.findIndex( + (paymentMode) => paymentMode.id === response.data.data.id + ) + this.paymentModes[pos] = data.paymentModes + notificationStore.showNotification({ + type: 'success', + message: global.t( + 'settings.payment_modes.payment_mode_updated' + ), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deletePaymentMode(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/payment-methods/${id}`) + .then((response) => { + let index = this.paymentModes.findIndex( + (paymentMode) => paymentMode.id === id + ) + this.paymentModes.splice(index, 1) + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.payment_modes.deleted_message'), + }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/recurring-invoice.js b/crater/resources/scripts/admin/stores/recurring-invoice.js new file mode 100644 index 0000000..ece0107 --- /dev/null +++ b/crater/resources/scripts/admin/stores/recurring-invoice.js @@ -0,0 +1,460 @@ +import { defineStore } from 'pinia' +import axios from 'axios' +import recurringInvoiceStub from '@/scripts/admin/stub/recurring-invoice' +import recurringInvoiceItemStub from '@/scripts/admin/stub/recurring-invoice-item' +import TaxStub from '../stub/tax' +import { useRoute } from 'vue-router' +import { useCompanyStore } from './company' +import { useItemStore } from './item' +import { useTaxTypeStore } from './tax-type' +import { useCustomerStore } from './customer' +import Guid from 'guid' +import { handleError } from '@/scripts/helpers/error-handling' +import moment from 'moment' +import _ from 'lodash' +import { useInvoiceStore } from './invoice' +import { useNotificationStore } from '@/scripts/stores/notification' + +export const useRecurringInvoiceStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'recurring-invoice', + + state: () => ({ + templates: [], + recurringInvoices: [], + selectedRecurringInvoices: [], + totalRecurringInvoices: 0, + isFetchingInitialSettings: false, + isFetchingViewData: false, + showExchangeRate: false, + selectAllField: false, + newRecurringInvoice: { + ...recurringInvoiceStub(), + }, + + frequencies: [ + { label: 'Every Minute', value: '* * * * *' }, + { label: 'Every 30 Minute', value: '*/30 * * * *' }, + { label: 'Every Hour', value: '0 * * * *' }, + { label: 'Every 2 Hour', value: '0 */2 * * *' }, + { label: 'Every day at midnight ', value: '0 0 * * *' }, + { label: 'Every Week', value: '0 0 * * 0' }, + { label: 'Every 15 days at midnight', value: '0 5 */15 * *' }, + { label: 'On the first day of every month at 00:00', value: '0 0 1 * *' }, + { label: 'Every 6 Month', value: '0 0 1 */6 *' }, + { label: 'Every year on the first day of january at 00:00', value: '0 0 1 1 *' }, + { label: 'Custom', value: 'CUSTOM' }, + ], + }), + + getters: { + getSubTotal() { + return ( + this.newRecurringInvoice?.items.reduce(function (a, b) { + return a + b['total'] + }, 0) || 0 + ) + }, + + getTotalSimpleTax() { + return _.sumBy(this.newRecurringInvoice.taxes, function (tax) { + if (!tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalCompoundTax() { + return _.sumBy(this.newRecurringInvoice.taxes, function (tax) { + if (tax.compound_tax) { + return tax.amount + } + return 0 + }) + }, + + getTotalTax() { + if ( + this.newRecurringInvoice.tax_per_item === 'NO' || + this.newRecurringInvoice.tax_per_item === null + ) { + return this.getTotalSimpleTax + this.getTotalCompoundTax + } + return _.sumBy(this.newRecurringInvoice.items, function (tax) { + return tax.tax + }) + }, + + getSubtotalWithDiscount() { + return this.getSubTotal - this.newRecurringInvoice.discount_val + }, + + getTotal() { + return this.getSubtotalWithDiscount + this.getTotalTax + }, + }, + + actions: { + resetCurrentRecurringInvoice() { + this.newRecurringInvoice = { + ...recurringInvoiceStub(), + } + }, + + deselectItem(index) { + this.newRecurringInvoice.items[index] = { + ...recurringInvoiceItemStub, + id: Guid.raw(), + taxes: [{ ...TaxStub, id: Guid.raw() }], + } + }, + + addRecurringInvoice(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/recurring-invoices', data) + .then((response) => { + this.recurringInvoices = [ + ...this.recurringInvoices, + response.data.recurringInvoice, + ] + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('recurring_invoices.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchRecurringInvoice(id) { + return new Promise((resolve, reject) => { + this.isFetchingViewData = true + axios + .get(`/api/v1/recurring-invoices/${id}`) + .then((response) => { + Object.assign(this.newRecurringInvoice, response.data.data) + this.newRecurringInvoice.invoices = + response.data.data.invoices || [] + this.setSelectedFrequency() + this.isFetchingViewData = false + resolve(response) + }) + .catch((err) => { + this.isFetchingViewData = false + handleError(err) + reject(err) + }) + }) + }, + + updateRecurringInvoice(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/recurring-invoices/${data.id}`, data) + .then((response) => { + resolve(response) + + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('recurring_invoices.updated_message'), + }) + + let pos = this.recurringInvoices.findIndex( + (invoice) => invoice.id === response.data.data.id + ) + + this.recurringInvoices[pos] = response.data.data + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + selectCustomer(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/customers/${id}`) + .then((response) => { + this.newRecurringInvoice.customer = response.data.data + this.newRecurringInvoice.customer_id = response.data.data.id + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + searchRecurringInvoice(data) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/recurring-invoices?${data}`) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchRecurringInvoices(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/recurring-invoices`, { params }) + .then((response) => { + this.recurringInvoices = response.data.data + this.totalRecurringInvoices = + response.data.meta.recurring_invoice_total_count + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteRecurringInvoice(id) { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/recurring-invoices/delete`, id) + .then((response) => { + let index = this.recurringInvoices.findIndex( + (invoice) => invoice.id === id + ) + this.recurringInvoices.splice(index, 1) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleRecurringInvoices(id) { + return new Promise((resolve, reject) => { + let ids = this.selectedRecurringInvoices + if (id) { + ids = [id] + } + axios + .post(`/api/v1/recurring-invoices/delete`, { + ids: ids, + }) + .then((response) => { + this.selectedRecurringInvoices.forEach((invoice) => { + let index = this.recurringInvoices.findIndex( + (_inv) => _inv.id === invoice.id + ) + this.recurringInvoices.splice(index, 1) + }) + this.selectedRecurringInvoices = [] + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + resetSelectedCustomer() { + this.newRecurringInvoice.customer = null + this.newRecurringInvoice.customer_id = '' + }, + + selectRecurringInvoice(data) { + this.selectedRecurringInvoices = data + if ( + this.selectedRecurringInvoices.length === + this.recurringInvoices.length + ) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllRecurringInvoices() { + if ( + this.selectedRecurringInvoices.length === + this.recurringInvoices.length + ) { + this.selectedRecurringInvoices = [] + this.selectAllField = false + } else { + let allInvoiceIds = this.recurringInvoices.map( + (invoice) => invoice.id + ) + this.selectedRecurringInvoices = allInvoiceIds + this.selectAllField = true + } + }, + + addItem() { + this.newRecurringInvoice.items.push({ + ...recurringInvoiceItemStub, + id: Guid.raw(), + taxes: [{ ...TaxStub, id: Guid.raw() }], + }) + }, + + removeItem(index) { + this.newRecurringInvoice.items.splice(index, 1) + }, + + updateItem(data) { + Object.assign(this.newRecurringInvoice.items[data.index], { ...data }) + }, + + async fetchRecurringInvoiceInitialSettings(isEdit) { + const companyStore = useCompanyStore() + const customerStore = useCustomerStore() + const itemStore = useItemStore() + const invoiceStore = useInvoiceStore() + const taxTypeStore = useTaxTypeStore() + const route = useRoute() + + this.isFetchingInitialSettings = true + this.newRecurringInvoice.currency = companyStore.selectedCompanyCurrency + + if (route.query.customer) { + let response = await customerStore.fetchCustomer(route.query.customer) + this.newRecurringInvoice.customer = response.data.data + this.selectCustomer(response.data.data.id) + } + + let editActions = [] + + // on create + if (!isEdit) { + this.newRecurringInvoice.tax_per_item = + companyStore.selectedCompanySettings.tax_per_item + this.newRecurringInvoice.discount_per_item = + companyStore.selectedCompanySettings.discount_per_item + this.newRecurringInvoice.sales_tax_type = companyStore.selectedCompanySettings.sales_tax_type + this.newRecurringInvoice.sales_tax_address_type = companyStore.selectedCompanySettings.sales_tax_address_type + this.newRecurringInvoice.starts_at = moment().format('YYYY-MM-DD') + this.newRecurringInvoice.next_invoice_date = moment() + .add(7, 'days') + .format('YYYY-MM-DD') + } else { + editActions = [this.fetchRecurringInvoice(route.params.id)] + } + + Promise.all([ + itemStore.fetchItems({ + filter: {}, + orderByField: '', + orderBy: '', + }), + this.resetSelectedNote(), + invoiceStore.fetchInvoiceTemplates(), + taxTypeStore.fetchTaxTypes({ limit: 'all' }), + ...editActions, + ]) + .then(async ([res1, res2, res3, res4, res5]) => { + if (res3.data) { + this.templates = invoiceStore.templates + } + + if (!isEdit) { + this.setTemplate(this.templates[0].name) + } + + if (isEdit && res5?.data) { + let data = { + ...res5.data.data, + } + + this.setTemplate(res5?.data?.data?.template_name) + } + if (isEdit) { + this.addSalesTaxUs() + } + this.isFetchingInitialSettings = false + }) + .catch((err) => { + console.log(err); + handleError(err) + }) + }, + + addSalesTaxUs() { + const taxTypeStore = useTaxTypeStore() + let salesTax = { ...TaxStub } + let found = this.newRecurringInvoice.taxes.find((_t) => _t.name === 'Sales Tax' && _t.type === 'MODULE') + if (found) { + for (const key in found) { + if (Object.prototype.hasOwnProperty.call(salesTax, key)) { + salesTax[key] = found[key] + } + } + salesTax.id = found.tax_type_id + taxTypeStore.taxTypes.push(salesTax) + } + }, + + setTemplate(data) { + this.newRecurringInvoice.template_name = data + }, + + setSelectedFrequency() { + let data = this.frequencies.find( + (frequency) => { + return frequency.value === this.newRecurringInvoice.frequency + } + ) + data ? this.newRecurringInvoice.selectedFrequency = data + : this.newRecurringInvoice.selectedFrequency = { label: 'Custom', value: 'CUSTOM' } + + }, + + resetSelectedNote() { + this.newRecurringInvoice.selectedNote = null + }, + + fetchRecurringInvoiceFrequencyDate(params) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/recurring-invoice-frequency', { params }) + .then((response) => { + this.newRecurringInvoice.next_invoice_at = + response.data.next_invoice_at + + resolve(response) + }) + .catch((err) => { + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'error', + message: global.t('errors.enter_valid_cron_format'), + }) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/reset.js b/crater/resources/scripts/admin/stores/reset.js new file mode 100644 index 0000000..e5dff5a --- /dev/null +++ b/crater/resources/scripts/admin/stores/reset.js @@ -0,0 +1,91 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useBackupStore } from './backup' +import { useCategoryStore } from './category' +import { useCompanyStore } from './company' +import { useCustomFieldStore } from './custom-field' +import { useCustomerStore } from './customer' +import { useDashboardStore } from './dashboard' +import { useDialogStore } from '@/scripts/stores/dialog' +import { useDiskStore } from './disk' +import { useEstimateStore } from './estimate' +import { useExchangeRateStore } from './exchange-rate' +import { useExpenseStore } from './expense' +import { useGlobalStore } from './global' +import { useInstallationStore } from './installation' +import { useInvoiceStore } from './invoice' +import { useItemStore } from './item' +import { useMailDriverStore } from './mail-driver' +import { useModalStore } from '@/scripts/stores/modal' +import { useNotesStore } from './note' +import { useNotificationStore } from '@/scripts/stores/notification' +import { usePaymentStore } from './payment' +import { useRecurringInvoiceStore } from './recurring-invoice' +import { useRoleStore } from './role' +import { useTaxTypeStore } from './tax-type' +import { useUserStore } from './user' +import { useUsersStore } from './users' + +export const useResetStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'reset', + actions: { + clearPinia() { + const backupStore = useBackupStore() + const categoryStore = useCategoryStore() + const companyStore = useCompanyStore() + const customFieldStore = useCustomFieldStore() + const customerStore = useCustomerStore() + const dashboardStore = useDashboardStore() + const dialogStore = useDialogStore() + const diskStore = useDiskStore() + const estimateStore = useEstimateStore() + const exchangeRateStore = useExchangeRateStore() + const expenseStore = useExpenseStore() + const globalStore = useGlobalStore() + const installationStore = useInstallationStore() + const invoiceStore = useInvoiceStore() + const itemStore = useItemStore() + const mailDriverStore = useMailDriverStore() + const modalStore = useModalStore() + const noteStore = useNotesStore() + const notificationStore = useNotificationStore() + const paymentStore = usePaymentStore() + const recurringInvoiceStore = useRecurringInvoiceStore() + const roleStore = useRoleStore() + const taxTypeStore = useTaxTypeStore() + const userStore = useUserStore() + const usersStore = useUsersStore() + + backupStore.$reset() + categoryStore.$reset() + companyStore.$reset() + customFieldStore.$reset() + customerStore.$reset() + dashboardStore.$reset() + dialogStore.$reset() + diskStore.$reset() + estimateStore.$reset() + exchangeRateStore.$reset() + expenseStore.$reset() + globalStore.$reset() + installationStore.$reset() + invoiceStore.$reset() + itemStore.$reset() + mailDriverStore.$reset() + modalStore.$reset() + noteStore.$reset() + notificationStore.$reset() + paymentStore.$reset() + recurringInvoiceStore.$reset() + roleStore.$reset() + taxTypeStore.$reset() + userStore.$reset() + usersStore.$reset() + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/role.js b/crater/resources/scripts/admin/stores/role.js new file mode 100644 index 0000000..dce94e2 --- /dev/null +++ b/crater/resources/scripts/admin/stores/role.js @@ -0,0 +1,169 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import _ from 'lodash' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useRoleStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'role', + state: () => ({ + roles: [], + allAbilities: [], + selectedRoles: [], + currentRole: { + id: null, + name: '', + abilities: [], + }, + }), + + getters: { + isEdit: (state) => (state.currentRole.id ? true : false), + abilitiesList: (state) => { + let abilities = state.allAbilities.map((a) => ({ + modelName: a.model + ? a.model.substring(a.model.lastIndexOf('\\') + 1) + : 'Common', + disabled: false, + ...a, + })) + return _.groupBy(abilities, 'modelName') + }, + }, + + actions: { + fetchRoles(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/roles`, { params }) + .then((response) => { + this.roles = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchRole(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/roles/${id}`) + .then((response) => { + this.currentRole.name = response.data.data.name + this.currentRole.id = response.data.data.id + + response.data.data.abilities.forEach((_ra) => { + for (const property in this.abilitiesList) { + this.abilitiesList[property].forEach((_p) => { + if (_p.ability === _ra.name) { + this.currentRole.abilities.push(_p) + } + }) + } + }) + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addRole(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post('/api/v1/roles', data) + .then((response) => { + this.roles.push(response.data.role) + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.roles.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateRole(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/roles/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.roles.findIndex( + (role) => role.id === response.data.data.id + ) + this.roles[pos] = data.role + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.roles.updated_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchAbilities(params) { + return new Promise((resolve, reject) => { + if (this.allAbilities.length) { + resolve(this.allAbilities) + } else { + axios + .get(`/api/v1/abilities`, { params }) + .then((response) => { + this.allAbilities = response.data.abilities + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + } + }) + }, + + deleteRole(id) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/roles/${id}`) + .then((response) => { + let index = this.roles.findIndex((role) => role.id === id) + this.roles.splice(index, 1) + + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.roles.deleted_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/tax-type.js b/crater/resources/scripts/admin/stores/tax-type.js new file mode 100644 index 0000000..945e586 --- /dev/null +++ b/crater/resources/scripts/admin/stores/tax-type.js @@ -0,0 +1,164 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useTaxTypeStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'taxType', + + state: () => ({ + taxTypes: [], + currentTaxType: { + id: null, + name: '', + percent: 0, + description: '', + compound_tax: false, + collective_tax: 0, + }, + }), + + getters: { + isEdit: (state) => (state.currentTaxType.id ? true : false), + }, + + actions: { + resetCurrentTaxType() { + this.currentTaxType = { + id: null, + name: '', + percent: 0, + description: '', + compound_tax: false, + collective_tax: 0, + } + }, + + fetchTaxTypes(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/tax-types`, { params }) + .then((response) => { + this.taxTypes = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchTaxType(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/tax-types/${id}`) + .then((response) => { + this.currentTaxType = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addTaxType(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post('/api/v1/tax-types', data) + .then((response) => { + this.taxTypes.push(response.data.data) + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.tax_types.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateTaxType(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/tax-types/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.taxTypes.findIndex( + (taxTypes) => taxTypes.id === response.data.data.id + ) + this.taxTypes[pos] = data.taxTypes + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.tax_types.updated_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchSalesTax(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/m/sales-tax-us/current-tax', data) + .then((response) => { + if (response.data) { + let pos = this.taxTypes.findIndex( + (_t) => _t.name === 'SalesTaxUs' + ) + pos > -1 ? this.taxTypes.splice(pos, 1) : '' + this.taxTypes.push({ ...response.data.data, tax_type_id: response.data.data.id }) + } + + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteTaxType(id) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/tax-types/${id}`) + .then((response) => { + if (response.data.success) { + let index = this.taxTypes.findIndex( + (taxType) => taxType.id === id + ) + this.taxTypes.splice(index, 1) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.tax_types.deleted_message'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/user.js b/crater/resources/scripts/admin/stores/user.js new file mode 100644 index 0000000..d05c962 --- /dev/null +++ b/crater/resources/scripts/admin/stores/user.js @@ -0,0 +1,155 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useUserStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'user', + + state: () => ({ + currentUser: null, + currentAbilities: [], + currentUserSettings: {}, + + userForm: { + name: '', + email: '', + password: '', + confirm_password: '', + language: '', + }, + }), + + getters: { + currentAbilitiesCount: (state) => state.currentAbilities.length, + }, + + actions: { + updateCurrentUser(data) { + return new Promise((resolve, reject) => { + axios + .put('/api/v1/me', data) + .then((response) => { + this.currentUser = response.data.data + Object.assign(this.userForm, response.data.data) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('settings.account_settings.updated_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchCurrentUser(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/me`, params) + .then((response) => { + this.currentUser = response.data.data + this.userForm = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + uploadAvatar(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/me/upload-avatar', data) + .then((response) => { + this.currentUser.avatar = response.data.data.avatar + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchUserSettings(settings) { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/me/settings', { + params: { + settings, + }, + }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateUserSettings(data) { + return new Promise((resolve, reject) => { + axios + .put('/api/v1/me/settings', data) + .then((response) => { + if (data.settings.language) { + this.currentUserSettings.language = data.settings.language + global.locale = data.settings.language + } + if (data.settings.default_estimate_template) { + this.currentUserSettings.default_estimate_template = + data.settings.default_estimate_template + } + if (data.settings.default_invoice_template) { + this.currentUserSettings.default_invoice_template = + data.settings.default_invoice_template + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + hasAbilities(abilities) { + return !!this.currentAbilities.find((ab) => { + if (ab.name === '*') return true + if (typeof abilities === 'string') { + return ab.name === abilities + } + return !!abilities.find((p) => { + return ab.name === p + }) + }) + }, + + hasAllAbilities(abilities) { + let isAvailable = true + this.currentAbilities.filter((ab) => { + let hasContain = !!abilities.find((p) => { + return ab.name === p + }) + if (!hasContain) { + isAvailable = false + } + }) + + return isAvailable + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stores/users.js b/crater/resources/scripts/admin/stores/users.js new file mode 100644 index 0000000..30a5bfd --- /dev/null +++ b/crater/resources/scripts/admin/stores/users.js @@ -0,0 +1,232 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' + +export const useUsersStore = (useWindow = false) => { + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'users', + state: () => ({ + roles: [], + users: [], + totalUsers: 0, + currentUser: null, + selectAllField: false, + selectedUsers: [], + customerList: [], + userList: [], + + userData: { + name: '', + email: '', + password: null, + phone: null, + companies: [], + }, + }), + + actions: { + resetUserData() { + this.userData = { + name: '', + email: '', + password: null, + phone: null, + role: null, + companies: [], + } + }, + + fetchUsers(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/users`, { params }) + .then((response) => { + this.users = response.data.data + this.totalUsers = response.data.meta.total + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchUser(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/users/${id}`) + .then((response) => { + this.userData = response.data.data + if (this.userData?.companies?.length) { + this.userData.companies.forEach((c, i) => { + this.userData.roles.forEach((r) => { + if (r.scope === c.id) + this.userData.companies[i].role = r.name + }) + }) + } + resolve(response) + }) + .catch((err) => { + console.log(err) + handleError(err) + reject(err) + }) + }) + }, + + fetchRoles(state) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/roles`) + .then((response) => { + this.roles = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addUser(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/users', data) + .then((response) => { + this.users.push(response.data) + const notificationStore = useNotificationStore() + + notificationStore.showNotification({ + type: 'success', + message: global.t('users.created_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateUser(data) { + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/users/${data.id}`, data) + .then((response) => { + if (response) { + let pos = this.users.findIndex( + (user) => user.id === response.data.data.id + ) + this.users[pos] = response.data.data + } + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t('users.updated_message'), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteUser(id) { + const notificationStore = useNotificationStore() + + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/users/delete`, { users: id.ids }) + .then((response) => { + let index = this.users.findIndex((user) => user.id === id) + this.users.splice(index, 1) + notificationStore.showNotification({ + type: 'success', + message: global.tc('users.deleted_message', 1), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMultipleUsers() { + return new Promise((resolve, reject) => { + axios + .post(`/api/v1/users/delete`, { users: this.selectedUsers }) + .then((response) => { + this.selectedUsers.forEach((user) => { + let index = this.users.findIndex( + (_user) => _user.id === user.id + ) + this.users.splice(index, 1) + }) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.tc('users.deleted_message', 2), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + searchUsers(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/search`, { params }) + .then((response) => { + this.userList = response.data.users.data + this.customerList = response.data.customers.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + setSelectAllState(data) { + this.selectAllField = data + }, + + selectUser(data) { + this.selectedUsers = data + if (this.selectedUsers.length === this.users.length) { + this.selectAllField = true + } else { + this.selectAllField = false + } + }, + + selectAllUsers() { + if (this.selectedUsers.length === this.users.length) { + this.selectedUsers = [] + this.selectAllField = false + } else { + let allUserIds = this.users.map((user) => user.id) + this.selectedUsers = allUserIds + this.selectAllField = true + } + }, + }, + })() +} diff --git a/crater/resources/scripts/admin/stub/abilities.js b/crater/resources/scripts/admin/stub/abilities.js new file mode 100644 index 0000000..024e999 --- /dev/null +++ b/crater/resources/scripts/admin/stub/abilities.js @@ -0,0 +1,79 @@ +export default { + DASHBOARD: 'dashboard', + + // customers + CREATE_CUSTOMER: 'create-customer', + DELETE_CUSTOMER: 'delete-customer', + EDIT_CUSTOMER: 'edit-customer', + VIEW_CUSTOMER: 'view-customer', + + // Items + CREATE_ITEM: 'create-item', + DELETE_ITEM: 'delete-item', + EDIT_ITEM: 'edit-item', + VIEW_ITEM: 'view-item', + + // Tax Types + CREATE_TAX_TYPE: 'create-tax-type', + DELETE_TAX_TYPE: 'delete-tax-type', + EDIT_TAX_TYPE: 'edit-tax-type', + VIEW_TAX_TYPE: 'view-tax-type', + + // Estimates + CREATE_ESTIMATE: 'create-estimate', + DELETE_ESTIMATE: 'delete-estimate', + EDIT_ESTIMATE: 'edit-estimate', + VIEW_ESTIMATE: 'view-estimate', + SEND_ESTIMATE: 'send-estimate', + + // Invoices + CREATE_INVOICE: 'create-invoice', + DELETE_INVOICE: 'delete-invoice', + EDIT_INVOICE: 'edit-invoice', + VIEW_INVOICE: 'view-invoice', + SEND_INVOICE: 'send-invoice', + + // Recurring Invoices + CREATE_RECURRING_INVOICE: 'create-recurring-invoice', + DELETE_RECURRING_INVOICE: 'delete-recurring-invoice', + EDIT_RECURRING_INVOICE: 'edit-recurring-invoice', + VIEW_RECURRING_INVOICE: 'view-recurring-invoice', + + // Payment + CREATE_PAYMENT: 'create-payment', + DELETE_PAYMENT: 'delete-payment', + EDIT_PAYMENT: 'edit-payment', + VIEW_PAYMENT: 'view-payment', + SEND_PAYMENT: 'send-payment', + + // Payment + CREATE_EXPENSE: 'create-expense', + DELETE_EXPENSE: 'delete-expense', + EDIT_EXPENSE: 'edit-expense', + VIEW_EXPENSE: 'view-expense', + + // Custom fields + CREATE_CUSTOM_FIELDS: 'create-custom-field', + DELETE_CUSTOM_FIELDS: 'delete-custom-field', + EDIT_CUSTOM_FIELDS: 'edit-custom-field', + VIEW_CUSTOM_FIELDS: 'view-custom-field', + + // Roles + CREATE_ROLE: 'create-role', + DELETE_ROLE: 'delete-role', + EDIT_ROLE: 'edit-role', + VIEW_ROLE: 'view-role', + + // exchange rates + VIEW_EXCHANGE_RATE: 'view-exchange-rate-provider', + CREATE_EXCHANGE_RATE: 'create-exchange-rate-provider', + EDIT_EXCHANGE_RATE: 'edit-exchange-rate-provider', + DELETE_EXCHANGE_RATE: 'delete-exchange-rate-provider', + + // Reports + VIEW_FINANCIAL_REPORT: 'view-financial-reports', + + // settings + MANAGE_NOTE: 'manage-all-notes', + VIEW_NOTE: 'view-all-notes', +} diff --git a/crater/resources/scripts/admin/stub/address.js b/crater/resources/scripts/admin/stub/address.js new file mode 100644 index 0000000..cb5f067 --- /dev/null +++ b/crater/resources/scripts/admin/stub/address.js @@ -0,0 +1,11 @@ +export default { + name: null, + phone: null, + address_street_1: null, + address_street_2: null, + city: null, + state: null, + country_id: null, + zip: null, + type: null, +} diff --git a/crater/resources/scripts/admin/stub/custom-field.js b/crater/resources/scripts/admin/stub/custom-field.js new file mode 100644 index 0000000..af04dac --- /dev/null +++ b/crater/resources/scripts/admin/stub/custom-field.js @@ -0,0 +1,12 @@ +export default { + id: null, + label: null, + type: null, + name: null, + default_answer: null, + is_required: false, + placeholder: null, + model_type: null, + order: 1, + options: [], +} diff --git a/crater/resources/scripts/admin/stub/customer.js b/crater/resources/scripts/admin/stub/customer.js new file mode 100644 index 0000000..55512ad --- /dev/null +++ b/crater/resources/scripts/admin/stub/customer.js @@ -0,0 +1,19 @@ +import addressStub from '@/scripts/admin/stub/address.js' + +export default function () { + return { + name: '', + contact_name: '', + email: '', + phone: null, + password: '', + confirm_password:'', + currency_id: null, + website: null, + billing: { ...addressStub }, + shipping: { ...addressStub }, + customFields: [], + fields: [], + enable_portal: false, + } +} diff --git a/crater/resources/scripts/admin/stub/estimate-item.js b/crater/resources/scripts/admin/stub/estimate-item.js new file mode 100644 index 0000000..3cd1a3b --- /dev/null +++ b/crater/resources/scripts/admin/stub/estimate-item.js @@ -0,0 +1,19 @@ +export default { + estimate_id: null, + item_id: null, + name: '', + title: '', + description: null, + quantity: 1, + price: 0, + discount_type: 'fixed', + discount_val: 0, + discount: 0, + total: 0, + sub_total: 0, + totalTax: 0, + totalSimpleTax: 0, + totalCompoundTax: 0, + tax: 0, + taxes: [], +} diff --git a/crater/resources/scripts/admin/stub/estimate.js b/crater/resources/scripts/admin/stub/estimate.js new file mode 100644 index 0000000..15968ed --- /dev/null +++ b/crater/resources/scripts/admin/stub/estimate.js @@ -0,0 +1,39 @@ +import Guid from 'guid' +import estimateItemStub from './estimate-item' +import taxStub from './tax' + +export default function () { + return { + id: null, + customer: null, + template_name: '', + tax_per_item: null, + sales_tax_type: null, + sales_tax_address_type: null, + discount_per_item: null, + estimate_date: '', + expiry_date: '', + estimate_number: '', + customer_id: null, + sub_total: 0, + total: 0, + tax: 0, + notes: '', + discount_type: 'fixed', + discount_val: 0, + reference_number: null, + discount: 0, + items: [ + { + ...estimateItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + }, + ], + taxes: [], + customFields: [], + fields: [], + selectedNote: null, + selectedCurrency: '', + } +} diff --git a/crater/resources/scripts/admin/stub/expense.js b/crater/resources/scripts/admin/stub/expense.js new file mode 100644 index 0000000..98e945a --- /dev/null +++ b/crater/resources/scripts/admin/stub/expense.js @@ -0,0 +1,17 @@ +import moment from 'moment' + +export default { + expense_category_id: null, + expense_date: moment().format('YYYY-MM-DD'), + amount: 100, + notes: '', + attachment_receipt: null, + customer_id: '', + currency_id: '', + payment_method_id: '', + receiptFiles: [], + customFields: [], + fields: [], + in_use: false, + selectedCurrency: null +} diff --git a/crater/resources/scripts/admin/stub/invoice-item.js b/crater/resources/scripts/admin/stub/invoice-item.js new file mode 100644 index 0000000..fdb0ced --- /dev/null +++ b/crater/resources/scripts/admin/stub/invoice-item.js @@ -0,0 +1,18 @@ +export default { + invoice_id: null, + item_id: null, + name: '', + title: '', + description: null, + quantity: 1, + price: 0, + discount_type: 'fixed', + discount_val: 0, + discount: 0, + total: 0, + totalTax: 0, + totalSimpleTax: 0, + totalCompoundTax: 0, + tax: 0, + taxes: [], +} diff --git a/crater/resources/scripts/admin/stub/invoice.js b/crater/resources/scripts/admin/stub/invoice.js new file mode 100644 index 0000000..665c877 --- /dev/null +++ b/crater/resources/scripts/admin/stub/invoice.js @@ -0,0 +1,39 @@ +import Guid from 'guid' +import invoiceItemStub from './invoice-item' +import taxStub from './tax' + +export default function () { + return { + id: null, + invoice_number: '', + customer: null, + customer_id: null, + template_name: null, + invoice_date: '', + due_date: '', + notes: '', + discount: 0, + discount_type: 'fixed', + discount_val: 0, + reference_number: null, + tax: 0, + sub_total: 0, + total: 0, + tax_per_item: null, + sales_tax_type: null, + sales_tax_address_type: null, + discount_per_item: null, + taxes: [], + items: [ + { + ...invoiceItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + }, + ], + customFields: [], + fields: [], + selectedNote: null, + selectedCurrency: '', + } +} diff --git a/crater/resources/scripts/admin/stub/payment.js b/crater/resources/scripts/admin/stub/payment.js new file mode 100644 index 0000000..ca04450 --- /dev/null +++ b/crater/resources/scripts/admin/stub/payment.js @@ -0,0 +1,15 @@ +export default { + maxPayableAmount: Number.MAX_SAFE_INTEGER, + selectedCustomer: '', + currency: null, + currency_id: '', + customer_id: '', + payment_number: '', + payment_date: '', + amount: 0, + invoice_id: '', + notes: '', + payment_method_id: '', + customFields: [], + fields: [] +} diff --git a/crater/resources/scripts/admin/stub/recurring-invoice-item.js b/crater/resources/scripts/admin/stub/recurring-invoice-item.js new file mode 100644 index 0000000..78abb27 --- /dev/null +++ b/crater/resources/scripts/admin/stub/recurring-invoice-item.js @@ -0,0 +1,20 @@ +export default { + recurring_invoice_id: null, + item_id: null, + name: '', + title: '', + sales_tax_type: null, + sales_tax_address_type: null, + description: null, + quantity: 1, + price: 0, + discount_type: 'fixed', + discount_val: 0, + discount: 0, + total: 0, + totalTax: 0, + totalSimpleTax: 0, + totalCompoundTax: 0, + tax: 0, + taxes: [], +} diff --git a/crater/resources/scripts/admin/stub/recurring-invoice.js b/crater/resources/scripts/admin/stub/recurring-invoice.js new file mode 100644 index 0000000..8fbe7f6 --- /dev/null +++ b/crater/resources/scripts/admin/stub/recurring-invoice.js @@ -0,0 +1,48 @@ +import Guid from 'guid' +import recurringInvoiceItemStub from './recurring-invoice-item' +import taxStub from './tax' + +export default function () { + return { + currency: null, + customer: null, + + customer_id: null, + invoice_template_id: 1, + sub_total: 0, + total: 0, + tax: 0, + notes: '', + discount_type: 'fixed', + discount_val: 0, + discount: 0, + starts_at: null, + send_automatically: true, + status: 'ACTIVE', + company_id: null, + next_invoice_at: null, + next_invoice_date: null, + frequency: '0 0 * * 0', + limit_count: null, + limit_by: 'NONE', + limit_date: null, + exchange_rate: null, + tax_per_item: null, + discount_per_item: null, + template_name: null, + items: [ + { + ...recurringInvoiceItemStub, + id: Guid.raw(), + taxes: [{ ...taxStub, id: Guid.raw() }], + }, + ], + taxes: [], + customFields: [], + fields: [], + invoices: [], + selectedNote: null, + selectedFrequency: { label: 'Every Week', value: '0 0 * * 0' }, + selectedInvoice: null, + } +} diff --git a/crater/resources/scripts/admin/stub/tax.js b/crater/resources/scripts/admin/stub/tax.js new file mode 100644 index 0000000..1c37a18 --- /dev/null +++ b/crater/resources/scripts/admin/stub/tax.js @@ -0,0 +1,8 @@ +export default { + name: '', + tax_type_id: 0, + type: 'GENERAL', + amount: null, + percent: null, + compound_tax: false, +} diff --git a/crater/resources/scripts/admin/views/SampleTable.vue b/crater/resources/scripts/admin/views/SampleTable.vue new file mode 100644 index 0000000..93b2f1e --- /dev/null +++ b/crater/resources/scripts/admin/views/SampleTable.vue @@ -0,0 +1,175 @@ + + + diff --git a/crater/resources/scripts/admin/views/auth/ForgotPassword.vue b/crater/resources/scripts/admin/views/auth/ForgotPassword.vue new file mode 100644 index 0000000..d5bfeca --- /dev/null +++ b/crater/resources/scripts/admin/views/auth/ForgotPassword.vue @@ -0,0 +1,92 @@ + + + diff --git a/crater/resources/scripts/admin/views/auth/Login.vue b/crater/resources/scripts/admin/views/auth/Login.vue new file mode 100644 index 0000000..d3b9686 --- /dev/null +++ b/crater/resources/scripts/admin/views/auth/Login.vue @@ -0,0 +1,129 @@ + + + diff --git a/crater/resources/scripts/admin/views/auth/ResetPassword.vue b/crater/resources/scripts/admin/views/auth/ResetPassword.vue new file mode 100644 index 0000000..7f1c468 --- /dev/null +++ b/crater/resources/scripts/admin/views/auth/ResetPassword.vue @@ -0,0 +1,165 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/Create.vue b/crater/resources/scripts/admin/views/customers/Create.vue new file mode 100644 index 0000000..a40a2a6 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/Create.vue @@ -0,0 +1,756 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/Index.vue b/crater/resources/scripts/admin/views/customers/Index.vue new file mode 100644 index 0000000..9c64d38 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/Index.vue @@ -0,0 +1,368 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/View.vue b/crater/resources/scripts/admin/views/customers/View.vue new file mode 100644 index 0000000..205ebf2 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/View.vue @@ -0,0 +1,144 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/partials/CustomerChart.vue b/crater/resources/scripts/admin/views/customers/partials/CustomerChart.vue new file mode 100644 index 0000000..8b85c10 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/partials/CustomerChart.vue @@ -0,0 +1,222 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/partials/CustomerChartPlaceholder.vue b/crater/resources/scripts/admin/views/customers/partials/CustomerChartPlaceholder.vue new file mode 100644 index 0000000..29c7316 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/partials/CustomerChartPlaceholder.vue @@ -0,0 +1,79 @@ + diff --git a/crater/resources/scripts/admin/views/customers/partials/CustomerInfo.vue b/crater/resources/scripts/admin/views/customers/partials/CustomerInfo.vue new file mode 100644 index 0000000..128dcc8 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/partials/CustomerInfo.vue @@ -0,0 +1,119 @@ + + + diff --git a/crater/resources/scripts/admin/views/customers/partials/CustomerViewSidebar.vue b/crater/resources/scripts/admin/views/customers/partials/CustomerViewSidebar.vue new file mode 100644 index 0000000..e76e5d5 --- /dev/null +++ b/crater/resources/scripts/admin/views/customers/partials/CustomerViewSidebar.vue @@ -0,0 +1,329 @@ + + + diff --git a/crater/resources/scripts/admin/views/dashboard/Dashboard.vue b/crater/resources/scripts/admin/views/dashboard/Dashboard.vue new file mode 100644 index 0000000..649b388 --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/Dashboard.vue @@ -0,0 +1,28 @@ + + + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardChart.vue b/crater/resources/scripts/admin/views/dashboard/DashboardChart.vue new file mode 100644 index 0000000..cb7e0c0 --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardChart.vue @@ -0,0 +1,186 @@ + + + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardChartPlaceholder.vue b/crater/resources/scripts/admin/views/dashboard/DashboardChartPlaceholder.vue new file mode 100644 index 0000000..a537ecf --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardChartPlaceholder.vue @@ -0,0 +1,88 @@ + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardStats.vue b/crater/resources/scripts/admin/views/dashboard/DashboardStats.vue new file mode 100644 index 0000000..e54b3af --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardStats.vue @@ -0,0 +1,71 @@ + + + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardStatsItem.vue b/crater/resources/scripts/admin/views/dashboard/DashboardStatsItem.vue new file mode 100644 index 0000000..5a79bde --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardStatsItem.vue @@ -0,0 +1,64 @@ + + + + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardStatsPlaceholder.vue b/crater/resources/scripts/admin/views/dashboard/DashboardStatsPlaceholder.vue new file mode 100644 index 0000000..a114818 --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardStatsPlaceholder.vue @@ -0,0 +1,20 @@ + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardStatsSmPlaceholder.vue b/crater/resources/scripts/admin/views/dashboard/DashboardStatsSmPlaceholder.vue new file mode 100644 index 0000000..90ad7a1 --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardStatsSmPlaceholder.vue @@ -0,0 +1,31 @@ + diff --git a/crater/resources/scripts/admin/views/dashboard/DashboardTable.vue b/crater/resources/scripts/admin/views/dashboard/DashboardTable.vue new file mode 100644 index 0000000..0decb6a --- /dev/null +++ b/crater/resources/scripts/admin/views/dashboard/DashboardTable.vue @@ -0,0 +1,186 @@ + + + diff --git a/crater/resources/scripts/admin/views/errors/404.vue b/crater/resources/scripts/admin/views/errors/404.vue new file mode 100644 index 0000000..2137c65 --- /dev/null +++ b/crater/resources/scripts/admin/views/errors/404.vue @@ -0,0 +1,64 @@ + + + diff --git a/crater/resources/scripts/admin/views/estimates/Index.vue b/crater/resources/scripts/admin/views/estimates/Index.vue new file mode 100644 index 0000000..73be69b --- /dev/null +++ b/crater/resources/scripts/admin/views/estimates/Index.vue @@ -0,0 +1,487 @@ + + + diff --git a/crater/resources/scripts/admin/views/estimates/View.vue b/crater/resources/scripts/admin/views/estimates/View.vue new file mode 100644 index 0000000..a4d0f79 --- /dev/null +++ b/crater/resources/scripts/admin/views/estimates/View.vue @@ -0,0 +1,540 @@ + + + diff --git a/crater/resources/scripts/admin/views/modules/partials/ModuleCard.vue b/crater/resources/scripts/admin/views/modules/partials/ModuleCard.vue new file mode 100644 index 0000000..f22f4a8 --- /dev/null +++ b/crater/resources/scripts/admin/views/modules/partials/ModuleCard.vue @@ -0,0 +1,140 @@ + + + diff --git a/crater/resources/scripts/admin/views/modules/partials/ModuleCardPlaceholder.vue b/crater/resources/scripts/admin/views/modules/partials/ModuleCardPlaceholder.vue new file mode 100644 index 0000000..55a7236 --- /dev/null +++ b/crater/resources/scripts/admin/views/modules/partials/ModuleCardPlaceholder.vue @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/crater/resources/scripts/admin/views/modules/partials/ModulePlaceholder.vue b/crater/resources/scripts/admin/views/modules/partials/ModulePlaceholder.vue new file mode 100644 index 0000000..b5f6c93 --- /dev/null +++ b/crater/resources/scripts/admin/views/modules/partials/ModulePlaceholder.vue @@ -0,0 +1,104 @@ + diff --git a/crater/resources/scripts/admin/views/modules/partials/RecentModuleCard.vue b/crater/resources/scripts/admin/views/modules/partials/RecentModuleCard.vue new file mode 100644 index 0000000..cd5ba46 --- /dev/null +++ b/crater/resources/scripts/admin/views/modules/partials/RecentModuleCard.vue @@ -0,0 +1,63 @@ + + + diff --git a/crater/resources/scripts/admin/views/payments/Create.vue b/crater/resources/scripts/admin/views/payments/Create.vue new file mode 100644 index 0000000..3988830 --- /dev/null +++ b/crater/resources/scripts/admin/views/payments/Create.vue @@ -0,0 +1,531 @@ + + + diff --git a/crater/resources/scripts/admin/views/payments/Index.vue b/crater/resources/scripts/admin/views/payments/Index.vue new file mode 100644 index 0000000..c83a53a --- /dev/null +++ b/crater/resources/scripts/admin/views/payments/Index.vue @@ -0,0 +1,380 @@ + + + diff --git a/crater/resources/scripts/admin/views/payments/View.vue b/crater/resources/scripts/admin/views/payments/View.vue new file mode 100644 index 0000000..0b08827 --- /dev/null +++ b/crater/resources/scripts/admin/views/payments/View.vue @@ -0,0 +1,460 @@ +