add README.md for some configuration and building the app

This commit is contained in:
atseirjo 2025-10-07 13:32:24 +02:00
parent fd00141fba
commit aeb77ed1cd
39 changed files with 11742 additions and 7 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# 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
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false

5
.postcssrc.json Normal file
View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

4
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

28
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,28 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Chrome against localhost",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/",
"webRoot": "${workspaceFolder}",
},
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

View File

@ -12,4 +12,4 @@ COPY . .
RUN ng build --configuration=production RUN ng build --configuration=production
CMD [ "ng", "serve","--port", "4240", "--host", "0.0.0.0" ] CMD [ "ng", "serve","--port", "4260", "--host", "0.0.0.0" ]

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Appwrite
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,26 +1,37 @@
# Angular starter kit with Appwrite # Angular Template with Login and Register with Appwrite
Kickstart your Angular development with this ready-to-use starter project integrated with [Appwrite](https://www.appwrite.io) Kickstart your Angular development with this ready-to-use starter project integrated with [Appwrite](https://www.appwrite.io)
## 🚀Getting started ## 🚀Getting started
### ###
Clone the Project Clone the Project
Clone this repository to your local machine using Git: Clone this repository to your local machine using Git:
`git clone https://github.com/appwrite/starter-for-angular` `git clone https://gitea.joshihomeserver.ipv64.net/josiadmin/appwrite-template-login`
## 🛠️ Development guide ## 🛠️ Development guide
1. **Configure Appwrite**<br/>
1. **Configure Appwrite**
Navigate to `/environments/environment{.development}.ts` and update the values to match your Appwrite project credentials. Navigate to `/environments/environment{.development}.ts` and update the values to match your Appwrite project credentials.
2. **Customize as needed**<br/> 2. **Customize as needed**
Modify the starter kit to suit your app's requirements. Adjust UI, features, or backend Modify the starter kit to suit your app's requirements. Adjust UI, features, or backend
integrations as per your needs. integrations as per your needs.
3. **Install dependencies**<br/> 3. **Install Angular/CLI**
Run`npm install -g @angular/cli`
4. **Install dependencies**
Run `npm install` to install all dependencies. Run `npm install` to install all dependencies.
4. **Run the app**<br/> 5. **Run the app**
Start the project by running `ng serve`. Start the project by running `ng serve`.
6. **Run in Debug Mode in VsCode by**
Tipping F5.
## 💡 Additional notes ## 💡 Additional notes
- This starter project is designed to streamline your Angular development with Appwrite. - This starter project is designed to streamline your Angular development with Appwrite.
- Refer to the [Appwrite documentation](https://appwrite.io/docs) for detailed integration guidance. - Refer to the [Appwrite documentation](https://appwrite.io/docs) for detailed integration guidance.
- Images in the public/img Folder are test Images to change with yours, it will be loading in the styles.css File
- A Dockerfile is on the root Directory to make on a Docker Server a full Web-App
- Bevore you make a full Web-App you need to build the app for Production with `ng build --configuration=production` .
- In the angular.json file you make a entry under server with option to your one web-address-server, in this file you find the etry by searching for `test.123server.at`.

127
angular.json Normal file
View File

@ -0,0 +1,127 @@
{
"$schema": "@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-starter-kit-for-appwrite": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/web-kegel-app-angular",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.css"],
"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,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"options": {
"allowedHosts": [
"test.123server.at"
]
},
"configurations": {
"production": {
"buildTarget": "angular-starter-kit-for-appwrite:build:production"
},
"development": {
"buildTarget": "angular-starter-kit-for-appwrite:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.css"],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
},
"schematics": {
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
}
}

10673
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
package.json Normal file
View File

@ -0,0 +1,45 @@
{
"name": "web-kegel-app-angular",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^20.3.2",
"@angular/common": "^20.3.2",
"@angular/compiler": "^20.3.2",
"@angular/core": "^20.3.2",
"@angular/forms": "^20.3.2",
"@angular/platform-browser": "^20.3.2",
"@angular/platform-browser-dynamic": "^20.3.2",
"@angular/router": "^20.3.2",
"@fortawesome/fontawesome-free": "^7.0.1",
"@tailwindcss/postcss": "^4.0.15",
"appwrite": "^17.0.0",
"postcss": "^8.5.3",
"rxjs": "~7.8.0",
"tailwindcss": "^4.0.15",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular/build": "^20.3.3",
"@angular/cli": "^20.3.3",
"@angular/compiler-cli": "^20.3.2",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.5.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",
"postcss": "^8.5.3",
"prettier": "^3.5.1",
"typescript": "~5.9.3"
}
}

