CHORE: setup
This commit is contained in:
commit
1ad1418023
38 changed files with 17196 additions and 0 deletions
29
.forgejo/pull_request_template.yml
Normal file
29
.forgejo/pull_request_template.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
name: Feature
|
||||
title: '[Feature]: '
|
||||
about: 'Vorlage Für ein Feature Ticket'
|
||||
ref: 'trunk'
|
||||
body:
|
||||
- type: input
|
||||
id: ticket
|
||||
attributes:
|
||||
label: Ticket
|
||||
description: Für Welches Ticket ist diese Pull Request?
|
||||
placeholder: TD-1
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: beschreibung
|
||||
attributes:
|
||||
label: Beschreibung
|
||||
description: Was hast du genau gemacht in diesem Ticket?
|
||||
placeholder: ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: more
|
||||
attributes:
|
||||
label: Weitere Infos
|
||||
description: Gibt es noch etwas das ich wissen muss um das Ticket zu Reviewn?
|
||||
placeholder: ...
|
||||
validations:
|
||||
required: false
|
3
.forgejo/workflows/Dockerfile
Normal file
3
.forgejo/workflows/Dockerfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
FROM httpd:2.4
|
||||
|
||||
ADD web /usr/local/apache2/htdocs/
|
84
.forgejo/workflows/build.yml
Normal file
84
.forgejo/workflows/build.yml
Normal file
|
@ -0,0 +1,84 @@
|
|||
name: Build Application
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?'
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: stable
|
||||
container:
|
||||
image: git.euph.dev/actions/runner-java-21:latest
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "https://git.euph.dev/actions/checkout@v3"
|
||||
- name: "Install Angular CLI"
|
||||
run: sudo npm i -g @angular/cli
|
||||
- name: "Install Dependencies"
|
||||
run: npm ci
|
||||
- name: Build Bundle
|
||||
run: ng build
|
||||
- name: Upload Binary as Artifact
|
||||
uses: "https://git.euph.dev/actions/upload-artifact@v3"
|
||||
with:
|
||||
name: web
|
||||
path: dist/tower-defence-companion/browser/*
|
||||
|
||||
build-docker:
|
||||
runs-on: docker
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "https://git.euph.dev/actions/checkout@v3"
|
||||
- name: Download artifact from previous job
|
||||
uses: "https://git.euph.dev/actions/download-artifact@v3"
|
||||
with:
|
||||
name: web
|
||||
path: .forgejo/workflows/web
|
||||
- name: Login to Registry
|
||||
uses: "https://git.euph.dev/actions/docker-login@v3"
|
||||
with:
|
||||
registry: git.euph.dev
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
password: ${{ secrets.DEPLOY_SECRET }}
|
||||
- name: Build and push Web Image
|
||||
uses: "https://git.euph.dev/actions/docker-build-push@v5"
|
||||
with:
|
||||
context: ".forgejo/workflows/"
|
||||
push: true
|
||||
tags: |
|
||||
git.euph.dev/towerdefence/web-companion:${{ github.ref_name }}
|
||||
${{ contains(github.ref_name, 'rc') == false && 'git.euph.dev/towerdefence/web-companion:latest' || '' }}
|
||||
|
||||
release:
|
||||
runs-on: stable
|
||||
container:
|
||||
image: git.euph.dev/actions/runner-basic:latest
|
||||
needs:
|
||||
- build
|
||||
- build-docker
|
||||
steps:
|
||||
- name: Create Empty Release Asset Dir
|
||||
run: mkdir -p tmp/this/dir/does/not/exist/yet
|
||||
- name: Create Release
|
||||
uses: "https://git.euph.dev/actions/release@v2"
|
||||
with:
|
||||
direction: upload
|
||||
tag: ${{ github.ref_name }}
|
||||
token: ${{ secrets.DEPLOY_TOKEN }}
|
||||
prerelease: ${{ contains( github.ref_name, "rc") }}
|
||||
release-dir: tmp/this/dir/does/not/exist/yet
|
||||
release-notes: |
|
||||
# Tower Defence - Companion ${{ github.ref_name }}
|
||||
Run this release with docker like this:
|
||||
\`\`\`sh
|
||||
docker run --rm -p 4300:80 git.euph.dev/towerdefence/web-companion:${{ github.ref_name }}
|
||||
\`\`\`
|
||||
It will be available under [\`localhost:4300\`](localhost:4300)
|
||||
<br><br>
|
||||
For more information read the [Documentation](https://git.euph.dev/TowerDefence/Dokumentation/wiki/Companion/Config)
|
||||
|
||||
|
28
.forgejo/workflows/qs.yml
Normal file
28
.forgejo/workflows/qs.yml
Normal file
|
@ -0,0 +1,28 @@
|
|||
name: "Quality Check"
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
name: "Linting"
|
||||
runs-on: stable
|
||||
container:
|
||||
image: "git.euph.dev/actions/runner-basic:latest"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "https://git.euph.dev/actions/checkout@v3"
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.node_modules
|
||||
key: ${{ runner.os }}-${{ hashFiles('package-lock.json', 'eslint.config.mjs', '.stylelintrc', '.stylelintignore') }}
|
||||
restore-keys: ${{ runner.os }}-
|
||||
- name: "NPM install"
|
||||
run: npm i
|
||||
- name: "Linting TS"
|
||||
run: npm run lint:ts
|
||||
- name: "Linting SCSS"
|
||||
run: npm run lint:scss
|
||||
|
||||
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# 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-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Generated
|
||||
src/app/core/server/**/*
|
||||
openapitools.json
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
*
|
3
.stylelintignore
Normal file
3
.stylelintignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.*
|
||||
!*.css
|
||||
!*.scss
|
14
.stylelintrc.json
Normal file
14
.stylelintrc.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "stylelint-config-standard-scss",
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
"rules": {
|
||||
"custom-property-empty-line-before": null,
|
||||
"declaration-empty-line-before": null,
|
||||
"media-feature-range-notation": null,
|
||||
"import-notation": "string",
|
||||
"scss/no-global-function-names": null,
|
||||
"no-empty-source": null
|
||||
}
|
||||
}
|
13
Justfile
Normal file
13
Justfile
Normal file
|
@ -0,0 +1,13 @@
|
|||
_choose:
|
||||
just --choose
|
||||
|
||||
up:
|
||||
docker compose up -d
|
||||
ng serve
|
||||
|
||||
down:
|
||||
docker compose down
|
||||
|
||||
lint:
|
||||
-npm run lint:ts:fix
|
||||
-npm run lint:scss:fix
|
1
License.md
Normal file
1
License.md
Normal file
|
@ -0,0 +1 @@
|
|||
See License [here](https://git.euph.dev/TowerDefence/.profile/src/branch/main/License.md)
|
3
Readme.md
Normal file
3
Readme.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
[![QS Badge](https://git.euph.dev/TowerDefence/Companion/actions/workflows/qs.yml/badge.svg?branch=trunk&style=for-the-badge&label=QS)](https://git.euph.dev/TowerDefence/Companion/actions?workflow=qs.yml)
|
||||
[![Build Badge](https://git.euph.dev/TowerDefence/Companion/actions/workflows/build.yml/badge.svg?style=for-the-badge&label=Build)](https://git.euph.dev/TowerDefence/Companion/actions?workflow=build.yml)
|
||||
# Tower Defence - Companion
|
102
angular.json
Normal file
102
angular.json
Normal file
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"Tower-Defence-Companion": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/tower-defence-companion",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "Tower-Defence-Companion:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "Tower-Defence-Companion:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
eslint.config.mjs
Normal file
63
eslint.config.mjs
Normal file
|
@ -0,0 +1,63 @@
|
|||
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import globals from "globals";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import path from "node:path";
|
||||
import {fileURLToPath} from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import {FlatCompat} from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ["src/app/core/server"],
|
||||
},
|
||||
...compat.extends("eslint:recommended", "plugin:@typescript-eslint/strict"),
|
||||
{
|
||||
plugins: {
|
||||
"simple-import-sort": simpleImportSort,
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
|
||||
rules: {
|
||||
indent: ["error", 4],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
quotes: ["error", "single"],
|
||||
semi: ["error", "always"],
|
||||
strict: "error",
|
||||
"array-bracket-newline": "error",
|
||||
yoda: "error",
|
||||
|
||||
"@typescript-eslint/array-type": [
|
||||
"error",
|
||||
{
|
||||
default: "generic",
|
||||
},
|
||||
],
|
||||
|
||||
"@typescript-eslint/ban-tslint-comment": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-extraneous-class": "off",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"no-mixed-spaces-and-tabs": "off",
|
||||
},
|
||||
},
|
||||
]
|
16280
package-lock.json
generated
Normal file
16280
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
56
package.json
Normal file
56
package.json
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "tower-defence-companion",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"lint:ts": "eslint src",
|
||||
"lint:scss": "stylelint src",
|
||||
"lint:ts:fix": "eslint src --fix",
|
||||
"lint:scss:fix": "stylelint src --fix"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^19.0.0",
|
||||
"@angular/common": "^19.0.0",
|
||||
"@angular/compiler": "^19.0.0",
|
||||
"@angular/core": "^19.0.0",
|
||||
"@angular/forms": "^19.0.0",
|
||||
"@angular/material": "^19.1.2",
|
||||
"@angular/platform-browser": "^19.0.0",
|
||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||
"@angular/router": "^19.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.0.5",
|
||||
"@angular/cli": "^19.0.5",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.22.0",
|
||||
"@typescript-eslint/parser": "^8.22.0",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-autofix": "^2.2.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.14.0",
|
||||
"jasmine-core": "~5.4.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",
|
||||
"stylelint": "^16.14.1",
|
||||
"stylelint-config-standard-scss": "^14.0.0",
|
||||
"stylelint-scss": "^6.11.0",
|
||||
"typescript": "~5.6.2"
|
||||
},
|
||||
"api_version": "v0.0.0-rc.2",
|
||||
"volta": {
|
||||
"node": "22.13.1"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
6
src/app/app.component.html
Normal file
6
src/app/app.component.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<app-header></app-header>
|
||||
<app-notification-box></app-notification-box>
|
||||
<main>
|
||||
<h1>{{title.getTitle()}}</h1>
|
||||
<router-outlet />
|
||||
</main>
|
24
src/app/app.component.scss
Normal file
24
src/app/app.component.scss
Normal file
|
@ -0,0 +1,24 @@
|
|||
app-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main {
|
||||
flex-grow: 1;
|
||||
min-width: 100%;
|
||||
width: fit-content;
|
||||
box-sizing: border-box;
|
||||
padding: 2rem;
|
||||
|
||||
@media (min-width: 60rem) {
|
||||
width: 80%;
|
||||
min-width: 60rem;
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
15
src/app/app.component.ts
Normal file
15
src/app/app.component.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { HeaderComponent } from '@app/header/header.component';
|
||||
import { NotificationBoxComponent } from '@app/notification-box/notification-box.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, HeaderComponent, NotificationBoxComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(public title: Title) { }
|
||||
}
|
15
src/app/app.config.ts
Normal file
15
src/app/app.config.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideAnimationsAsync(),
|
||||
provideRouter(routes),
|
||||
provideHttpClient(),
|
||||
]
|
||||
};
|
4
src/app/app.routes.ts
Normal file
4
src/app/app.routes.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { DashboardComponent } from '@app/views/dashboard/dashboard.component';
|
||||
|
||||
export const routes: Routes = [{ path: '', component: DashboardComponent, title: 'Home' }];
|
37
src/app/core/notification/notification.service.ts
Normal file
37
src/app/core/notification/notification.service.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
|
||||
export interface Notification {
|
||||
msg: string;
|
||||
type: NotificationType;
|
||||
}
|
||||
|
||||
export enum NotificationType {
|
||||
Information = 'info',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
export type NotificationCallback = (notification: Notification) => void;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NotificationService {
|
||||
private readonly bus: Subject<Notification> = new Subject();
|
||||
private readonly subscribers: Map<string, Subscription> = new Map<string, Subscription>();
|
||||
|
||||
subscribe(subscriberId: string, callback: NotificationCallback): void {
|
||||
this.subscribers.set(
|
||||
subscriberId,
|
||||
this.bus.subscribe({
|
||||
next: callback
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
publish(msg: string, type: NotificationType = NotificationType.Information) {
|
||||
this.bus.next({ msg, type });
|
||||
}
|
||||
|
||||
unsubscribe(subscriberId: string) {
|
||||
this.subscribers.get(subscriberId)?.unsubscribe();
|
||||
}
|
||||
}
|
11
src/app/header/header.component.html
Normal file
11
src/app/header/header.component.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<mat-toolbar class="header">
|
||||
<nav>
|
||||
<a routerLink="" mat-button>
|
||||
<mat-icon>smartphone</mat-icon>
|
||||
Tower Defence Companion
|
||||
</a>
|
||||
@for (route of routes; track route) {
|
||||
<a mat-button routerLink="{{ route.path }}" class="{{ route.class }}">{{ route.title }}</a>
|
||||
}
|
||||
</nav>
|
||||
</mat-toolbar>
|
45
src/app/header/header.component.scss
Normal file
45
src/app/header/header.component.scss
Normal file
|
@ -0,0 +1,45 @@
|
|||
.header {
|
||||
margin: 0;
|
||||
padding: 0.5rem 1rem;
|
||||
z-index: 100;
|
||||
height: fit-content;
|
||||
background-color: var(--mat-sys-primary-container);
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
list-style: none;
|
||||
gap: 0.25rem;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
a:first-of-type {
|
||||
display: none;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
color: var(--mat-sys-primary);
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: var(--mat-sys-primary-overlay)
|
||||
}
|
||||
}
|
||||
|
||||
&__login {
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
>a:last-of-type {
|
||||
display: none;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
42
src/app/header/header.component.ts
Normal file
42
src/app/header/header.component.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatAnchor } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatToolbar } from '@angular/material/toolbar';
|
||||
import { EventType, Router, RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
imports: [
|
||||
MatToolbar,
|
||||
MatAnchor,
|
||||
RouterLink,
|
||||
MatIcon,
|
||||
],
|
||||
templateUrl: './header.component.html',
|
||||
styleUrl: './header.component.scss'
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
routes: Array<{ path: string, title: string, class: string }> = [];
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const routes = this.router.config
|
||||
.filter(route => !route.path?.includes(':'))
|
||||
.filter(route => !route.path?.includes('/'));
|
||||
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event.type != EventType.NavigationEnd) {
|
||||
return;
|
||||
}
|
||||
this.routes = routes.map(route => ({
|
||||
path: `/${route.path}`,
|
||||
title: route.title as string,
|
||||
class: `/${route.path}` == event.url ? 'active' : ''
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
7
src/app/notification-box/notification-box.component.html
Normal file
7
src/app/notification-box/notification-box.component.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<div class="notification-box">
|
||||
@for (notification of notifications.values(); track notification){
|
||||
<mat-card class="notification-box__card {{notification.type}}" [@slideInOut]>
|
||||
<mat-card-content>{{notification.msg}}</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
</div>
|
36
src/app/notification-box/notification-box.component.scss
Normal file
36
src/app/notification-box/notification-box.component.scss
Normal file
|
@ -0,0 +1,36 @@
|
|||
.notification-box {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 0;
|
||||
top: 3.5rem; // Header Height
|
||||
|
||||
width: 15%;
|
||||
min-width: 20rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
gap: 0.5rem;
|
||||
|
||||
@media (max-width: 20rem) {
|
||||
min-width: calc(100% - 0.5rem);
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
&__card {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.info {
|
||||
color: var(--mat-sys-tertiary);
|
||||
background-color: var(--mat-sys-tertiary-container);
|
||||
}
|
||||
|
||||
&.error {
|
||||
color: var(--mat-sys-error);
|
||||
background-color: var(--mat-sys-error-container);
|
||||
}
|
||||
}
|
||||
}
|
41
src/app/notification-box/notification-box.component.ts
Normal file
41
src/app/notification-box/notification-box.component.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { Notification, NotificationService } from '@core/notification/notification.service';
|
||||
|
||||
const NOTIFICATION_TTL = 3000;
|
||||
|
||||
@Component({
|
||||
selector: 'app-notification-box',
|
||||
imports: [MatCardModule],
|
||||
animations: [
|
||||
trigger('slideInOut', [
|
||||
transition(':enter', [
|
||||
style({ transform: 'translateX(100%)' }),
|
||||
animate('200ms ease-in-out', style({ transform: 'translateX(0)' })),
|
||||
]),
|
||||
transition(':leave', [animate('200ms ease-in-out', style({ transform: 'translateX(100%)' })),])
|
||||
]),
|
||||
],
|
||||
templateUrl: './notification-box.component.html',
|
||||
styleUrl: './notification-box.component.scss'
|
||||
})
|
||||
export class NotificationBoxComponent implements OnInit {
|
||||
notifications: Map<number, Notification> = new Map();
|
||||
|
||||
constructor(private notificationService: NotificationService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.notificationService.subscribe('notification-box', this.onNotification.bind(this));
|
||||
}
|
||||
|
||||
onNotification(notification: Notification): void {
|
||||
const now = Date.now();
|
||||
this.notifications.set(now, notification);
|
||||
|
||||
setTimeout(() => {
|
||||
this.notifications.delete(now);
|
||||
}, NOTIFICATION_TTL);
|
||||
}
|
||||
}
|
3
src/app/views/dashboard/dashboard.component.html
Normal file
3
src/app/views/dashboard/dashboard.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="dashboard">
|
||||
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Facere illo animi quidem repellat perspiciatis, excepturi amet corrupti ipsa sit consequuntur placeat ratione saepe velit asperiores suscipit esse quod minima exercitationem minus, alias laudantium inventore! Beatae cum nobis error suscipit cupiditate, praesentium itaque ut ipsa iusto in doloribus unde quisquam consequuntur.</p>
|
||||
</div>
|
0
src/app/views/dashboard/dashboard.component.scss
Normal file
0
src/app/views/dashboard/dashboard.component.scss
Normal file
11
src/app/views/dashboard/dashboard.component.ts
Normal file
11
src/app/views/dashboard/dashboard.component.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
imports: [],
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrl: './dashboard.component.scss'
|
||||
})
|
||||
export class DashboardComponent {
|
||||
|
||||
}
|
15
src/index.html
Normal file
15
src/index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tower Defence - Companion</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body class="mat-typography">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
6
src/main.ts
Normal file
6
src/main.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from '@app/app.component';
|
||||
import { appConfig } from '@app/app.config';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
82
src/styles.scss
Normal file
82
src/styles.scss
Normal file
|
@ -0,0 +1,82 @@
|
|||
@use '@angular/material' as mat;
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
|
||||
background: var(--mat-sys-surface);
|
||||
color: var(--mat-sys-on-surface);
|
||||
|
||||
--mat-sys-primary-overlay: color-mix(in srgb, var(--mat-sys-primary) 10%, transparent);
|
||||
|
||||
color-scheme: light dark;
|
||||
font-family: var(--mat-sys-label-medium-font);
|
||||
|
||||
@include mat.theme((
|
||||
color: mat.$azure-palette,
|
||||
typography: Roboto,
|
||||
density: 0
|
||||
));
|
||||
}
|
||||
|
||||
app-root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mdc-button,
|
||||
.mdc-fab {
|
||||
&.abort,
|
||||
&.error,
|
||||
&.warn {
|
||||
$ripple: var(color-mix(in srgb, var(--mat-sys-on-error) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent));
|
||||
$fg: var(--mat-sys-on-error);
|
||||
$bg: var(--mat-sys-error);
|
||||
$fg-container: var(--mat-sys-on-error-container);
|
||||
$bg-container: var(--mat-sys-error-container);
|
||||
|
||||
@include mat.button-overrides((
|
||||
// default
|
||||
text-label-text-color: $bg,
|
||||
text-ripple-color: $ripple,
|
||||
text-state-layer-color: $bg,
|
||||
// filled
|
||||
filled-label-text-color: $fg,
|
||||
filled-container-color: $bg,
|
||||
filled-ripple-color: $ripple,
|
||||
filled-state-layer-color: $fg,
|
||||
));
|
||||
@include mat.fab-overrides((
|
||||
// default
|
||||
container-color: $bg-container,
|
||||
foreground-color: $fg-container,
|
||||
state-layer-color: $fg-container,
|
||||
ripple-color: $ripple,
|
||||
// mini
|
||||
small-container-color: $bg-container,
|
||||
small-foreground-color: $fg-container,
|
||||
small-state-layer-color: $fg-container,
|
||||
small-ripple-color: $ripple
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
.mdc-fab.shadowless {
|
||||
@include mat.fab-overrides((
|
||||
// default
|
||||
container-elevation-shadow: transparent,
|
||||
hover-container-elevation-shadow: transparent,
|
||||
pressed-container-elevation-shadow: transparent,
|
||||
focus-container-elevation-shadow: transparent,
|
||||
// mini
|
||||
small-container-elevation-shadow: transparent,
|
||||
small-hover-container-elevation-shadow: transparent,
|
||||
small-pressed-container-elevation-shadow: transparent,
|
||||
small-focus-container-elevation-shadow: transparent,
|
||||
));
|
||||
}
|
15
tsconfig.app.json
Normal file
15
tsconfig.app.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
43
tsconfig.json
Normal file
43
tsconfig.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"dom"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@app/*": [
|
||||
"./src/app/*"
|
||||
],
|
||||
"@core/*": [
|
||||
"./src/app/core/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
15
tsconfig.spec.json
Normal file
15
tsconfig.spec.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
Loading…
Add table
Reference in a new issue