Compare commits

...

18 Commits

Author SHA1 Message Date
5cdd313e5b Merge pull request 'develop' (#3) from develop into main
Reviewed-on: #3
2026-11-12 16:37:00 +01:00
Wanja Krah
b9cbfa0d50 Package lock maintenance
Some checks failed
Build and Deploy develop / builds (push) Has been cancelled
2025-04-03 16:35:37 +02:00
32b244a42e should run on host 2024-12-28 20:41:04 +01:00
94e5973f88 docker action yaml 2024-12-28 20:32:19 +01:00
712baefa7f opzet voor gitea actions 2024-12-26 21:30:35 +01:00
dd6dbb6236 docker setup 2024-12-26 20:24:15 +01:00
170b472545 prevents the stop-sound being placed twice 2024-12-17 21:35:43 +01:00
e2232af098 fixes the end-of-timer-chime being played continiously 2024-12-17 21:29:54 +01:00
2a1a68e969 leesbare tijdseenheid in stats plus timer door laten lopen na einde 2024-12-10 18:55:32 +01:00
cf3f5b11ac correct timers 2024-12-09 21:20:09 +01:00
64ca83083e working on logging process and adding sounds 2024-12-03 11:56:54 +01:00
17af0f2015 First try getting focusstats in 2024-12-02 07:32:00 +01:00
6434fc195e Beslissing voor backend vastgelegd 2024-11-28 13:10:28 +01:00
7c457ba8c7 Opzet businesslogica opzetten focus-stats 2024-11-26 20:56:43 +01:00
a168fa31c0 sound on finishing time 2024-11-16 19:51:16 +01:00
4e3431e7a7 Finished the pomodoro timer
except for a sound, the settings and perfect centering on mobile devices
2024-11-15 18:54:28 +01:00
c75f322a0c Move from old git to gittea 2024-11-08 10:17:11 +01:00
48b2880ea0 commit gitignore file 2024-11-08 10:14:59 +01:00
71 changed files with 19855 additions and 0 deletions

16
.browserslistrc Normal file
View 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
View 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
View 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": {}
}
]
}

View 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
View 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
View 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

View File

@@ -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.

View 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

View 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 ------

View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
{
"name": "tusha-fe",
"integrations": {
"capacitor": {}
},
"type": "angular-standalone"
}

44
karma.conf.js Normal file
View 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
});
};

17340
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

66
package.json Normal file
View 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.6",
"@capacitor/core": "5.6.0",
"@capacitor/haptics": "5.0.6",
"@capacitor/keyboard": "5.0.7",
"@capacitor/status-bar": "5.0.6",
"@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.6.0",
"@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.6",
"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"
}

View 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&#64;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
View 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);
}

View 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
View 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
View 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),
},
];

View 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>

View 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;
}

View 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();
});
});

View 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;
}
}

View File

View File

@@ -0,0 +1,11 @@
ion-footer ion-title {
position: absolute;
right: 0px;
color: darkgray;
font-size: 0.75rem;
}

View 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>

View 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']);
}
}

View 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';
}

View File

@@ -0,0 +1,3 @@
export enum FocusState {
FOCUS = 'in_focus', SHORT_BREAK = 'short_break', LONG_BREAK = 'long_break', STOPPED = 'stopped'
}

View 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;
}
}

View 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;
}

View 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>

View 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
}
}

View 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);
}
}
}

View 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>

View 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']);
}
}

View File

@@ -0,0 +1,3 @@
export interface CardColor {
color: '' | 'blue' | 'green' | 'orange' | 'black' | 'purple' | 'beige' | 'red';
}

View 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;
}

View 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>

View 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();
}
}

View 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;
}

View File

@@ -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>
}

View 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() {
}
}

View 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;
}

View 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>

View 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 {
}

View 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
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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

Binary file not shown.

BIN
src/assets/icon/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

1
src/assets/shapes.svg Normal file
View 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

Binary file not shown.

View File

@@ -0,0 +1,3 @@
export const environment = {
production: true
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"
]
}