13
prepare-env.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
# Script used during deployment on Appwrite Sites
# Replace [appwriteEndpoint] with APPWRITE_ENDPOINT in environments files
sed -i "s|\[appwriteEndpoint\]|$APPWRITE_ENDPOINT|g" src/environments/*.ts
# Replace [appwriteProjectId] with APPWRITE_PROJECT_ID in environments files
sed -i "s|\[appwriteProjectId\]|$APPWRITE_PROJECT_ID|g" src/environments/*.ts
# Replace [appwriteProjectName] with APPWRITE_PROJECT_NAME in environments files
sed -i "s|\[appwriteProjectName\]|$APPWRITE_PROJECT_NAME|g" src/environments/*.ts

14
public/angular.svg Normal file
View File

@ -0,0 +1,14 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 250 250" style="enable-background:new 0 0 250 250;" xml:space="preserve">
<style type="text/css">
.st0{fill:#DD0031;}
.st1{fill:#C3002F;}
.st2{fill:#FFFFFF;}
</style>
<g>
<polygon class="st0" points="125,30 125,30 125,30 31.9,63.2 46.1,186.3 125,230 125,230 125,230 203.9,186.3 218.1,63.2 "/>
<polygon class="st1" points="125,30 125,52.2 125,52.1 125,153.4 125,153.4 125,230 125,230 203.9,186.3 218.1,63.2 125,30 "/>
<path class="st2" d="M125,52.1L66.8,182.6h0h21.7h0l11.7-29.2h49.4l11.7,29.2h0h21.7h0L125,52.1L125,52.1L125,52.1L125,52.1
L125,52.1z M142,135.4H108l17-40.9L142,135.4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 762 B

8
public/appwrite.svg Normal file
View File

@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M24.4429 16.4322V21.9096H10.7519C6.76318 21.9096 3.28044 19.7067 1.4171 16.4322C1.14622 15.9561 0.909137 15.4567 0.710264 14.9383C0.319864 13.9225 0.0744552 12.8325 0 11.6952V10.2143C0.0161646 9.96089 0.0416361 9.70942 0.0749451 9.46095C0.143032 8.95105 0.245898 8.45211 0.381093 7.96711C1.66006 3.36909 5.81877 0 10.7519 0C15.6851 0 19.8433 3.36909 21.1223 7.96711H15.2682C14.3072 6.4683 12.6437 5.4774 10.7519 5.4774C8.86017 5.4774 7.19668 6.4683 6.23562 7.96711C5.9427 8.42274 5.71542 8.92516 5.56651 9.46095C5.43425 9.93599 5.36371 10.4369 5.36371 10.9548C5.36371 12.5248 6.01324 13.94 7.05463 14.9383C8.01961 15.865 9.32061 16.4322 10.7519 16.4322H24.4429Z"
fill="#FD366E" />
<path
d="M24.4429 9.46094V14.9383H14.4492C15.4906 13.94 16.1401 12.5248 16.1401 10.9548C16.1401 10.4369 16.0696 9.93598 15.9373 9.46094H24.4429Z"
fill="#FD366E" />
</svg>

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

View File

@ -0,0 +1,5 @@
<main>
<!-- Hier wird der Inhalt deiner gerouteten Seiten angezeigt -->
<router-outlet></router-outlet>
</main>

16
src/app/app.component.ts Normal file
View File

@ -0,0 +1,16 @@
import { Component} from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent{
}

14
src/app/app.config.ts Normal file
View File

@ -0,0 +1,14 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { importProvidersFrom } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; // <-- Wichtig!
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
importProvidersFrom(ReactiveFormsModule) // <-- Bereitstellen
],
};

10
src/app/app.routes.ts Normal file
View File

@ -0,0 +1,10 @@
import { Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
export const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent},
{ path: '**', redirectTo: '/login' },
];

View File

@ -0,0 +1,13 @@
.headerText {
text-align: center;
color: rgb(42, 42, 88);
font-weight: bold;
font-size: 1.75rem;
font-family: "Libre Baskerville", serif;
}
i{
color: rgb(42, 42, 88);
cursor: pointer;
}

View File

@ -0,0 +1,8 @@
<div class="flex flex-row justify-between items-center bg-red-500/80 py-4 px-2">
<h2 class="headerText pl-4 ">Kegel Spielplan</h2>
<div>
<i class="fa-solid fa-file-lines fa-2xl pr-10" (click)="goToSummery()"></i>
<i class="fa-solid fa-file-circle-plus fa-2xl pr-10" (click)="goToInput()" ></i>
<i class="fa-solid fa-right-from-bracket fa-2xl pr-10" (click)="logout()"></i>
</div>
</div>

View File

@ -0,0 +1,40 @@
import { Component } from '@angular/core';
import { AppwriteService } from '../../services/appwrite.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-header',
standalone: true,
imports: [],
templateUrl: './header.component.html',
styleUrl: './header.component.css',
})
export class HeaderComponentComponent {
constructor(
private appwriteService: AppwriteService,
private routes: Router,
) {}
goToSummery() {
// this.routes.navigate(['/list']);
}
goToInput() {
// this.routes.navigate(['/input']);
}
async logout() {
try {
const accountService = await this.appwriteService.accountService;
await this.appwriteService.accountService.deleteSession(
(await accountService.getSession('current')).$id,
);
// Bei erfolgreicher Abmeldung
//this.toast.success('Logout erfolgreich!', 'Auf Wiedersehen!');
this.routes.navigate(['/login']);
} catch (err: any) {
console.error('Fehler beim Abmelden:', err.message);
}
}
}

View File

@ -0,0 +1,47 @@
<main class="login-container">
<div class="mt-8 flex flex-col items-center justify-center rounded-lg bg-red-500/30 p-6 shadow-md">
<h2 class="mb-4 text-4xl font-bold text-[#000000FF]">Pinin</h2>
<form class="flex flex-col gap-4" (ngSubmit)="login()">
<div>
<label for="username" class="sr-only">Benutzername</label>
<input
id="username"
type="email"
[(ngModel)]="email"
placeholder="E-Mail-Adresse"
name="email"
required
class="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-[#000000] focus:outline-none focus:ring-1 focus:ring-[#030303]
text-black
text-xl
bg-white"
/>
</div>
<div>
<label for="password" class="sr-only">Passwort</label>
<input
id="password"
type="password"
placeholder="Passwort"
class="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-[#000000] focus:outline-none focus:ring-1 focus:ring-[#000000] text-black
text-xl
bg-white"
[(ngModel)]="password"
name="password"
required
/>
</div>
<button
type="submit"
class="text-xl cursor-pointer rounded-md bg-[#ff0048] px-4 py-2 text-white hover:bg-[#E03164] focus:outline-none focus:ring-2 focus:ring-[#FD366E] focus:ring-offset-2">
Anmelden
</button>
<button (click)="logout()" type="button" class="text-xl cursor-pointer rounded-md bg-[#168B07FF] px-4 py-2 text-white hover:bg-[#034E13FF] focus:outline-none focus:ring-2 focus:ring-[#168B07FF] focus:ring-offset-2">
Logout
</button>
</form>
<p class="pt-2 text-gray-800">Or you can here <span class="text-blue-600"><button (click)="register()" type="button" class="cursor-pointer">Sign in</button></span> to the Applikation</p>
</div>
</main>

View File

@ -0,0 +1,65 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppwriteService } from '../../services/appwrite.service';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './login.component.html',
styleUrl: './login.component.css'
})
export class LoginComponent {
detailHeight: number = 0;
email = '';
password = '';
sessionId = '';
constructor(
private router: Router,
private appwriteService: AppwriteService
) {}
register() {
this.router.navigate(['/register']);
}
async logout() {
try {
await this.appwriteService.accountService.deleteSession(this.sessionId).then((response) => {
this.sessionId = '';
this.email = '';
this.password = '';
alert('Erfolgreich abgemeldet');
}).catch((error) => {
console.error('Fehler beim Abmelden: ' + error.message);
});
} catch (err: any) {
console.error('Logout fehlgeschlagen: ' + err.message);
}
}
async login() {
try {
await this.appwriteService.accountService.createEmailPasswordSession(
this.email,
this.password,
).then((response) => {
this.sessionId = response.$id;
this.email = '';
this.password = '';
//this.router.navigate(['/input']);
}).catch((error) => {
console.error('Fehler beim Anmelden: ' + error.message);
alert('Fehler beim Anmelden: ' + error.message);
});
} catch (err: any) {
console.error('Anmeldung fehlgeschlagen: ' + err.message);
}
}
}

View File

@ -0,0 +1,19 @@
summary::-webkit-details-marker {
display: none
}
.checker-background::before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-image: linear-gradient(#e6e6e690 1px, transparent 1px),
linear-gradient(90deg, #e6e6e690 1px, transparent 1px);
background-size: 3.7em 3.7em;
-webkit-mask-image: radial-gradient(ellipse at 50% 40%, black 0%, transparent 55%);
mask-image: radial-gradient(ellipse at 50% 40%, black 0%, transparent 55%);
z-index: -1;
background-position-x: center;
}

View File

@ -0,0 +1,58 @@
<main class="login-container">
<div class="mt-8 flex flex-col items-center justify-center rounded-lg bg-red-500/30 p-6 shadow-md">
<h2 class="mb-4 text-4xl font-bold text-[#050505FF]">Registrieren</h2>
<form class="flex flex-col gap-4" (ngSubmit)="registerUser()">
<div>
<label for="username" class="sr-only">Benutzer</label>
<input
id="username"
type="text"
[(ngModel)]="userName"
placeholder="Benutzer Name"
name="username"
required
class="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-[#000000] focus:outline-none focus:ring-1 focus:ring-[#030303]
text-black
text-xl
bg-white"
/>
</div>
<div>
<label for="email" class="sr-only">Email</label>
<input
id="email"
type="email"
[(ngModel)]="email"
placeholder="E-Mail-Adresse"
name="email"
required
class="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-[#000000] focus:outline-none focus:ring-1 focus:ring-[#030303]
text-black
text-xl
bg-white"
/>
</div>
<div>
<label for="password" class="sr-only">Passwort</label>
<input
id="password"
type="password"
placeholder="Passwort"
class="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-[#000000] focus:outline-none focus:ring-1 focus:ring-[#000000] text-black
text-xl
bg-white"
[(ngModel)]="password"
name="password"
required
/>
</div>
<button
type="submit"
class="text-xl cursor-pointer rounded-md bg-[#ff0048] px-4 py-2 text-white hover:bg-[#E03164] focus:outline-none focus:ring-2 focus:ring-[#FD366E] focus:ring-offset-2">
Register
</button>
</form>
<p class="pt-2 text-gray-800">Or you can here <span class="text-blue-600"><button (click)="goTologin()" type="button" class="cursor-pointer">Log in</button></span> to the Applikation</p>
</div>
</main>

View File

@ -0,0 +1,37 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppwriteService } from '../../services/appwrite.service';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
@Component({
selector: 'app-register',
imports: [CommonModule, FormsModule],
templateUrl: './register.component.html',
styleUrl: './register.component.css'
})
export class RegisterComponent {
email = 'ich@example.com';
userName = 'TestUser';
password = '123456789';
constructor(
private router: Router,
private appwriteService: AppwriteService
) { }
async registerUser(): Promise<void>{
try {
const responseUserSignIn = await this.appwriteService.register(this.email, this.password, this.userName);
this.router.navigate(['/login']);
} catch (error) {
console.log('Registrierung fehlgeschlagen :' + error);
}
}
goTologin() {
this.router.navigate(['/login']);
}
}

View File

@ -0,0 +1,182 @@
import { Injectable } from '@angular/core';
import { Client, Account, Databases, ID, Query, Models } from 'appwrite';
import { environment } from '../../environments/environment';
import { Observable, from } from 'rxjs';
import { tap, map } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class AppwriteService {
private client: Client;
private account: Account;
private databases: Databases;
private readonly localStorageKey = 'appwriteRecords';
constructor() {
this.client = new Client()
.setEndpoint(environment.appwriteEndpoint) // Your Appwrite API endpoint
.setProject(environment.appwriteProjectId); // Your project ID
this.account = new Account(this.client);
this.databases = new Databases(this.client);
}
// --- Account Service ---
get accountService(): Account {
return this.account;
}
async register(email: string, password: string, name: string): Promise<any> {
return await this.account.create(ID.unique(), email, password, name);
}
// --- CRUD Operations for Databases ---
/**
* Creates a new document in a specified collection.
* @param databaseId The ID of the database.
* @param collectionId The ID of the collection.
* @param data The data object for the new document.
* @returns A promise with the created document.
*/
async createDocument(data: any): Promise<any> {
try {
const result = await this.databases.createDocument(
environment.appwriteDatabaseId,
environment.appwriteCollectionId,
ID.unique(),
data,
);
return result;
} catch (error) {
console.error('Error creating document:', error);
throw error;
}
}
/**
* Retrieves a single document from a collection.
* @param databaseId The ID of the database.
* @param collectionId The ID of the collection.
* @param documentId The ID of the document to retrieve.
* @returns A promise with the retrieved document.
*/
async getDocument(documentId: string): Promise<any> {
try {
const result = await this.databases.getDocument(
environment.appwriteDatabaseId,
environment.appwriteCollectionId,
documentId,
);
return result;
} catch (error) {
console.error('Error getting document:', error);
throw error;
}
}
/**
* Lists documents from a specified collection with optional queries.
* @param databaseId The ID of the database.
* @param collectionId The ID of the collection.
* @param queries An array of Query objects to filter the results (e.g., [Query.equal('name', 'John')]).
* @returns A promise with the list of documents.
*/
public getRecords(queries: string[] = []): Observable<any> {
//If no Data in localStorage
return from(
this.databases.listDocuments(
environment.appwriteDatabaseId,
environment.appwriteCollectionId,
queries,
),
).pipe(
map((response: Models.DocumentList<any>) => response.documents),
tap((documents: any) => {
this.saveToLocalStorage(documents);
}),
);
}
// Rest deines Codes bleibt unverändert
private saveToLocalStorage(data: any): void {
try {
localStorage.setItem(this.localStorageKey, JSON.stringify(data));
} catch (e) {
console.error('Fehler beim Speichern im LocalStorage', e);
}
}
// Lädt die Daten aus dem LocalStorage und parst sie als JSON
private loadFromLocalStorage(): any | null {
try {
const storedData = localStorage.getItem(this.localStorageKey);
return storedData ? JSON.parse(storedData) : null;
} catch (e) {
console.error('Fehler beim Laden aus LocalStorage', e);
return null;
}
}
// async listDocuments(
// queries: string[] = []
// ): Promise<any> {
// try {
// const result = await this.databases.listDocuments(
// environment.appwriteDatabaseId,
// environment.appwriteCollectionId,
// queries
// );
// return result;
// } catch (error) {
// console.error('Error listing documents:', error);
// throw error;
// }
// }
/**
* Updates an existing document in a collection.
* @param databaseId The ID of the database.
* @param collectionId The ID of the collection.
* @param documentId The ID of the document to update.
* @param data The new data object to merge with the existing document.
* @returns A promise with the updated document.
*/
async updateDocument(documentId: string, data: any): Promise<any> {
try {
const result = await this.databases.updateDocument(
environment.appwriteDatabaseId,
environment.appwriteCollectionId,
documentId,
data,
);
return result;
} catch (error) {
console.error('Error updating document:', error);
throw error;
}
}
/**
* Deletes a document from a collection.
* @param databaseId The ID of the database.
* @param collectionId The ID of the collection.
* @param documentId The ID of the document to delete.
* @returns A promise that resolves upon successful deletion.
*/
async deleteDocument(documentId: string): Promise<any> {
try {
await this.databases.deleteDocument(
environment.appwriteDatabaseId,
environment.appwriteCollectionId,
documentId,
);
return { success: true, message: 'Document deleted successfully.' };
} catch (error) {
console.error('Error deleting document:', error);
throw error;
}
}
}

