Compare commits
32 Commits
634a5fe298
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a9b40bb9de | |||
| 6bd7377a3f | |||
| a96f742b52 | |||
| 56b359df87 | |||
| 02de90fa4a | |||
| 596c205f3d | |||
| 117c7fc1f8 | |||
| 1c9b4ce53f | |||
| 71c44f146f | |||
| 120f0cdc96 | |||
| b1db249a99 | |||
| fcf7e5e8c0 | |||
| b1c4c1b75f | |||
| 18069d883c | |||
| 5cdd313e5b | |||
|
|
b9cbfa0d50 | ||
| 32b244a42e | |||
| 94e5973f88 | |||
| 712baefa7f | |||
| dd6dbb6236 | |||
| 170b472545 | |||
| e2232af098 | |||
| 2a1a68e969 | |||
| cf3f5b11ac | |||
| 64ca83083e | |||
| 17af0f2015 | |||
| 6434fc195e | |||
| 7c457ba8c7 | |||
| a168fa31c0 | |||
| 4e3431e7a7 | |||
| c75f322a0c | |||
| 48b2880ea0 |
16
.browserslistrc
Normal file
16
.browserslistrc
Normal file
@@ -0,0 +1,16 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
Chrome >=79
|
||||
ChromeAndroid >=79
|
||||
Firefox >=70
|
||||
Edge >=79
|
||||
Safari >=14
|
||||
iOS >=14
|
||||
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
46
.eslintrc.json
Normal file
46
.eslintrc.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["projects/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"parserOptions": {
|
||||
"project": ["tsconfig.json"],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/component-class-suffix": [
|
||||
"error",
|
||||
{
|
||||
"suffixes": ["Page", "Component"]
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@angular-eslint/template/recommended"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
39
.gitea/workflows/build-and-deploy.yaml
Normal file
39
.gitea/workflows/build-and-deploy.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Build and Deploy develop
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
builds:
|
||||
runs-on: host
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install
|
||||
npm install -g @angular/cli
|
||||
|
||||
- name: Build Angular project
|
||||
run: ng build --configuration=production
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build -t 192.168.2.28:5050/tusha-fe:latest .
|
||||
|
||||
- name: Push Docker image
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
|
||||
docker push 192.168.2.28:5050/tusha-fe:latest
|
||||
|
||||
69
.gitignore
vendored
Normal file
69
.gitignore
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Specifies intentionally untracked files to ignore when using Git
|
||||
# http://git-scm.com/docs/gitignore
|
||||
|
||||
*~
|
||||
*.sw[mnpcod]
|
||||
.tmp
|
||||
*.tmp
|
||||
*.tmp.*
|
||||
UserInterfaceState.xcuserstate
|
||||
$RECYCLE.BIN/
|
||||
|
||||
*.log
|
||||
log.txt
|
||||
|
||||
|
||||
/.sourcemaps
|
||||
/.versions
|
||||
/coverage
|
||||
|
||||
# Ionic
|
||||
/.ionic
|
||||
/www
|
||||
/platforms
|
||||
/plugins
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
|
||||
# Miscellaneous
|
||||
/.angular
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.vscode
|
||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:20-bullseye as build
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
RUN npm install -g @angular/cli
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN ng build --configuration=production
|
||||
|
||||
FROM nginx:latest
|
||||
COPY --from=build app/dist/aftas-angular /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
@@ -0,0 +1,7 @@
|
||||
# Project Decisions
|
||||
|
||||
### backend
|
||||
Looked at Spring Boot, Quarkus and Open Liberty. <br/>
|
||||
The choise has been made for: <br/>
|
||||
_*QUARKUS*_ <br/>
|
||||
Quarkus does more at build time, can compile native (which is nice since it is in a container anyay), and spins up fast. It's the most optimized for running in containers.
|
||||
|
||||
19
aantekeningen_dag-doelen.txt
Normal file
19
aantekeningen_dag-doelen.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
12:30 tenissen
|
||||
16:00 Ronja naar dansles brengen
|
||||
|
||||
- eerste twaalf kaartjes leren
|
||||
V min. 1 pomodoro ochtend
|
||||
- min. 1 pomodoro middag
|
||||
V PR nakijken
|
||||
V Angular blog lezen
|
||||
- Queues blog lezen
|
||||
- min. 1/2 uur aan Tusha
|
||||
V fruit
|
||||
|
||||
|
||||
|
||||
Declarabel:
|
||||
- 07:00 - 12:00 = 5 uur
|
||||
- 13:55 - 14:10 = 5,25 uur
|
||||
- 14:30 - 16:00 = 6,75 uur
|
||||
|
||||
32
aantekeningen_q-doelen.txt
Normal file
32
aantekeningen_q-doelen.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Dreams:
|
||||
- Eigen Tusha-app met eigen omzet
|
||||
- Senior Front-end developer
|
||||
- Eigen boek uitgegeven
|
||||
|
||||
Q2-doelen (approx. 10 weken):
|
||||
- sporten - voor een gezondere en fittere geest
|
||||
- progressie aan Tusha-app
|
||||
- Kennes en ervaring met front-end development
|
||||
- Meer focus
|
||||
|
||||
Normen en waarden - focus voor Q2:
|
||||
- Weerbaar - niet blijven hangen in falen, noch in succes
|
||||
- No-nonsense - eerlijk en oprecht naar eigen gedrag
|
||||
- Compassie - liefde tonen voor waar je goed in bent
|
||||
- Dankbaar - dankbaarheid tonen voor wat je hebt gekregen en hebt bereikt
|
||||
|
||||
SMART doelen Q2:
|
||||
- 8x tenissen - sporten, focus, weerbaarheid
|
||||
- 7x swingfit - sporten, focus, weerbaarheid
|
||||
- smartcards the now habit leren - focus, weerbaarheid
|
||||
- 8 uur development aan tusha - progressie, kennis & ervaring, compassie
|
||||
- 30 dagen met ochtend pomodoro - focus, kennis & ervaring, weerbaarheid
|
||||
- 30 dagen met middag pomodoro - focus, kennis & ervaring, weerbaarheid
|
||||
- 10 focus meditatiesessies - focus, weerbaarheid
|
||||
- 10 ontspanning meditatiesessies - weerbaarheid, dankbaarheid
|
||||
- 8 uur zelfstudie m.b.t. front-end - compassie, kennis & ervaring
|
||||
- 8x wekelijkse evaluatie - weerbaarheid, no-nonsense
|
||||
|
||||
|
||||
------ VOORTGANG ------
|
||||
|
||||
22
aantekeningen_w-doelen.txt
Normal file
22
aantekeningen_w-doelen.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
Week-doelen:
|
||||
- Sporten
|
||||
- Angular leren
|
||||
- Weerbaarheid/focus leren
|
||||
- Aan Tusha werken
|
||||
- Kennis backend op peil houden
|
||||
|
||||
SMART DOELEN:
|
||||
- sporten: 1 x tennissen
|
||||
- weerbaarheidstraining: alle kaartjes een keer doorgenomen - de eerste 12 geleerd, de tweede 12 soms met spieken
|
||||
- 1 uur development aan Tusha
|
||||
- 4 ochtenden met min. 1 pomodoro
|
||||
- 4 middagen met min. 1 pomodoro
|
||||
- 1 back-end gerelateerd artikel lezen
|
||||
- 1 front-end gerelateerd artikel lezen
|
||||
- 1 x focus meditatie
|
||||
- 1 x ontspanningsmeditatie
|
||||
- Week evalueren
|
||||
- min. 5 dagen fruit
|
||||
|
||||
|
||||
---- VOORTGANG ---
|
||||
148
angular.json
Normal file
148
angular.json
Normal file
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"app": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@ionic/angular-toolkit:page": {
|
||||
"styleext": "scss",
|
||||
"standalone": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "www",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "assets"
|
||||
}
|
||||
],
|
||||
"styles": ["src/theme/variables.scss", "src/global.scss"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "app:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "app:build:development"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "assets"
|
||||
}
|
||||
],
|
||||
"styles": ["src/theme/variables.scss", "src/global.scss"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"progress": false,
|
||||
"watch": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"schematicCollections": [
|
||||
"@ionic/angular-toolkit"
|
||||
],
|
||||
"analytics": false
|
||||
},
|
||||
"schematics": {
|
||||
"@ionic/angular-toolkit:component": {
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@ionic/angular-toolkit:page": {
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@angular-eslint/schematics:application": {
|
||||
"setParserOptionsProject": true
|
||||
},
|
||||
"@angular-eslint/schematics:library": {
|
||||
"setParserOptionsProject": true
|
||||
}
|
||||
}
|
||||
}
|
||||
12
capacitor.config.ts
Normal file
12
capacitor.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: 'io.ionic.starter',
|
||||
appName: 'tusha-fe',
|
||||
webDir: 'www',
|
||||
server: {
|
||||
androidScheme: 'https'
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
7
ionic.config.json
Normal file
7
ionic.config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "tusha-fe",
|
||||
"integrations": {
|
||||
"capacitor": {}
|
||||
},
|
||||
"type": "angular-standalone"
|
||||
}
|
||||
44
karma.conf.js
Normal file
44
karma.conf.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/app'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
17365
package-lock.json
generated
Normal file
17365
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
66
package.json
Normal file
66
package.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "tusha-fe",
|
||||
"version": "0.0.1",
|
||||
"author": "Ionic Framework",
|
||||
"homepage": "https://ionicframework.com/",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.3.0",
|
||||
"@angular/common": "^17.3.0",
|
||||
"@angular/compiler": "^17.3.0",
|
||||
"@angular/core": "^17.3.0",
|
||||
"@angular/forms": "^17.3.0",
|
||||
"@angular/platform-browser": "^17.3.0",
|
||||
"@angular/platform-browser-dynamic": "^17.3.0",
|
||||
"@angular/router": "^17.3.0",
|
||||
"@capacitor/app": "5.0.8",
|
||||
"@capacitor/core": "5.7.8",
|
||||
"@capacitor/haptics": "5.0.8",
|
||||
"@capacitor/keyboard": "5.0.9",
|
||||
"@capacitor/status-bar": "5.0.8",
|
||||
"@ionic/angular": "^7.5.0",
|
||||
"chart.js": "^4.4.1",
|
||||
"ionicons": "^7.2.1",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.3.0",
|
||||
"@angular-eslint/builder": "^17.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^17.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^17.0.0",
|
||||
"@angular-eslint/schematics": "^17.0.0",
|
||||
"@angular-eslint/template-parser": "^17.0.0",
|
||||
"@angular/cli": "^17.3.0",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@angular/language-service": "^17.3.0",
|
||||
"@capacitor/cli": "5.7.8",
|
||||
"@ionic/angular-toolkit": "^9.0.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsdoc": "30.7.13",
|
||||
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||
"jasmine-core": "~5.1.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"ts-node": "^8.3.0",
|
||||
"typescript": "~5.2.2"
|
||||
},
|
||||
"description": "An Ionic project"
|
||||
}
|
||||
20
src/app/app.component.html
Normal file
20
src/app/app.component.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<ion-app>
|
||||
<ion-split-pane contentId="main-content">
|
||||
<ion-menu contentId="main-content" type="overlay">
|
||||
<ion-content>
|
||||
<ion-list id="inbox-list">
|
||||
<ion-list-header>Inbox</ion-list-header>
|
||||
<ion-note>hi@ionicframework.com</ion-note>
|
||||
|
||||
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index">
|
||||
<ion-item routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" routerLinkActive="selected">
|
||||
<ion-icon aria-hidden="true" slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
|
||||
<ion-label>{{ p.title }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
<ion-router-outlet id="main-content"></ion-router-outlet>
|
||||
</ion-split-pane>
|
||||
</ion-app>
|
||||
119
src/app/app.component.scss
Normal file
119
src/app/app.component.scss
Normal file
@@ -0,0 +1,119 @@
|
||||
ion-menu ion-content {
|
||||
--background: var(--ion-item-background, var(--ion-background-color, #fff));
|
||||
}
|
||||
|
||||
ion-menu.md ion-content {
|
||||
--padding-start: 8px;
|
||||
--padding-end: 8px;
|
||||
--padding-top: 20px;
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
ion-menu.md ion-note {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list-header,
|
||||
ion-menu.md ion-note {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list {
|
||||
border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list ion-list-header {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#labels-list ion-list-header {
|
||||
font-size: 16px;
|
||||
|
||||
margin-bottom: 18px;
|
||||
|
||||
color: #757575;
|
||||
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item {
|
||||
--padding-start: 10px;
|
||||
--padding-end: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected {
|
||||
--background: rgba(var(--ion-color-primary-rgb), 0.14);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-icon {
|
||||
color: #616e7e;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-content {
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list {
|
||||
padding: 20px 0 0 0;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
line-height: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item {
|
||||
--padding-start: 16px;
|
||||
--padding-end: 16px;
|
||||
--min-height: 50px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item.selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item ion-icon {
|
||||
font-size: 24px;
|
||||
color: #73849a;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list#labels-list ion-list-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list-header,
|
||||
ion-menu.ios ion-note {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-note {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
|
||||
color: var(--ion-color-medium-shade);
|
||||
}
|
||||
|
||||
ion-item.selected {
|
||||
--color: var(--ion-color-primary);
|
||||
}
|
||||
44
src/app/app.component.spec.ts
Normal file
44
src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
TestBed.overrideComponent(AppComponent, {
|
||||
add: {
|
||||
imports: [RouterTestingModule]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have menu labels', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const app = fixture.nativeElement;
|
||||
const menuItems = app.querySelectorAll('ion-label');
|
||||
expect(menuItems.length).toEqual(12);
|
||||
expect(menuItems[0].textContent).toContain('Inbox');
|
||||
expect(menuItems[1].textContent).toContain('Outbox');
|
||||
});
|
||||
|
||||
it('should have urls', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const app = fixture.nativeElement;
|
||||
const menuItems = app.querySelectorAll('ion-item');
|
||||
expect(menuItems.length).toEqual(12);
|
||||
expect(menuItems[0].getAttribute('ng-reflect-router-link')).toEqual(
|
||||
'/folder/inbox'
|
||||
);
|
||||
expect(menuItems[1].getAttribute('ng-reflect-router-link')).toEqual(
|
||||
'/folder/outbox'
|
||||
);
|
||||
});
|
||||
});
|
||||
32
src/app/app.component.ts
Normal file
32
src/app/app.component.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet } from '@ionic/angular/standalone';
|
||||
import { addIcons } from 'ionicons';
|
||||
import { mailOutline, mailSharp, paperPlaneOutline, paperPlaneSharp, heartOutline, heartSharp, archiveOutline, archiveSharp, trashOutline, trashSharp, warningOutline, warningSharp, bookmarkOutline, bookmarkSharp } from 'ionicons/icons';
|
||||
import { HomePage} from "./pages/home/home.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
standalone: true,
|
||||
imports: [HomePage, RouterLink, RouterLinkActive, CommonModule, IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet],
|
||||
})
|
||||
export class AppComponent {
|
||||
public appPages = [
|
||||
{ title: 'Home', url: '/tusha/home', icon: 'home' },
|
||||
{ title: 'Daily goals', url: '/folder/daily', icon: 'today' },
|
||||
{ title: 'Weekly goals', url: '/folder/weekly', icon: 'calendar' },
|
||||
{ title: 'Quarterly goals', url: '/folder/quaterly', icon: 'book' },
|
||||
{ title: 'Modus', url: '/tusha/modus', icon: 'aperture' },
|
||||
{ title: 'Settings', url: '/folder/settings', icon: 'settings' },
|
||||
];
|
||||
public labels = ['Family', 'Friends', 'Notes', 'Work', 'Travel', 'Reminders'];
|
||||
|
||||
constructor() {
|
||||
type Test = 'a' | 'b';
|
||||
// ^?
|
||||
addIcons({ mailOutline, mailSharp, paperPlaneOutline, paperPlaneSharp, heartOutline, heartSharp, archiveOutline, archiveSharp, trashOutline, trashSharp, warningOutline, warningSharp, bookmarkOutline, bookmarkSharp });
|
||||
}
|
||||
}
|
||||
26
src/app/app.routes.ts
Normal file
26
src/app/app.routes.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'tusha/home',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'tusha/home',
|
||||
loadComponent: () => import('./pages/home/home.component').then(m => m.HomePage)
|
||||
},
|
||||
{
|
||||
path: 'tusha/modus',
|
||||
loadComponent: () => import('./pages/modus/modus-overview.component').then(m => m.ModusOverviewPage)
|
||||
},
|
||||
{
|
||||
path: 'tusha/modus/focus',
|
||||
loadComponent: () => import('./pages/modus/focus/focus.component').then(m => m.FocusPage)
|
||||
},
|
||||
{
|
||||
path: 'folder/:id',
|
||||
loadComponent: () =>
|
||||
import('./folder/folder.page').then((m) => m.FolderPage),
|
||||
},
|
||||
];
|
||||
21
src/app/folder/folder.page.html
Normal file
21
src/app/folder/folder.page.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ folder }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content [fullscreen]="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">{{ folder }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<div id="container">
|
||||
<strong class="capitalize">{{ folder }}</strong>
|
||||
<p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
|
||||
</div>
|
||||
</ion-content>
|
||||
28
src/app/folder/folder.page.scss
Normal file
28
src/app/folder/folder.page.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
ion-menu-button {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
#container {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#container strong {
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
#container p {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
color: #8c8c8c;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
25
src/app/folder/folder.page.spec.ts
Normal file
25
src/app/folder/folder.page.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { FolderPage } from './folder.page';
|
||||
|
||||
describe('FolderPage', () => {
|
||||
let component: FolderPage;
|
||||
let fixture: ComponentFixture<FolderPage>;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.overrideComponent(FolderPage, {
|
||||
add: {
|
||||
imports: [RouterTestingModule]
|
||||
}
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(FolderPage);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
src/app/folder/folder.page.ts
Normal file
20
src/app/folder/folder.page.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { IonHeader, IonToolbar, IonButtons, IonMenuButton, IonTitle, IonContent } from '@ionic/angular/standalone';
|
||||
|
||||
@Component({
|
||||
selector: 'app-folder',
|
||||
templateUrl: './folder.page.html',
|
||||
styleUrls: ['./folder.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonHeader, IonToolbar, IonButtons, IonMenuButton, IonTitle, IonContent],
|
||||
})
|
||||
export class FolderPage implements OnInit {
|
||||
public folder!: string;
|
||||
private activatedRoute = inject(ActivatedRoute);
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {
|
||||
this.folder = this.activatedRoute.snapshot.paramMap.get('id') as string;
|
||||
}
|
||||
}
|
||||
0
src/app/pages/daily/dialy.component.css
Normal file
0
src/app/pages/daily/dialy.component.css
Normal file
11
src/app/pages/home/home.component.css
Normal file
11
src/app/pages/home/home.component.css
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
ion-footer ion-title {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
color: darkgray;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
13
src/app/pages/home/home.component.html
Normal file
13
src/app/pages/home/home.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<app-tusha-page>
|
||||
<ion-breadcrumbs slot="breadcrumb">
|
||||
<ion-breadcrumb >
|
||||
<ion-icon slot="start" name="home"/>
|
||||
Home
|
||||
</ion-breadcrumb>
|
||||
</ion-breadcrumbs>
|
||||
|
||||
<tusha-status-cards [card1]="card1" [card2]="card2" />
|
||||
|
||||
<tusha-nav-rows [navcards]="navcards" />
|
||||
|
||||
</app-tusha-page>
|
||||
54
src/app/pages/home/home.component.ts
Normal file
54
src/app/pages/home/home.component.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Component } from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
import {
|
||||
IonBreadcrumbs, IonCard, IonCardContent, IonCol, IonRow, IonBreadcrumb, IonIcon
|
||||
} from "@ionic/angular/standalone";
|
||||
import {TushaNavRowsComponent} from "../../tusha/tusha-nav-rows/tusha-nav-rows.component";
|
||||
import {NavCardData} from "../../tusha/tusha-nav-rows/nav-card-data";
|
||||
import {TushaPageComponent} from "../../tusha/tusha-page/tusha-page.component";
|
||||
import {TushaStatusCardsComponent} from "../../tusha/tusha-status-cards/tusha-status-cards.component";
|
||||
import {StatusCardsData} from "../../tusha/tusha-status-cards/status-cards-data";
|
||||
|
||||
@Component({
|
||||
templateUrl: 'home.component.html',
|
||||
styleUrls: ['home.component.css'],
|
||||
standalone: true,
|
||||
imports: [IonRow, IonCol, IonCard, IonCardContent, TushaNavRowsComponent, TushaPageComponent, IonBreadcrumbs, IonBreadcrumb, IonIcon, TushaStatusCardsComponent]
|
||||
})
|
||||
export class HomePage {
|
||||
navcards: NavCardData[];
|
||||
card1: StatusCardsData;
|
||||
card2: StatusCardsData;
|
||||
|
||||
constructor(public router: Router) {
|
||||
this.navcards = [
|
||||
{id: 1, title: 'Daily Goals', color: {color: 'blue'}, onclick: this.goToDailyGoals.bind(this), icon: 'today-outline'},
|
||||
{id: 2, title: 'Weekly Goals', color: {color: 'green'}, onclick: this.goToDailyGoals.bind(this), icon: 'calendar-outline'},
|
||||
{id: 3, title: 'Quaterly Goals', color: {color: 'orange'}, onclick: this.goToDailyGoals.bind(this), icon: 'book-outline'},
|
||||
{id: 4, title: 'Go Into Modus', color: {color: 'black'}, onclick: this.goToModus.bind(this), icon: 'aperture-outline'},
|
||||
];
|
||||
this.card1 = {
|
||||
id: 1,
|
||||
color: {color: 'orange'},
|
||||
type: 'number',
|
||||
amount: 15,
|
||||
description: 'Tasks Done'
|
||||
};
|
||||
this.card2 = {
|
||||
id: 2,
|
||||
color: {color: 'green'},
|
||||
type: 'percentage',
|
||||
amount: 58,
|
||||
description: 'Week Progress'
|
||||
};
|
||||
}
|
||||
|
||||
goToDailyGoals() {
|
||||
this.router.navigate(['folder/daily']);
|
||||
}
|
||||
|
||||
goToModus() {
|
||||
this.router.navigate(['tusha/modus']);
|
||||
}
|
||||
|
||||
}
|
||||
9
src/app/pages/modus/focus/focus-configuration.ts
Normal file
9
src/app/pages/modus/focus/focus-configuration.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class FocusConfiguration {
|
||||
focusMinutes: number = 25;
|
||||
shortBreak: number = 5;
|
||||
longBreak: number = 15;
|
||||
|
||||
cyclesBeforeLongBreak: number = 4;
|
||||
|
||||
behaviorOnFocusEnd: 'nothing' | 'startBreak' | 'overtime' = 'overtime';
|
||||
}
|
||||
3
src/app/pages/modus/focus/focus-state.ts
Normal file
3
src/app/pages/modus/focus/focus-state.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export enum FocusState {
|
||||
FOCUS = 'in_focus', SHORT_BREAK = 'short_break', LONG_BREAK = 'long_break', STOPPED = 'stopped'
|
||||
}
|
||||
79
src/app/pages/modus/focus/focus-stats.ts
Normal file
79
src/app/pages/modus/focus/focus-stats.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import {FocusState} from "./focus-state";
|
||||
|
||||
export class FocusStats {
|
||||
private _focusCycles: number = 0;
|
||||
private _focusTimeInMs: number = 0;
|
||||
private _focusTime: string = '';
|
||||
private _shortBreakCycles: number = 0;
|
||||
private _longBreakCycles: number = 0;
|
||||
private _breakTimeInMs: number = 0;
|
||||
private _breakTime: string = '';
|
||||
|
||||
inc(state: FocusState, deltaInMs?: number): void {
|
||||
switch (state) {
|
||||
case FocusState.FOCUS:
|
||||
this._focusCycles++;
|
||||
this._focusTimeInMs += !!deltaInMs ? deltaInMs : 0;
|
||||
this.updateFocusTime();
|
||||
break;
|
||||
case FocusState.SHORT_BREAK:
|
||||
this._shortBreakCycles++;
|
||||
this._breakTimeInMs += !!deltaInMs ? deltaInMs : 0;
|
||||
this.updateBreakTime();
|
||||
break;
|
||||
case FocusState.LONG_BREAK:
|
||||
this._longBreakCycles++;
|
||||
this._breakTimeInMs += !!deltaInMs ? deltaInMs : 0;
|
||||
this.updateBreakTime();
|
||||
break;
|
||||
default:
|
||||
// nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private updateFocusTime() {
|
||||
this._focusTime = this.transformTime(this._focusTimeInMs);
|
||||
}
|
||||
|
||||
private updateBreakTime() {
|
||||
this._breakTime = this.transformTime(this._breakTimeInMs);
|
||||
}
|
||||
|
||||
private transformTime(ms: number): string {
|
||||
const totalSeconds = Math.floor(ms / 1000);
|
||||
const seconds = totalSeconds % 60;
|
||||
const totalMinutes = Math.floor(totalSeconds / 60);
|
||||
const minutes = totalMinutes % 60;
|
||||
const hours = Math.floor(totalMinutes / 60);
|
||||
|
||||
const pad = (n: number) => n.toString().padStart(2, "0");
|
||||
|
||||
if (hours > 0) {
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
} else {
|
||||
return `${pad(minutes)}:${pad(seconds)}`;
|
||||
}
|
||||
}
|
||||
|
||||
get focusCycles(): number {
|
||||
return this._focusCycles;
|
||||
}
|
||||
|
||||
get shortBreakCycles(): number {
|
||||
return this._shortBreakCycles;
|
||||
}
|
||||
|
||||
get longBreakCycles(): number {
|
||||
return this._longBreakCycles;
|
||||
}
|
||||
|
||||
|
||||
get focusTime(): string {
|
||||
return this._focusTime;
|
||||
}
|
||||
|
||||
get breakTime(): string {
|
||||
return this._breakTime;
|
||||
}
|
||||
}
|
||||
169
src/app/pages/modus/focus/focus.component.css
Normal file
169
src/app/pages/modus/focus/focus.component.css
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
/* focus-timer.component.scss */
|
||||
app-tusha-page {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.leftright-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.fullwidth {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.countdown-box {
|
||||
height: 320px;
|
||||
border: 2px solid;
|
||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.countdown-state-text {
|
||||
font-family: Monaco, "Lucida Console", monospace;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.countdown-state-stopped-box {
|
||||
background-color: lightcoral;
|
||||
border-color: darkred;
|
||||
}
|
||||
|
||||
.countdown-state-stopped-text{
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.countdown-state-short_break-box {
|
||||
background-color: lightcyan;
|
||||
border-color: darkgreen;
|
||||
}
|
||||
|
||||
.countdown-state-short_break-text {
|
||||
color: darkgreen;
|
||||
}
|
||||
|
||||
.countdown-state-long_break-box {
|
||||
background-color: peachpuff;
|
||||
border-color: orangered;
|
||||
}
|
||||
|
||||
.countdown-state-long_break-text {
|
||||
color: orangered;
|
||||
|
||||
}
|
||||
|
||||
.countdown-state-in_focus-box {
|
||||
background-color: lightblue;
|
||||
border-color: darkblue;
|
||||
}
|
||||
|
||||
.countdown-state-in_focus-text {
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
.focustimer {
|
||||
position: relative;
|
||||
width: 270px;
|
||||
height: 270px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid;
|
||||
background-color: lavender;
|
||||
}
|
||||
|
||||
.countdown-text {
|
||||
position: relative;
|
||||
transform: translate(25%, -225%);
|
||||
font-size: 4em;
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-button {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.countdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
align-content: center;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.focusmode {
|
||||
border-width: 10px;
|
||||
border-color: lightblue;
|
||||
border-style: outset;
|
||||
background-color: aliceblue;
|
||||
}
|
||||
|
||||
.breakmode {
|
||||
border-width: 10px;
|
||||
border-color: antiquewhite;
|
||||
border-style: outset;
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.timer {
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.state {
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
border: 2px solid;
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
display: block;
|
||||
position: relative;
|
||||
top: -350px;
|
||||
left: -50px;
|
||||
}
|
||||
|
||||
.state-stopped {
|
||||
color: darkred;
|
||||
background-color: lightcoral;
|
||||
border-color: darkred;
|
||||
}
|
||||
|
||||
.state-short_break {
|
||||
color: darkgreen;
|
||||
background-color: lightgreen;
|
||||
border-color: darkgreen;
|
||||
}
|
||||
|
||||
.state-long_break {
|
||||
color: orangered;
|
||||
background-color: peachpuff;
|
||||
border-color: orangered;
|
||||
}
|
||||
|
||||
.state_in_focus {
|
||||
color: darkblue;
|
||||
background-color: lightblue;
|
||||
border-color: darkblue;
|
||||
}
|
||||
|
||||
.button-bar {
|
||||
width: 350px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
107
src/app/pages/modus/focus/focus.component.html
Normal file
107
src/app/pages/modus/focus/focus.component.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<app-tusha-page>
|
||||
<ion-breadcrumbs slot="breadcrumb">
|
||||
<ion-breadcrumb href="tusha/home">
|
||||
<ion-icon slot="start" name="home"/>
|
||||
Home
|
||||
</ion-breadcrumb>
|
||||
<ion-breadcrumb href="tusha/modus">
|
||||
<ion-icon slot="start" name="aperture"/>
|
||||
Modus
|
||||
</ion-breadcrumb>
|
||||
<ion-breadcrumb>
|
||||
<ion-icon slot="start" name="time"/>
|
||||
Focus
|
||||
</ion-breadcrumb>
|
||||
</ion-breadcrumbs>
|
||||
|
||||
<!-- TODO add default action to div -->
|
||||
<!-- TODO add are you sure pop-ups -->
|
||||
|
||||
<div class="leftright-line fullwidth">
|
||||
<ion-button (click)="focus()" color="primary">
|
||||
<ion-icon slot="start" name="aperture"></ion-icon>
|
||||
Focus
|
||||
</ion-button>
|
||||
<ion-button (click)="stopTimer()" color="danger">
|
||||
<ion-icon slot="start" name="close-circle"></ion-icon>
|
||||
STOP
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<div class="countdown-box countdown-state-{{state}}-box fullwidth">
|
||||
<div class="leftright-line">
|
||||
<span class="countdown-state-text countdown-state-{{state}}-text">{{state}}</span>
|
||||
<!-- TODO add behaviour to settings cogwheel -->
|
||||
<ion-icon name="settings-outline"></ion-icon>
|
||||
</div>
|
||||
<div class="countdown-timer">
|
||||
<svg class="focustimer countdown-state-{{state}}-box" width="100%" height="100%" viewBox="0 0 200 200" (click)="startWhereWeAre()">
|
||||
<!-- Definieer de gradient -->
|
||||
<defs>
|
||||
<!-- have the gradient follow the path is possible but too difficult for now - can be read at https://stackoverflow.com/questions/14633363/can-i-apply-a-gradient-along-an-svg-path -->
|
||||
<linearGradient id="grad1" x1="1" y1="0" x2="0" y2="0">
|
||||
<stop offset="0%" stop-color="blue"></stop>
|
||||
<stop offset="80%" stop-color="green"></stop>
|
||||
<stop offset="100%" stop-color="red"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<path id="1stPath" [attr.d]="pathD"
|
||||
fill="none" stroke="url(#grad1)" stroke-width="30" stroke-dasharray="0" [attr.stroke-dashoffset]="dashOffset" />
|
||||
</svg>
|
||||
<div class="countdown-text">{{ minutesText }}:{{ secondsText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="leftright-line fullwidth">
|
||||
<ion-button (click)="reload()" color="secondary">
|
||||
<ion-icon slot="start" name="refresh-circle" ></ion-icon>
|
||||
Reload
|
||||
</ion-button>
|
||||
<ion-button (click)="recharge()" color="warning">
|
||||
<ion-icon slot="start" name="battery-charging"></ion-icon>
|
||||
Recharge
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<div class="focus-stats">
|
||||
hier komen de stats
|
||||
<ion-card>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<p>Aantal focus cycles: <span>{{ ($stats | async)?.focusCycles }}</span></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<p>Focus tijd: <span>{{ ($stats | async)?.focusTime}}</span></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<p>Aantal korte pauzes: <span>{{ ($stats | async)?.shortBreakCycles}}</span></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<p>Aantal lange pauzes: <span>{{ ($stats | async)?.longBreakCycles}}</span></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<p>Pauze tijd: <span>{{ ($stats | async)?.breakTime}}</span></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
<!--
|
||||
ion-card
|
||||
met daarin ion-lines met de vol
|
||||
|
||||
-->
|
||||
</div>
|
||||
|
||||
|
||||
</app-tusha-page>
|
||||
|
||||
179
src/app/pages/modus/focus/focus.component.ts
Normal file
179
src/app/pages/modus/focus/focus.component.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import {Component, OnDestroy, OnInit, Renderer2} from "@angular/core";
|
||||
import {
|
||||
IonBreadcrumb,
|
||||
IonBreadcrumbs,
|
||||
IonButton,
|
||||
IonCard,
|
||||
IonContent,
|
||||
IonIcon, IonItem, IonLabel, IonList,
|
||||
IonProgressBar
|
||||
} from "@ionic/angular/standalone";
|
||||
import {TushaPageComponent} from "../../../tusha/tusha-page/tusha-page.component";
|
||||
import {FocusState} from "./focus-state";
|
||||
import {FocusService} from "./focus.service";
|
||||
import {Observable} from "rxjs";
|
||||
import {FocusStats} from "./focus-stats";
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'focus.component.html',
|
||||
styleUrls: ['focus.component.css'],
|
||||
standalone: true,
|
||||
imports: [IonBreadcrumbs, IonBreadcrumb, IonIcon, TushaPageComponent, IonProgressBar, IonContent, IonButton, IonCard, IonItem, IonLabel, IonList, CommonModule]
|
||||
})
|
||||
export class FocusPage implements OnInit, OnDestroy {
|
||||
duration: number = 0; // default duration in minutes
|
||||
minutesText: string = '';
|
||||
secondsText: string = '';
|
||||
private interval: number | undefined;
|
||||
private timerStarted: number | null = null;
|
||||
state: FocusState = FocusState.STOPPED;
|
||||
private focusRuns = 0;
|
||||
$stats: Observable<FocusStats> = this.focusService.$stats;
|
||||
|
||||
private endOfTimerChime: HTMLAudioElement;
|
||||
private forcedEndOfTimerChime: HTMLAudioElement;
|
||||
private windUpChime: HTMLAudioElement;
|
||||
|
||||
pathD: string = 'M100 10 A90 90 0 1 1 0 0';
|
||||
|
||||
dashOffset = 0;
|
||||
|
||||
constructor(private renderer: Renderer2, private focusService: FocusService) {
|
||||
this.endOfTimerChime = new Audio('../../../assets/race-start-beeps-125125.mp3');
|
||||
this.forcedEndOfTimerChime = new Audio('../../../assets/hardstop.mp3');
|
||||
this.windUpChime = new Audio('../../../assets/wind-up.mp3');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.updateTimeDisplay(this.duration * 60, 0, 1);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
startTimer(duration: number) {
|
||||
this.duration = duration;
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
this.timerStarted = Date.now();
|
||||
this.update();
|
||||
this.interval = setInterval(() => this.update(), 1000);
|
||||
}
|
||||
|
||||
private update() {
|
||||
const elapsed = this.timerStarted ? Math.round((Date.now() - (this.timerStarted)) / 1000) : 0; // in seconden
|
||||
let progress = elapsed / (this.duration * 60); // from 0 to 1
|
||||
|
||||
if( progress === 0) {
|
||||
progress = 0.0000001; // otherwise no graph
|
||||
}
|
||||
|
||||
if (progress >= 1 || isNaN(progress)) {
|
||||
if (this.duration === 0) {
|
||||
clearInterval(this.interval);
|
||||
this.timerStarted = null;
|
||||
}
|
||||
else if (progress === 1) {
|
||||
this.endOfTimerChime.play().catch(error => {
|
||||
console.error('Error playing audio:', error);
|
||||
});
|
||||
}
|
||||
progress = 1;
|
||||
}
|
||||
|
||||
this.renderer.setStyle(
|
||||
document.documentElement,
|
||||
'--progress',
|
||||
progress.toString()
|
||||
);
|
||||
|
||||
this.updateTimeDisplay(this.duration * 60 - elapsed, elapsed, 1 - progress);
|
||||
}
|
||||
|
||||
private updateTimeDisplay(timeLeft: number, elapsed: number, percentage: number) {
|
||||
const theta = 2 * Math.PI * (percentage - 0.25);
|
||||
const circleEndX = 100 + 90 * Math.cos(theta);
|
||||
const circleEndY = 100 + 90 * Math.sin(theta);
|
||||
this.pathD = 'M100 10 A90 90 0 ' + (percentage > 0.5 ? '1' : '0') + ' 1 ' + circleEndX + ' ' + circleEndY;
|
||||
|
||||
const twoNumbers = (n: number) => n < 10 && n >= 0 ? '0' + n.toString() : n.toString();
|
||||
const floorPositiveCeilNegative = (n: number): number => n > 0 ? Math.floor(n) : Math.ceil(n);
|
||||
this.minutesText = twoNumbers(Math.abs(floorPositiveCeilNegative(timeLeft / 60)));
|
||||
this.secondsText = twoNumbers(Math.abs(floorPositiveCeilNegative(timeLeft % 60)));
|
||||
|
||||
this.dashOffset = elapsed;
|
||||
}
|
||||
|
||||
startWhereWeAre() {
|
||||
switch (this.state) {
|
||||
case FocusState.FOCUS:
|
||||
this.focusRuns++;
|
||||
if (this.focusRuns > 3) {
|
||||
this.focusRuns = 0;
|
||||
this.recharge();
|
||||
} else {
|
||||
this.reload();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stopTimer() {
|
||||
this.focusRuns = 0;
|
||||
this.state = FocusState.STOPPED;
|
||||
this.stopStatsCounter();
|
||||
this.startTimer(0);
|
||||
this.forcedEndOfTimerChime.play().catch(error => {
|
||||
console.error('Error playing audio:', error);
|
||||
});
|
||||
}
|
||||
|
||||
private stopStatsCounter() {
|
||||
this.focusService.stopCurrent(this.timerStarted !== null);
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.windUpChime.play().catch(error => {
|
||||
console.error('Error playing audio:', error);
|
||||
});
|
||||
this.state = FocusState.FOCUS;
|
||||
this.stopStatsCounter();
|
||||
this.focusService.startFocus();
|
||||
this.startTimer(0.3); // 25
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.windUpChime.play().catch(error => {
|
||||
console.error('Error playing audio:', error);
|
||||
});
|
||||
if (!!this.timerStarted) {
|
||||
this.focusRuns = 0;
|
||||
}
|
||||
this.state = FocusState.SHORT_BREAK;
|
||||
this.stopStatsCounter();
|
||||
this.focusService.startShortBreak();
|
||||
this.startTimer(0.1); // 5
|
||||
}
|
||||
|
||||
recharge() {
|
||||
this.windUpChime.play().catch(error => {
|
||||
console.error('Error playing audio:', error);
|
||||
});
|
||||
if (!!this.timerStarted) {
|
||||
this.focusRuns = 0;
|
||||
}
|
||||
this.state = FocusState.LONG_BREAK;
|
||||
this.stopStatsCounter();
|
||||
this.focusService.startLongBreak();
|
||||
this.startTimer(0.2); // 15
|
||||
}
|
||||
}
|
||||
40
src/app/pages/modus/focus/focus.service.ts
Normal file
40
src/app/pages/modus/focus/focus.service.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {FocusStats} from "./focus-stats";
|
||||
import {FocusState} from "./focus-state";
|
||||
import {Injectable} from "@angular/core";
|
||||
import {BehaviorSubject, Observable} from "rxjs";
|
||||
|
||||
@Injectable({ providedIn: 'root',})
|
||||
export class FocusService {
|
||||
current: {state: FocusState, start: Date} = {state: FocusState.STOPPED, start: new Date()};
|
||||
private localStats = new FocusStats();
|
||||
private statsSubject: BehaviorSubject<FocusStats> = new BehaviorSubject<FocusStats>(this.localStats);
|
||||
$stats: Observable<FocusStats> = this.statsSubject.asObservable();
|
||||
|
||||
private startState(state: FocusState) {
|
||||
this.current.state = state;
|
||||
this.current.start = new Date();
|
||||
}
|
||||
|
||||
startFocus(): void {
|
||||
this.startState(FocusState.FOCUS);
|
||||
}
|
||||
|
||||
startShortBreak(): void {
|
||||
this.startState(FocusState.SHORT_BREAK);
|
||||
}
|
||||
|
||||
startLongBreak(): void {
|
||||
this.startState(FocusState.LONG_BREAK);
|
||||
}
|
||||
|
||||
stopCurrent(forced: boolean) {
|
||||
if (!forced) {
|
||||
const deltaInMs = new Date().getTime() - this.current.start.getTime();
|
||||
this.localStats.inc(this.current.state, deltaInMs);
|
||||
this.statsSubject.next(this.localStats);
|
||||
} else {
|
||||
this.localStats.inc(this.current.state);
|
||||
this.statsSubject.next(this.localStats);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/app/pages/modus/modus-overview.component.css
Normal file
0
src/app/pages/modus/modus-overview.component.css
Normal file
16
src/app/pages/modus/modus-overview.component.html
Normal file
16
src/app/pages/modus/modus-overview.component.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<app-tusha-page>
|
||||
<ion-breadcrumbs slot="breadcrumb">
|
||||
<ion-breadcrumb href="tusha/home">
|
||||
<ion-icon slot="start" name="home"/>
|
||||
Home
|
||||
</ion-breadcrumb>
|
||||
<ion-breadcrumb >
|
||||
<ion-icon slot="start" name="aperture"/>
|
||||
Modus
|
||||
</ion-breadcrumb>
|
||||
</ion-breadcrumbs>
|
||||
|
||||
<tusha-status-cards [card1]="card1" [card2]="card2" />
|
||||
|
||||
<tusha-nav-rows [navcards]="navcards" />
|
||||
</app-tusha-page>
|
||||
61
src/app/pages/modus/modus-overview.component.ts
Normal file
61
src/app/pages/modus/modus-overview.component.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import {Component} from "@angular/core";
|
||||
import {
|
||||
IonBreadcrumb,
|
||||
IonBreadcrumbs,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCol,
|
||||
IonIcon,
|
||||
IonRow
|
||||
} from "@ionic/angular/standalone";
|
||||
import {TushaNavRowsComponent} from "../../tusha/tusha-nav-rows/tusha-nav-rows.component";
|
||||
import {TushaPageComponent} from "../../tusha/tusha-page/tusha-page.component";
|
||||
import {NavCardData} from "../../tusha/tusha-nav-rows/nav-card-data";
|
||||
import {Router} from "@angular/router";
|
||||
import {StatusCardsData} from "../../tusha/tusha-status-cards/status-cards-data";
|
||||
import {TushaStatusCardsComponent} from "../../tusha/tusha-status-cards/tusha-status-cards.component";
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'modus-overview.component.html',
|
||||
styleUrls: ['modus-overview.component.css'],
|
||||
standalone: true,
|
||||
imports: [IonRow, IonCol, IonCard, IonCardContent, TushaNavRowsComponent, TushaPageComponent, IonBreadcrumb, IonBreadcrumbs, IonIcon, TushaStatusCardsComponent]
|
||||
})
|
||||
export class ModusOverviewPage {
|
||||
navcards: NavCardData[];
|
||||
card1: StatusCardsData;
|
||||
card2: StatusCardsData;
|
||||
|
||||
constructor(public router: Router) {
|
||||
this.navcards = [
|
||||
{id: 1, title: 'Focus', color: {color: 'blue'}, onclick: this.goToFocus.bind(this), icon: 'time-outline'},
|
||||
{id: 2, title: 'Perceive', color: {color: 'purple'}, onclick: this.goToDailyGoals.bind(this), icon: 'leaf-outline'},
|
||||
{id: 3, title: 'Grow', color: {color: 'green'}, onclick: this.goToDailyGoals.bind(this), icon: 'rocket-outline'},
|
||||
{id: 4, title: 'Reflect', color: {color: 'beige'}, onclick: this.goToDailyGoals.bind(this), icon: 'speedometer-outline'},
|
||||
{id: 5, title: 'Love', color: {color: 'red'}, onclick: this.goToDailyGoals.bind(this), icon: 'heart-outline'},
|
||||
];
|
||||
this.card1 = {
|
||||
id: 1,
|
||||
color: {color: 'blue'},
|
||||
type: 'number',
|
||||
amount: 3,
|
||||
description: 'Focus sessions'
|
||||
};
|
||||
this.card2 = {
|
||||
id: 2,
|
||||
color: {color: 'orange'},
|
||||
type: 'number',
|
||||
amount: 4,
|
||||
description: 'Modus types'
|
||||
};
|
||||
}
|
||||
|
||||
goToDailyGoals() {
|
||||
this.router.navigate(['folder/daily']);
|
||||
}
|
||||
|
||||
goToFocus() {
|
||||
this.router.navigate(['tusha/modus/focus']);
|
||||
}
|
||||
}
|
||||
3
src/app/tusha/tusha-nav-card/card-color.ts
Normal file
3
src/app/tusha/tusha-nav-card/card-color.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface CardColor {
|
||||
color: '' | 'blue' | 'green' | 'orange' | 'black' | 'purple' | 'beige' | 'red';
|
||||
}
|
||||
115
src/app/tusha/tusha-nav-card/tusha-nav-card.component.css
Normal file
115
src/app/tusha/tusha-nav-card/tusha-nav-card.component.css
Normal file
@@ -0,0 +1,115 @@
|
||||
ion-card {
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
|
||||
ion-grid.innergrid {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
ion-grid.innergrid ion-row {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
ion-grid.innergrid ion-row ion-col {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
.navcard {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.navcard.daily {
|
||||
background-color: lightcyan;
|
||||
}
|
||||
|
||||
.navcard.daily :active {
|
||||
background-color: cyan;
|
||||
}
|
||||
|
||||
.navcard.weekly {
|
||||
background-color: mistyrose;
|
||||
}
|
||||
|
||||
.navcard.weekly :active {
|
||||
background-color: rosybrown;
|
||||
}
|
||||
|
||||
.navcard.quarterly {
|
||||
background-color: bisque;
|
||||
}
|
||||
|
||||
.navcard.quarterly :active {
|
||||
background-color: burlywood;
|
||||
}
|
||||
|
||||
.navcard.modus {
|
||||
background-color: lightgoldenrodyellow;
|
||||
}
|
||||
|
||||
.navcard.modus :active {
|
||||
background-color: gold;
|
||||
}
|
||||
|
||||
|
||||
.navcard.blue {
|
||||
background-color: #dbeeff;
|
||||
}
|
||||
.navcard.blue :active {
|
||||
background-color: #2898ff;
|
||||
color: white;
|
||||
}
|
||||
.navcard.green {
|
||||
background-color: #cafae7;
|
||||
}
|
||||
.navcard.green :active {
|
||||
background-color: #33a86a;
|
||||
color: white;
|
||||
}
|
||||
.navcard.orange {
|
||||
background-color: #ffbaa0;
|
||||
}
|
||||
.navcard.orange :active {
|
||||
background-color: #ff5b1a;
|
||||
color: white;
|
||||
}
|
||||
.navcard.black {
|
||||
background-color: #2f2f2f;
|
||||
color: white;
|
||||
}
|
||||
.navcard.black :active {
|
||||
background-color: #e1e1e1;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.navcard.purple {
|
||||
background-color: #794279;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.navcard.purple :active {
|
||||
background-color: #f3adf3;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.navcard.beige {
|
||||
background-color: #ecbb9e;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.navcard.beige :active {
|
||||
background-color: #735b4c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.navcard.red {
|
||||
background-color: #770101;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.navcard.red :active {
|
||||
background-color: #d5b2b2;
|
||||
color: black;
|
||||
}
|
||||
18
src/app/tusha/tusha-nav-card/tusha-nav-card.component.html
Normal file
18
src/app/tusha/tusha-nav-card/tusha-nav-card.component.html
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
<ion-card (click)="onClick()" class="navcard {{ color().color }}">
|
||||
<ion-card-content>
|
||||
<ion-grid class="innergrid">
|
||||
<ion-row>
|
||||
<ion-col size="1.5">
|
||||
<ng-content />
|
||||
</ion-col>
|
||||
<ion-col size="9.5">
|
||||
<p>{{ title() }}</p>
|
||||
</ion-col>
|
||||
<ion-col size="1">
|
||||
<ion-icon name="caret-forward"></ion-icon>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
28
src/app/tusha/tusha-nav-card/tusha-nav-card.component.ts
Normal file
28
src/app/tusha/tusha-nav-card/tusha-nav-card.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {Component, input, InputSignal, output} from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
import {
|
||||
IonCard, IonCardContent, IonCol,IonGrid, IonIcon, IonRow,
|
||||
} from "@ionic/angular/standalone";
|
||||
import {CardColor} from "./card-color";
|
||||
|
||||
@Component({
|
||||
selector: 'tusha-nav-card',
|
||||
templateUrl: 'tusha-nav-card.component.html',
|
||||
styleUrls: ['tusha-nav-card.component.css'],
|
||||
standalone: true,
|
||||
imports: [ IonIcon, IonCard, IonCardContent, IonGrid, IonRow, IonCol],
|
||||
styles: []
|
||||
})
|
||||
export class TushaNavCardComponent {
|
||||
title = input.required<String>();
|
||||
color: InputSignal<CardColor> = input<CardColor>({color: ''});
|
||||
|
||||
click = output<void>();
|
||||
|
||||
constructor(public router: Router) {
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.click.emit();
|
||||
}
|
||||
}
|
||||
9
src/app/tusha/tusha-nav-rows/nav-card-data.ts
Normal file
9
src/app/tusha/tusha-nav-rows/nav-card-data.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {CardColor} from "../tusha-nav-card/card-color";
|
||||
|
||||
export interface NavCardData {
|
||||
id: number;
|
||||
title: string;
|
||||
color: CardColor;
|
||||
icon: string;
|
||||
onclick: () => void;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@for (navcard of navcards(); track navcard.id) {
|
||||
<ion-row>
|
||||
<ion-col size="12">
|
||||
<tusha-nav-card [title]="navcard.title" [color]="navcard.color" (click)="navcard.onclick()">
|
||||
<ion-icon [name]="navcard.icon"></ion-icon>
|
||||
</tusha-nav-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
}
|
||||
22
src/app/tusha/tusha-nav-rows/tusha-nav-rows.component.ts
Normal file
22
src/app/tusha/tusha-nav-rows/tusha-nav-rows.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Component, input, Input} from "@angular/core";
|
||||
import {IonCol, IonGrid, IonIcon, IonRow,
|
||||
} from "@ionic/angular/standalone";
|
||||
import {NavCardData} from "./nav-card-data";
|
||||
import {TushaNavCardComponent} from "../tusha-nav-card/tusha-nav-card.component";
|
||||
import {Route, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'tusha-nav-rows',
|
||||
templateUrl: 'tusha-nav-rows.component.html',
|
||||
styleUrls: ['tusha-nav-rows.component.css'],
|
||||
standalone: true,
|
||||
imports: [IonIcon, IonGrid, IonRow, IonCol, TushaNavCardComponent],
|
||||
styles: []
|
||||
})
|
||||
export class TushaNavRowsComponent {
|
||||
navcards = input<NavCardData[]>([]);
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
22
src/app/tusha/tusha-page/tusha-page.component.css
Normal file
22
src/app/tusha/tusha-page/tusha-page.component.css
Normal file
@@ -0,0 +1,22 @@
|
||||
ion-grid {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
ion-grid .backpane {
|
||||
background-color: rgb(249, 239, 214);
|
||||
}
|
||||
|
||||
ion-grid.innergrid {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
ion-grid.innergrid ion-row {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
ion-grid.innergrid ion-row ion-col {
|
||||
margin: 0 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
19
src/app/tusha/tusha-page/tusha-page.component.html
Normal file
19
src/app/tusha/tusha-page/tusha-page.component.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ 'Tusha - Home' }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ng-content select="[slot=breadcrumb]"></ng-content>
|
||||
<ion-grid class="backpane">
|
||||
<ng-content />
|
||||
|
||||
</ion-grid>
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ 'Tusha - The Ultimate SelfHelp App - By AgileCC' }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
18
src/app/tusha/tusha-page/tusha-page.component.ts
Normal file
18
src/app/tusha/tusha-page/tusha-page.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {Component} from "@angular/core";
|
||||
import {
|
||||
IonBreadcrumb, IonBreadcrumbs,
|
||||
IonButtons, IonFooter, IonGrid, IonHeader, IonIcon, IonMenuButton, IonTitle, IonToolbar
|
||||
} from "@ionic/angular/standalone";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-tusha-page',
|
||||
templateUrl: 'tusha-page.component.html',
|
||||
styleUrls: ['tusha-page.component.css'],
|
||||
standalone: true,
|
||||
imports: [IonGrid, IonTitle, IonToolbar, IonFooter, IonMenuButton, IonButtons, IonHeader, IonBreadcrumb, IonBreadcrumbs, IonIcon],
|
||||
styles: []
|
||||
})
|
||||
export class TushaPageComponent {
|
||||
|
||||
}
|
||||
9
src/app/tusha/tusha-status-cards/status-cards-data.ts
Normal file
9
src/app/tusha/tusha-status-cards/status-cards-data.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {CardColor} from "../tusha-nav-card/card-color";
|
||||
|
||||
export interface StatusCardsData {
|
||||
id: number,
|
||||
color: CardColor,
|
||||
type: "number" | "percentage",
|
||||
amount: number,
|
||||
description: string
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
ion-card {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.scorecard {
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
}
|
||||
|
||||
.scorecard .content {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem 0.2rem 1rem 0.2rem;
|
||||
}
|
||||
|
||||
.scorecard .number {
|
||||
color: black;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.scoreboard .number, .large {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.scoreboard .number, .medium {
|
||||
font-size: 2.75rem;
|
||||
}
|
||||
|
||||
.scorecard.orange {
|
||||
background-color: lightsalmon;
|
||||
}
|
||||
|
||||
.scorecard.green {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
.scorecard.blue {
|
||||
background-color: #2898ff;
|
||||
}
|
||||
|
||||
.scorecard .description {
|
||||
color: black;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<ion-row>
|
||||
<ion-col size="6">
|
||||
<ion-card class={{card1_container_css}} id=`tusha-card-${card1().id}`>
|
||||
<ion-card-content class="content">
|
||||
<p class={{card1_number_css}}>{{ card1_number }}</p>
|
||||
<p class="description">{{ card1().description }}</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
<ion-col size="6">
|
||||
<ion-card class={{card2_container_css}} id=`tusha-card-${card2().id}`>
|
||||
<ion-card-content class="content">
|
||||
<p class={{card2_number_css}}>{{ card2_number }}</p>
|
||||
<p class="description">{{ card2().description }}</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
@@ -0,0 +1,67 @@
|
||||
import {Component, input, InputSignal, OnInit} from "@angular/core";
|
||||
import {StatusCardsData} from "./status-cards-data";
|
||||
import {IonCard, IonCardContent, IonCol, IonRow} from "@ionic/angular/standalone";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: "tusha-status-cards",
|
||||
templateUrl: "tusha-status-cards.component.html",
|
||||
styleUrls: ["tusha-status-cards.component.css"],
|
||||
standalone: true,
|
||||
imports: [IonRow, IonCol, IonCard, IonCardContent]
|
||||
})
|
||||
export class TushaStatusCardsComponent implements OnInit {
|
||||
card1: InputSignal<StatusCardsData> = input.required<StatusCardsData>();
|
||||
card2: InputSignal<StatusCardsData> = input.required<StatusCardsData>();
|
||||
card1_number_css: string = '';
|
||||
card2_number_css: string = '';
|
||||
card1_container_css: string = '';
|
||||
card2_container_css: string = '';
|
||||
card1_number: string = '';
|
||||
card2_number: string = '';
|
||||
|
||||
getSuffix(statusCard: StatusCardsData): string {
|
||||
console.log(statusCard);
|
||||
if (statusCard.type === 'percentage') {
|
||||
return '%';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
getSizeClass(statusCard: StatusCardsData): string {
|
||||
if (statusCard.type === 'percentage') {
|
||||
return 'medium';
|
||||
} else {
|
||||
return 'large';
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
function initCard(statusCardsData: StatusCardsData): { css_number: string, css_container: string, number: string } {
|
||||
if (statusCardsData.type === 'percentage') {
|
||||
return {
|
||||
css_container: 'scorecard ' + statusCardsData.color.color,
|
||||
css_number: 'number medium',
|
||||
number: statusCardsData.amount + ' %'
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
css_container: 'scorecard ' + statusCardsData.color.color,
|
||||
css_number: 'number large',
|
||||
number: statusCardsData.amount + ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const values1 = initCard(this.card1());
|
||||
this.card1_container_css = values1.css_container;
|
||||
this.card1_number_css = values1.css_number;
|
||||
this.card1_number = values1.number;
|
||||
|
||||
const values2 = initCard(this.card2());
|
||||
this.card2_container_css = values2.css_container;
|
||||
this.card2_number_css = values2.css_number;
|
||||
this.card2_number = values2.number;
|
||||
}
|
||||
}
|
||||
BIN
src/assets/hardstop.mp3
Normal file
BIN
src/assets/hardstop.mp3
Normal file
Binary file not shown.
BIN
src/assets/icon/favicon.png
Normal file
BIN
src/assets/icon/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 930 B |
BIN
src/assets/race-start-beeps-125125.mp3
Normal file
BIN
src/assets/race-start-beeps-125125.mp3
Normal file
Binary file not shown.
1
src/assets/shapes.svg
Normal file
1
src/assets/shapes.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/wind-up.mp3
Normal file
BIN
src/assets/wind-up.mp3
Normal file
Binary file not shown.
3
src/environments/environment.prod.ts
Normal file
3
src/environments/environment.prod.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
16
src/environments/environment.ts
Normal file
16
src/environments/environment.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
||||
26
src/global.scss
Normal file
26
src/global.scss
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* App Global CSS
|
||||
* ----------------------------------------------------------------------------
|
||||
* Put style rules here that you want to apply globally. These styles are for
|
||||
* the entire app and not just one component. Additionally, this file can be
|
||||
* used as an entry point to import other CSS/Sass files to be included in the
|
||||
* output CSS.
|
||||
* For more information on global stylesheets, visit the documentation:
|
||||
* https://ionicframework.com/docs/layout/global-stylesheets
|
||||
*/
|
||||
|
||||
/* Core CSS required for Ionic components to work properly */
|
||||
@import "@ionic/angular/css/core.css";
|
||||
|
||||
/* Basic CSS for apps built with Ionic */
|
||||
@import "@ionic/angular/css/normalize.css";
|
||||
@import "@ionic/angular/css/structure.css";
|
||||
@import "@ionic/angular/css/typography.css";
|
||||
@import "@ionic/angular/css/display.css";
|
||||
|
||||
/* Optional CSS utils that can be commented out */
|
||||
@import "@ionic/angular/css/padding.css";
|
||||
@import "@ionic/angular/css/float-elements.css";
|
||||
@import "@ionic/angular/css/text-alignment.css";
|
||||
@import "@ionic/angular/css/text-transformation.css";
|
||||
@import "@ionic/angular/css/flex-utils.css";
|
||||
29
src/index.html
Normal file
29
src/index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ionic App</title>
|
||||
|
||||
<base href="/" />
|
||||
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="msapplication-tap-highlight" content="no" />
|
||||
|
||||
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
|
||||
|
||||
<!-- add to homescreen for ios -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
|
||||
<script type="module" src="https://unpkg.com/ionicons@5.0.0/dist/ionicons/ionicons.esm.js"></script>
|
||||
<script nomodule="" src="https://unpkg.com/ionicons@5.0.0/dist/ionicons/ionicons.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
20
src/main.ts
Normal file
20
src/main.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { RouteReuseStrategy, provideRouter } from '@angular/router';
|
||||
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
|
||||
|
||||
import { routes } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
provideIonicAngular(),
|
||||
provideRouter(routes),
|
||||
],
|
||||
});
|
||||
55
src/polyfills.ts
Normal file
55
src/polyfills.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes recent versions of Safari, Chrome (including
|
||||
* Opera), Edge on the desktop, and iOS and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
import './zone-flags';
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js'; // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
14
src/test.ts
Normal file
14
src/test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting(),
|
||||
);
|
||||
244
src/theme/variables.scss
Normal file
244
src/theme/variables.scss
Normal file
@@ -0,0 +1,244 @@
|
||||
// Ionic Variables and Theming. For more info, please see:
|
||||
// http://ionicframework.com/docs/theming/
|
||||
|
||||
/** Ionic CSS Variables **/
|
||||
:root {
|
||||
/** primary **/
|
||||
--ion-color-primary: #3880ff;
|
||||
--ion-color-primary-rgb: 56, 128, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3171e0;
|
||||
--ion-color-primary-tint: #4c8dff;
|
||||
|
||||
/** secondary **/
|
||||
--ion-color-secondary: #3dc2ff;
|
||||
--ion-color-secondary-rgb: 61, 194, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #36abe0;
|
||||
--ion-color-secondary-tint: #50c8ff;
|
||||
|
||||
/** tertiary **/
|
||||
--ion-color-tertiary: #5260ff;
|
||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #4854e0;
|
||||
--ion-color-tertiary-tint: #6370ff;
|
||||
|
||||
/** success **/
|
||||
--ion-color-success: #2dd36f;
|
||||
--ion-color-success-rgb: 45, 211, 111;
|
||||
--ion-color-success-contrast: #ffffff;
|
||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-success-shade: #28ba62;
|
||||
--ion-color-success-tint: #42d77d;
|
||||
|
||||
/** warning **/
|
||||
--ion-color-warning: #ffc409;
|
||||
--ion-color-warning-rgb: 255, 196, 9;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0ac08;
|
||||
--ion-color-warning-tint: #ffca22;
|
||||
|
||||
/** danger **/
|
||||
--ion-color-danger: #eb445a;
|
||||
--ion-color-danger-rgb: 235, 68, 90;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #cf3c4f;
|
||||
--ion-color-danger-tint: #ed576b;
|
||||
|
||||
/** dark **/
|
||||
--ion-color-dark: #222428;
|
||||
--ion-color-dark-rgb: 34, 36, 40;
|
||||
--ion-color-dark-contrast: #ffffff;
|
||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-dark-shade: #1e2023;
|
||||
--ion-color-dark-tint: #383a3e;
|
||||
|
||||
/** medium **/
|
||||
--ion-color-medium: #92949c;
|
||||
--ion-color-medium-rgb: 146, 148, 156;
|
||||
--ion-color-medium-contrast: #ffffff;
|
||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-medium-shade: #808289;
|
||||
--ion-color-medium-tint: #9d9fa6;
|
||||
|
||||
/** light **/
|
||||
--ion-color-light: #f4f5f8;
|
||||
--ion-color-light-rgb: 244, 245, 248;
|
||||
--ion-color-light-contrast: #000000;
|
||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-light-shade: #d7d8da;
|
||||
--ion-color-light-tint: #f5f6f9;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66,140,255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255,255,255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80,200,255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106,100,255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47,223,117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0,0,0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255,213,52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255,73,97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244,245,248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152,154,162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34,36,40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255,255,255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0,0,0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18,18,18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
/*
|
||||
* For more information on dynamic font scaling, visit the documentation:
|
||||
* https://ionicframework.com/docs/layout/dynamic-font-scaling
|
||||
*/
|
||||
--ion-dynamic-font: var(--ion-default-dynamic-font);
|
||||
}
|
||||
6
src/zone-flags.ts
Normal file
6
src/zone-flags.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Prevents Angular change detection from
|
||||
* running with certain Web Component callbacks
|
||||
*/
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
(window as any).__Zone_disable_customElements = true;
|
||||
15
tsconfig.app.json
Normal file
15
tsconfig.app.json
Normal file
@@ -0,0 +1,15 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2022",
|
||||
"module": "es2020",
|
||||
"lib": ["es2018", "dom"],
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
18
tsconfig.spec.json
Normal file
18
tsconfig.spec.json
Normal file
@@ -0,0 +1,18 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user