View File

@ -0,0 +1,13 @@
export const environment: {
appwriteEndpoint: string;
appwriteProjectId: string;
appwriteProjectName: string;
appwriteDatabaseId: string;
appwriteCollectionId: string;
} = {
appwriteEndpoint: '<Your endpoint URL>', // Your Appwrite Endpoint
appwriteProjectId: '<123456789abcdefgh>', // Your project ID
appwriteProjectName: '<Your Project name>', // Your project name
appwriteDatabaseId: '<123456789abcdefgh>', // Your database ID
appwriteCollectionId: '<123456789abcdefgh>', // Your collection ID
};

View File

@ -0,0 +1,13 @@
export const environment: {
appwriteEndpoint: string;
appwriteProjectId: string;
appwriteProjectName: string;
appwriteDatabaseId: string;
appwriteCollectionId: string;
} = {
appwriteEndpoint: '<Your endpoint URL>', // Your Appwrite Endpoint
appwriteProjectId: '<123456789abcdefgh>', // Your project ID
appwriteProjectName: '<Your Project name>', // Your project name
appwriteDatabaseId: '<123456789abcdefgh>', // Your database ID
appwriteCollectionId: '<123456789abcdefgh>', // Your collection ID
};

20
src/index.html Normal file
View File

@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Appwrite + Angular</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/appwrite.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Inter:opsz,wght@14..32,100..900&family=Poppins:wght@300;400&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
<body class="bg-[#FAFAFB] font-[Inter] text-sm text-[#56565C]">
<app-root></app-root>
</body>
</html>

7
src/main.ts Normal file
View File

@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);

112
src/styles.css Normal file
View File

@ -0,0 +1,112 @@
@import "tailwindcss";
@import "@fortawesome/fontawesome-free/css/all.min.css";
.login-container {
/* Positioniere den Container, um den gesamten Viewport abzudecken */
position: fixed;
width: 100%;
height: 100%;
/* Füge das Hintergrundbild hinzu */
background-image: url('/assets/img/KegelGaudi2025.png');
/* Stelle sicher, dass das Bild den gesamten Container ausfüllt */
background-size: cover;
/* Zentriere das Bild horizontal und vertikal */
background-position: center;
/* Verhindere, dass sich das Bild wiederholt */
background-repeat: no-repeat;
/* Setze ein Overlay (optional), um den Text lesbarer zu machen */
/* background-color: rgba(0, 0, 0, 0.5); Semi-transparentes Schwarz */
background-blend-mode: overlay;
/* Zentriere die Login-Karte */
display: flex;
justify-content: center;
align-items: center;
}
.input-container{
/* Positioniere den Container, um den gesamten Viewport abzudecken */
position: fixed;
width: 100%;
height: 100%;
/* Füge das Hintergrundbild hinzu */
background-image: url('/assets/img/InputKegel.png');
/* Stelle sicher, dass das Bild den gesamten Container ausfüllt */
background-size: cover;
/* Zentriere das Bild horizontal und vertikal */
background-position: center;
/* Verhindere, dass sich das Bild wiederholt */
background-repeat: no-repeat;
/* Setze ein Overlay (optional), um den Text lesbarer zu machen */
/* background-color: rgba(0, 0, 0, 0.5); Semi-transparentes Schwarz */
background-blend-mode: overlay;
/* Zentriere die Login-Karte */
display: flex;
justify-content: center;
align-items: center;
}
.kegel-container {
max-width: 800px;
display: flex;
flex-direction: column;
height: 75vh;
margin: 2rem auto;
padding: 1rem;
font-family: "Libre Baskerville", serif;
}
/* Definiert den Bereich, der scrollen soll */
.scrollable-content {
flex: 2; /* Nimmt den verfügbaren Platz ein */
overflow-y: auto; /* Erlaubt vertikales Scrollen in diesem Block */
}
.save-button {
margin-top: 1rem;
padding: 1rem;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1.1rem;
min-width: 250px;
}
.save-button:hover {
background-color: #0056b3;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group label {
margin-bottom: 0.5rem;
font-weight: bold;
font-size: 1.3em;
color: rgb(3, 3, 3);
}
.form-group input {
padding: 0.75rem;
border: 1px solid #020202;
border-radius: 4px;
font-size: 1rem;
}
.error-message {
color: red;
}

11
tsconfig.app.json Normal file
View File

@ -0,0 +1,11 @@
/* 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"]
}

27
tsconfig.json Normal file
View File

@ -0,0 +1,27 @@
/* 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"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

10
tsconfig.spec.json Normal file
View File

@ -0,0 +1,10 @@
/* 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"]
}