From b3a128bedfa4f919e1cb198357da0ac56dd4419b Mon Sep 17 00:00:00 2001 From: Raahim08 <71621239+Raahim08@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:05:47 +0000 Subject: [PATCH 1/5] repo setup --- client-management/.editorconfig | 17 ++ client-management/.gitignore | 42 +++ client-management/.vscode/extensions.json | 4 + client-management/.vscode/launch.json | 20 ++ client-management/.vscode/tasks.json | 42 +++ client-management/README.md | 59 ++++ client-management/angular.json | 98 +++++++ client-management/package.json | 47 +++ client-management/public/favicon.ico | Bin 0 -> 15086 bytes client-management/src/app/app.config.ts | 12 + client-management/src/app/app.html | 342 ++++++++++++++++++++++ client-management/src/app/app.routes.ts | 11 + client-management/src/app/app.scss | 0 client-management/src/app/app.spec.ts | 23 ++ client-management/src/app/app.ts | 12 + client-management/src/index.html | 13 + client-management/src/main.ts | 6 + client-management/src/styles.scss | 1 + client-management/tsconfig.app.json | 15 + client-management/tsconfig.json | 34 +++ client-management/tsconfig.spec.json | 14 + package.json | 4 +- 22 files changed, 814 insertions(+), 2 deletions(-) create mode 100644 client-management/.editorconfig create mode 100644 client-management/.gitignore create mode 100644 client-management/.vscode/extensions.json create mode 100644 client-management/.vscode/launch.json create mode 100644 client-management/.vscode/tasks.json create mode 100644 client-management/README.md create mode 100644 client-management/angular.json create mode 100644 client-management/package.json create mode 100644 client-management/public/favicon.ico create mode 100644 client-management/src/app/app.config.ts create mode 100644 client-management/src/app/app.html create mode 100644 client-management/src/app/app.routes.ts create mode 100644 client-management/src/app/app.scss create mode 100644 client-management/src/app/app.spec.ts create mode 100644 client-management/src/app/app.ts create mode 100644 client-management/src/index.html create mode 100644 client-management/src/main.ts create mode 100644 client-management/src/styles.scss create mode 100644 client-management/tsconfig.app.json create mode 100644 client-management/tsconfig.json create mode 100644 client-management/tsconfig.spec.json diff --git a/client-management/.editorconfig b/client-management/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/client-management/.editorconfig @@ -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 diff --git a/client-management/.gitignore b/client-management/.gitignore new file mode 100644 index 0000000..cc7b141 --- /dev/null +++ b/client-management/.gitignore @@ -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/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/client-management/.vscode/extensions.json b/client-management/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/client-management/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/client-management/.vscode/launch.json b/client-management/.vscode/launch.json new file mode 100644 index 0000000..925af83 --- /dev/null +++ b/client-management/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "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" + } + ] +} diff --git a/client-management/.vscode/tasks.json b/client-management/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/client-management/.vscode/tasks.json @@ -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" + } + } + } + } + ] +} diff --git a/client-management/README.md b/client-management/README.md new file mode 100644 index 0000000..4f0f4af --- /dev/null +++ b/client-management/README.md @@ -0,0 +1,59 @@ +# ClientManagement + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.1.3. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/client-management/angular.json b/client-management/angular.json new file mode 100644 index 0000000..510ca37 --- /dev/null +++ b/client-management/angular.json @@ -0,0 +1,98 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "client-management": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.scss" + ] + }, + "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/build:dev-server", + "configurations": { + "production": { + "buildTarget": "client-management:build:production" + }, + "development": { + "buildTarget": "client-management: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", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.scss" + ] + } + } + } + } + } +} diff --git a/client-management/package.json b/client-management/package.json new file mode 100644 index 0000000..0d4e630 --- /dev/null +++ b/client-management/package.json @@ -0,0 +1,47 @@ +{ + "name": "client-management", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "prettier": { + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] + }, + "private": true, + "dependencies": { + "@angular/common": "^20.1.0", + "@angular/compiler": "^20.1.0", + "@angular/core": "^20.1.0", + "@angular/forms": "^20.1.0", + "@angular/platform-browser": "^20.1.0", + "@angular/router": "^20.1.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@angular/build": "^20.1.3", + "@angular/cli": "^20.1.3", + "@angular/compiler-cli": "^20.1.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.8.0", + "json-server": "^1.0.0-beta.3", + "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", + "typescript": "~5.8.2" + } +} diff --git a/client-management/public/favicon.ico b/client-management/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..57614f9c967596fad0a3989bec2b1deff33034f6 GIT binary patch literal 15086 zcmd^G33O9Omi+`8$@{|M-I6TH3wzF-p5CV8o}7f~KxR60LK+ApEFB<$bcciv%@SmA zV{n>g85YMFFeU*Uvl=i4v)C*qgnb;$GQ=3XTe9{Y%c`mO%su)noNCCQ*@t1WXn|B(hQ7i~ zrUK8|pUkD6#lNo!bt$6)jR!&C?`P5G(`e((P($RaLeq+o0Vd~f11;qB05kdbAOm?r zXv~GYr_sibQO9NGTCdT;+G(!{4Xs@4fPak8#L8PjgJwcs-Mm#nR_Z0s&u?nDX5^~@ z+A6?}g0|=4e_LoE69pPFO`yCD@BCjgKpzMH0O4Xs{Ahc?K3HC5;l=f zg>}alhBXX&);z$E-wai+9TTRtBX-bWYY@cl$@YN#gMd~tM_5lj6W%8ah4;uZ;jP@Q zVbuel1rPA?2@x9Y+u?e`l{Z4ngfG5q5BLH5QsEu4GVpt{KIp1?U)=3+KQ;%7ec8l* zdV=zZgN5>O3G(3L2fqj3;oBbZZw$Ij@`Juz@?+yy#OPw)>#wsTewVgTK9BGt5AbZ&?K&B3GVF&yu?@(Xj3fR3n+ZP0%+wo)D9_xp>Z$`A4 zfV>}NWjO#3lqumR0`gvnffd9Ka}JJMuHS&|55-*mCD#8e^anA<+sFZVaJe7{=p*oX zE_Uv?1>e~ga=seYzh{9P+n5<+7&9}&(kwqSaz;1aD|YM3HBiy<))4~QJSIryyqp| z8nGc(8>3(_nEI4n)n7j(&d4idW1tVLjZ7QbNLXg;LB ziHsS5pXHEjGJZb59KcvS~wv;uZR-+4qEqow`;JCfB*+b^UL^3!?;-^F%yt=VjU|v z39SSqKcRu_NVvz!zJzL0CceJaS6%!(eMshPv_0U5G`~!a#I$qI5Ic(>IONej@aH=f z)($TAT#1I{iCS4f{D2+ApS=$3E7}5=+y(rA9mM#;Cky%b*Gi0KfFA`ofKTzu`AV-9 znW|y@19rrZ*!N2AvDi<_ZeR3O2R{#dh1#3-d%$k${Rx42h+i&GZo5!C^dSL34*AKp z27mTd>k>?V&X;Nl%GZ(>0s`1UN~Hfyj>KPjtnc|)xM@{H_B9rNr~LuH`Gr5_am&Ep zTjZA8hljNj5H1Ipm-uD9rC}U{-vR!eay5&6x6FkfupdpT*84MVwGpdd(}ib)zZ3Ky z7C$pnjc82(W_y_F{PhYj?o!@3__UUvpX)v69aBSzYj3 zdi}YQkKs^SyXyFG2LTRz9{(w}y~!`{EuAaUr6G1M{*%c+kP1olW9z23dSH!G4_HSK zzae-DF$OGR{ofP*!$a(r^5Go>I3SObVI6FLY)N@o<*gl0&kLo-OT{Tl*7nCz>Iq=? zcigIDHtj|H;6sR?or8Wd_a4996GI*CXGU}o;D9`^FM!AT1pBY~?|4h^61BY#_yIfO zKO?E0 zJ{Pc`9rVEI&$xxXu`<5E)&+m(7zX^v0rqofLs&bnQT(1baQkAr^kEsk)15vlzAZ-l z@OO9RF<+IiJ*O@HE256gCt!bF=NM*vh|WVWmjVawcNoksRTMvR03H{p@cjwKh(CL4 z7_PB(dM=kO)!s4fW!1p0f93YN@?ZSG` z$B!JaAJCtW$B97}HNO9(x-t30&E}Mo1UPi@Av%uHj~?T|!4JLwV;KCx8xO#b9IlUW zI6+{a@Wj|<2Y=U;a@vXbxqZNngH8^}LleE_4*0&O7#3iGxfJ%Id>+sb;7{L=aIic8 z|EW|{{S)J-wr@;3PmlxRXU8!e2gm_%s|ReH!reFcY8%$Hl4M5>;6^UDUUae?kOy#h zk~6Ee_@ZAn48Bab__^bNmQ~+k=02jz)e0d9Z3>G?RGG!65?d1>9}7iG17?P*=GUV-#SbLRw)Hu{zx*azHxWkGNTWl@HeWjA?39Ia|sCi{e;!^`1Oec zb>Z|b65OM*;eC=ZLSy?_fg$&^2xI>qSLA2G*$nA3GEnp3$N-)46`|36m*sc#4%C|h zBN<2U;7k>&G_wL4=Ve5z`ubVD&*Hxi)r@{4RCDw7U_D`lbC(9&pG5C*z#W>8>HU)h z!h3g?2UL&sS!oY5$3?VlA0Me9W5e~V;2jds*fz^updz#AJ%G8w2V}AEE?E^=MK%Xt z__Bx1cr7+DQmuHmzn*|hh%~eEc9@m05@clWfpEFcr+06%0&dZJH&@8^&@*$qR@}o3 z@Tuuh2FsLz^zH+dN&T&?0G3I?MpmYJ;GP$J!EzjeM#YLJ!W$}MVNb0^HfOA>5Fe~UNn%Zk(PT@~9}1dt)1UQ zU*B5K?Dl#G74qmg|2>^>0WtLX#Jz{lO4NT`NYB*(L#D|5IpXr9v&7a@YsGp3vLR7L zHYGHZg7{ie6n~2p$6Yz>=^cEg7tEgk-1YRl%-s7^cbqFb(U7&Dp78+&ut5!Tn(hER z|Gp4Ed@CnOPeAe|N>U(dB;SZ?NU^AzoD^UAH_vamp6Ws}{|mSq`^+VP1g~2B{%N-!mWz<`)G)>V-<`9`L4?3dM%Qh6<@kba+m`JS{Ya@9Fq*m6$$ zA1%Ogc~VRH33|S9l%CNb4zM%k^EIpqY}@h{w(aBcJ9c05oiZx#SK9t->5lSI`=&l~ z+-Ic)a{FbBhXV$Xt!WRd`R#Jk-$+_Z52rS>?Vpt2IK<84|E-SBEoIw>cs=a{BlQ7O z-?{Fy_M&84&9|KM5wt~)*!~i~E=(6m8(uCO)I=)M?)&sRbzH$9Rovzd?ZEY}GqX+~ zFbEbLz`BZ49=2Yh-|<`waK-_4!7`ro@zlC|r&I4fc4oyb+m=|c8)8%tZ-z5FwhzDt zL5kB@u53`d@%nHl0Sp)Dw`(QU&>vujEn?GPEXUW!Wi<+4e%BORl&BIH+SwRcbS}X@ z01Pk|vA%OdJKAs17zSXtO55k!;%m9>1eW9LnyAX4uj7@${O6cfii`49qTNItzny5J zH&Gj`e}o}?xjQ}r?LrI%FjUd@xflT3|7LA|ka%Q3i}a8gVm<`HIWoJGH=$EGClX^C0lysQJ>UO(q&;`T#8txuoQ_{l^kEV9CAdXuU1Ghg8 zN_6hHFuy&1x24q5-(Z7;!poYdt*`UTdrQOIQ!2O7_+AHV2hgXaEz7)>$LEdG z<8vE^Tw$|YwZHZDPM!SNOAWG$?J)MdmEk{U!!$M#fp7*Wo}jJ$Q(=8>R`Ats?e|VU?Zt7Cdh%AdnfyN3MBWw{ z$OnREvPf7%z6`#2##_7id|H%Y{vV^vWXb?5d5?a_y&t3@p9t$ncHj-NBdo&X{wrfJ zamN)VMYROYh_SvjJ=Xd!Ga?PY_$;*L=SxFte!4O6%0HEh%iZ4=gvns7IWIyJHa|hT z2;1+e)`TvbNb3-0z&DD_)Jomsg-7p_Uh`wjGnU1urmv1_oVqRg#=C?e?!7DgtqojU zWoAB($&53;TsXu^@2;8M`#z{=rPy?JqgYM0CDf4v@z=ZD|ItJ&8%_7A#K?S{wjxgd z?xA6JdJojrWpB7fr2p_MSsU4(R7=XGS0+Eg#xR=j>`H@R9{XjwBmqAiOxOL` zt?XK-iTEOWV}f>Pz3H-s*>W z4~8C&Xq25UQ^xH6H9kY_RM1$ch+%YLF72AA7^b{~VNTG}Tj#qZltz5Q=qxR`&oIlW Nr__JTFzvMr^FKp4S3v*( literal 0 HcmV?d00001 diff --git a/client-management/src/app/app.config.ts b/client-management/src/app/app.config.ts new file mode 100644 index 0000000..d953f4c --- /dev/null +++ b/client-management/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes) + ] +}; diff --git a/client-management/src/app/app.html b/client-management/src/app/app.html new file mode 100644 index 0000000..7528372 --- /dev/null +++ b/client-management/src/app/app.html @@ -0,0 +1,342 @@ + + + + + + + + + + + +
+
+
+ +

Hello, {{ title() }}

+

Congratulations! Your app is running. 🎉

+
+ +
+
+ @for (item of [ + { title: 'Explore the Docs', link: 'https://angular.dev' }, + { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, + { title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'}, + { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, + { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, + { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, + ]; track item.title) { + + {{ item.title }} + + + + + } +
+ +
+
+
+ + + + + + + + + + + diff --git a/client-management/src/app/app.routes.ts b/client-management/src/app/app.routes.ts new file mode 100644 index 0000000..3e73375 --- /dev/null +++ b/client-management/src/app/app.routes.ts @@ -0,0 +1,11 @@ +import { Routes } from '@angular/router'; +import { Login } from './components/login/login'; +import { ClientList } from './components/client-list/client-list'; +import { ClientDetail } from './components/client-detail/client-detail'; + +export const routes: Routes = [ + { path: '', redirectTo: 'login', pathMatch: 'full' }, + { path: 'login', component: Login }, + { path: 'clients', component: ClientList }, + { path: 'clients/:id', component: ClientDetail } +]; \ No newline at end of file diff --git a/client-management/src/app/app.scss b/client-management/src/app/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/client-management/src/app/app.spec.ts b/client-management/src/app/app.spec.ts new file mode 100644 index 0000000..cc25f22 --- /dev/null +++ b/client-management/src/app/app.spec.ts @@ -0,0 +1,23 @@ +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, client-management'); + }); +}); diff --git a/client-management/src/app/app.ts b/client-management/src/app/app.ts new file mode 100644 index 0000000..154f7c4 --- /dev/null +++ b/client-management/src/app/app.ts @@ -0,0 +1,12 @@ +import { Component, signal } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + imports: [RouterOutlet], + templateUrl: './app.html', + styleUrl: './app.scss' +}) +export class App { + protected readonly title = signal('client-management'); +} diff --git a/client-management/src/index.html b/client-management/src/index.html new file mode 100644 index 0000000..2b6ebdc --- /dev/null +++ b/client-management/src/index.html @@ -0,0 +1,13 @@ + + + + + ClientManagement + + + + + + + + diff --git a/client-management/src/main.ts b/client-management/src/main.ts new file mode 100644 index 0000000..5df75f9 --- /dev/null +++ b/client-management/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/client-management/src/styles.scss b/client-management/src/styles.scss new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/client-management/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/client-management/tsconfig.app.json b/client-management/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/client-management/tsconfig.app.json @@ -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": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/client-management/tsconfig.json b/client-management/tsconfig.json new file mode 100644 index 0000000..e4955f2 --- /dev/null +++ b/client-management/tsconfig.json @@ -0,0 +1,34 @@ +/* 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": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/client-management/tsconfig.spec.json b/client-management/tsconfig.spec.json new file mode 100644 index 0000000..04df34c --- /dev/null +++ b/client-management/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* 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/**/*.ts" + ] +} diff --git a/package.json b/package.json index bf9fa05..a878eec 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "author": "virtusize", "license": "UNLICENSED", - "dependencies": { - "json-server": "^1.0.0-beta.2" + "devDependencies": { + "json-server": "^1.0.0-beta.3" } } From 797290f7271aee6ceb4f514faa0e554b5a920783 Mon Sep 17 00:00:00 2001 From: Raahim08 <71621239+Raahim08@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:06:21 +0000 Subject: [PATCH 2/5] initial-project-setup --- .../client-detail/client-detail.html | 1 + .../client-detail/client-detail.scss | 0 .../client-detail/client-detail.spec.ts | 23 +++++++++++++++++++ .../components/client-detail/client-detail.ts | 11 +++++++++ .../components/client-form/client-form.html | 1 + .../components/client-form/client-form.scss | 0 .../client-form/client-form.spec.ts | 23 +++++++++++++++++++ .../app/components/client-form/client-form.ts | 11 +++++++++ .../components/client-list/client-list.html | 1 + .../components/client-list/client-list.scss | 0 .../client-list/client-list.spec.ts | 23 +++++++++++++++++++ .../app/components/client-list/client-list.ts | 11 +++++++++ .../src/app/components/login/login.html | 1 + .../src/app/components/login/login.scss | 0 .../src/app/components/login/login.spec.ts | 23 +++++++++++++++++++ .../src/app/components/login/login.ts | 11 +++++++++ .../src/app/services/client.spec.ts | 16 +++++++++++++ client-management/src/app/services/client.ts | 8 +++++++ 18 files changed, 164 insertions(+) create mode 100644 client-management/src/app/components/client-detail/client-detail.html create mode 100644 client-management/src/app/components/client-detail/client-detail.scss create mode 100644 client-management/src/app/components/client-detail/client-detail.spec.ts create mode 100644 client-management/src/app/components/client-detail/client-detail.ts create mode 100644 client-management/src/app/components/client-form/client-form.html create mode 100644 client-management/src/app/components/client-form/client-form.scss create mode 100644 client-management/src/app/components/client-form/client-form.spec.ts create mode 100644 client-management/src/app/components/client-form/client-form.ts create mode 100644 client-management/src/app/components/client-list/client-list.html create mode 100644 client-management/src/app/components/client-list/client-list.scss create mode 100644 client-management/src/app/components/client-list/client-list.spec.ts create mode 100644 client-management/src/app/components/client-list/client-list.ts create mode 100644 client-management/src/app/components/login/login.html create mode 100644 client-management/src/app/components/login/login.scss create mode 100644 client-management/src/app/components/login/login.spec.ts create mode 100644 client-management/src/app/components/login/login.ts create mode 100644 client-management/src/app/services/client.spec.ts create mode 100644 client-management/src/app/services/client.ts diff --git a/client-management/src/app/components/client-detail/client-detail.html b/client-management/src/app/components/client-detail/client-detail.html new file mode 100644 index 0000000..52bb8c3 --- /dev/null +++ b/client-management/src/app/components/client-detail/client-detail.html @@ -0,0 +1 @@ +

client-detail works!

diff --git a/client-management/src/app/components/client-detail/client-detail.scss b/client-management/src/app/components/client-detail/client-detail.scss new file mode 100644 index 0000000..e69de29 diff --git a/client-management/src/app/components/client-detail/client-detail.spec.ts b/client-management/src/app/components/client-detail/client-detail.spec.ts new file mode 100644 index 0000000..05c5955 --- /dev/null +++ b/client-management/src/app/components/client-detail/client-detail.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientDetail } from './client-detail'; + +describe('ClientDetail', () => { + let component: ClientDetail; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ClientDetail] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ClientDetail); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/components/client-detail/client-detail.ts b/client-management/src/app/components/client-detail/client-detail.ts new file mode 100644 index 0000000..94d66bb --- /dev/null +++ b/client-management/src/app/components/client-detail/client-detail.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-client-detail', + imports: [], + templateUrl: './client-detail.html', + styleUrl: './client-detail.scss' +}) +export class ClientDetail { + +} diff --git a/client-management/src/app/components/client-form/client-form.html b/client-management/src/app/components/client-form/client-form.html new file mode 100644 index 0000000..c07c1f2 --- /dev/null +++ b/client-management/src/app/components/client-form/client-form.html @@ -0,0 +1 @@ +

client-form works!

diff --git a/client-management/src/app/components/client-form/client-form.scss b/client-management/src/app/components/client-form/client-form.scss new file mode 100644 index 0000000..e69de29 diff --git a/client-management/src/app/components/client-form/client-form.spec.ts b/client-management/src/app/components/client-form/client-form.spec.ts new file mode 100644 index 0000000..feee444 --- /dev/null +++ b/client-management/src/app/components/client-form/client-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientForm } from './client-form'; + +describe('ClientForm', () => { + let component: ClientForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ClientForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ClientForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/components/client-form/client-form.ts b/client-management/src/app/components/client-form/client-form.ts new file mode 100644 index 0000000..c159c7d --- /dev/null +++ b/client-management/src/app/components/client-form/client-form.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-client-form', + imports: [], + templateUrl: './client-form.html', + styleUrl: './client-form.scss' +}) +export class ClientForm { + +} diff --git a/client-management/src/app/components/client-list/client-list.html b/client-management/src/app/components/client-list/client-list.html new file mode 100644 index 0000000..a1e43bc --- /dev/null +++ b/client-management/src/app/components/client-list/client-list.html @@ -0,0 +1 @@ +

client-list works!

diff --git a/client-management/src/app/components/client-list/client-list.scss b/client-management/src/app/components/client-list/client-list.scss new file mode 100644 index 0000000..e69de29 diff --git a/client-management/src/app/components/client-list/client-list.spec.ts b/client-management/src/app/components/client-list/client-list.spec.ts new file mode 100644 index 0000000..7264841 --- /dev/null +++ b/client-management/src/app/components/client-list/client-list.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientList } from './client-list'; + +describe('ClientList', () => { + let component: ClientList; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ClientList] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ClientList); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/components/client-list/client-list.ts b/client-management/src/app/components/client-list/client-list.ts new file mode 100644 index 0000000..6dd619c --- /dev/null +++ b/client-management/src/app/components/client-list/client-list.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-client-list', + imports: [], + templateUrl: './client-list.html', + styleUrl: './client-list.scss' +}) +export class ClientList { + +} diff --git a/client-management/src/app/components/login/login.html b/client-management/src/app/components/login/login.html new file mode 100644 index 0000000..147cfc4 --- /dev/null +++ b/client-management/src/app/components/login/login.html @@ -0,0 +1 @@ +

login works!

diff --git a/client-management/src/app/components/login/login.scss b/client-management/src/app/components/login/login.scss new file mode 100644 index 0000000..e69de29 diff --git a/client-management/src/app/components/login/login.spec.ts b/client-management/src/app/components/login/login.spec.ts new file mode 100644 index 0000000..dd8bbb3 --- /dev/null +++ b/client-management/src/app/components/login/login.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Login } from './login'; + +describe('Login', () => { + let component: Login; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Login] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Login); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/components/login/login.ts b/client-management/src/app/components/login/login.ts new file mode 100644 index 0000000..a9e4eee --- /dev/null +++ b/client-management/src/app/components/login/login.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-login', + imports: [], + templateUrl: './login.html', + styleUrl: './login.scss' +}) +export class Login { + +} diff --git a/client-management/src/app/services/client.spec.ts b/client-management/src/app/services/client.spec.ts new file mode 100644 index 0000000..935a715 --- /dev/null +++ b/client-management/src/app/services/client.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Client } from './client'; + +describe('Client', () => { + let service: Client; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Client); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/services/client.ts b/client-management/src/app/services/client.ts new file mode 100644 index 0000000..f4c8a10 --- /dev/null +++ b/client-management/src/app/services/client.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class Client { + +} From d511e4f76b7ce6266447087dfd2c9b3dd5db605a Mon Sep 17 00:00:00 2001 From: Raahim08 <71621239+Raahim08@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:25:59 +0000 Subject: [PATCH 3/5] initial-dev --- client-management/.editorconfig | 1 - client-management/.gitignore | 2 +- client-management/README.md | 50 +-- client-management/angular.json | 54 +-- client-management/package.json | 48 ++- client-management/public/favicon.ico | Bin 15086 -> 0 bytes .../src/app/app-routing.module.ts | 21 ++ client-management/src/app/app.component.html | 1 + .../src/app/{app.scss => app.component.scss} | 0 .../src/app/app.component.spec.ts | 35 ++ client-management/src/app/app.component.ts | 10 + client-management/src/app/app.config.ts | 12 - client-management/src/app/app.html | 342 ------------------ client-management/src/app/app.module.ts | 48 +++ client-management/src/app/app.routes.ts | 11 - client-management/src/app/app.spec.ts | 23 -- client-management/src/app/app.ts | 12 - .../client-detail.component.html} | 0 .../client-detail.component.scss} | 0 .../client-detail.component.spec.ts} | 12 +- .../client-detail/client-detail.component.ts | 10 + .../client-form.component.html} | 0 .../client-form.component.scss} | 0 .../client-form.component.spec.ts} | 12 +- .../app/client-form/client-form.component.ts | 10 + .../client-list/client-list.component.html | 18 + .../client-list/client-list.component.scss | 33 ++ .../client-list.component.spec.ts} | 12 +- .../app/client-list/client-list.component.ts | 95 +++++ .../components/client-detail/client-detail.ts | 11 - .../app/components/client-form/client-form.ts | 11 - .../components/client-list/client-list.html | 1 - .../app/components/client-list/client-list.ts | 11 - .../src/app/components/login/login.html | 1 - .../src/app/components/login/login.scss | 0 .../src/app/components/login/login.ts | 11 - .../src/app/login/login.component.html | 32 ++ .../src/app/login/login.component.scss | 42 +++ .../login.component.spec.ts} | 12 +- .../src/app/login/login.component.ts | 56 +++ .../{client.spec.ts => auth.service.spec.ts} | 8 +- .../src/app/services/auth.service.ts | 41 +++ .../services/client-service.service.spec.ts | 16 + .../app/services/client-service.service.ts | 14 + client-management/src/app/services/client.ts | 8 - .../client-list.scss => assets/.gitkeep} | 0 client-management/src/favicon.ico | Bin 0 -> 948 bytes client-management/src/index.html | 5 +- client-management/src/main.ts | 11 +- client-management/src/styles.scss | 41 +++ client-management/tsconfig.app.json | 11 +- client-management/tsconfig.json | 31 +- client-management/tsconfig.spec.json | 6 +- clients.json | 8 + 54 files changed, 655 insertions(+), 605 deletions(-) delete mode 100644 client-management/public/favicon.ico create mode 100644 client-management/src/app/app-routing.module.ts create mode 100644 client-management/src/app/app.component.html rename client-management/src/app/{app.scss => app.component.scss} (100%) create mode 100644 client-management/src/app/app.component.spec.ts create mode 100644 client-management/src/app/app.component.ts delete mode 100644 client-management/src/app/app.config.ts delete mode 100644 client-management/src/app/app.html create mode 100644 client-management/src/app/app.module.ts delete mode 100644 client-management/src/app/app.routes.ts delete mode 100644 client-management/src/app/app.spec.ts delete mode 100644 client-management/src/app/app.ts rename client-management/src/app/{components/client-detail/client-detail.html => client-detail/client-detail.component.html} (100%) rename client-management/src/app/{components/client-detail/client-detail.scss => client-detail/client-detail.component.scss} (100%) rename client-management/src/app/{components/client-list/client-list.spec.ts => client-detail/client-detail.component.spec.ts} (51%) create mode 100644 client-management/src/app/client-detail/client-detail.component.ts rename client-management/src/app/{components/client-form/client-form.html => client-form/client-form.component.html} (100%) rename client-management/src/app/{components/client-form/client-form.scss => client-form/client-form.component.scss} (100%) rename client-management/src/app/{components/client-form/client-form.spec.ts => client-form/client-form.component.spec.ts} (52%) create mode 100644 client-management/src/app/client-form/client-form.component.ts create mode 100644 client-management/src/app/client-list/client-list.component.html create mode 100644 client-management/src/app/client-list/client-list.component.scss rename client-management/src/app/{components/client-detail/client-detail.spec.ts => client-list/client-list.component.spec.ts} (52%) create mode 100644 client-management/src/app/client-list/client-list.component.ts delete mode 100644 client-management/src/app/components/client-detail/client-detail.ts delete mode 100644 client-management/src/app/components/client-form/client-form.ts delete mode 100644 client-management/src/app/components/client-list/client-list.html delete mode 100644 client-management/src/app/components/client-list/client-list.ts delete mode 100644 client-management/src/app/components/login/login.html delete mode 100644 client-management/src/app/components/login/login.scss delete mode 100644 client-management/src/app/components/login/login.ts create mode 100644 client-management/src/app/login/login.component.html create mode 100644 client-management/src/app/login/login.component.scss rename client-management/src/app/{components/login/login.spec.ts => login/login.component.spec.ts} (55%) create mode 100644 client-management/src/app/login/login.component.ts rename client-management/src/app/services/{client.spec.ts => auth.service.spec.ts} (57%) create mode 100644 client-management/src/app/services/auth.service.ts create mode 100644 client-management/src/app/services/client-service.service.spec.ts create mode 100644 client-management/src/app/services/client-service.service.ts delete mode 100644 client-management/src/app/services/client.ts rename client-management/src/{app/components/client-list/client-list.scss => assets/.gitkeep} (100%) create mode 100644 client-management/src/favicon.ico diff --git a/client-management/.editorconfig b/client-management/.editorconfig index f166060..59d9a3a 100644 --- a/client-management/.editorconfig +++ b/client-management/.editorconfig @@ -10,7 +10,6 @@ trim_trailing_whitespace = true [*.ts] quote_type = single -ij_typescript_use_double_quotes = false [*.md] max_line_length = off diff --git a/client-management/.gitignore b/client-management/.gitignore index cc7b141..0711527 100644 --- a/client-management/.gitignore +++ b/client-management/.gitignore @@ -1,4 +1,4 @@ -# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. +# See http://help.github.com/ignore-files/ for more about ignoring files. # Compiled output /dist diff --git a/client-management/README.md b/client-management/README.md index 4f0f4af..6eac262 100644 --- a/client-management/README.md +++ b/client-management/README.md @@ -1,59 +1,27 @@ # ClientManagement -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.1.3. +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.11. ## Development server -To start a local development server, run: - -```bash -ng serve -``` - -Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. ## Code scaffolding -Angular CLI includes powerful code scaffolding tools. To generate a new component, run: - -```bash -ng generate component component-name -``` - -For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: - -```bash -ng generate --help -``` +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. -## Building +## Build -To build the project run: - -```bash -ng build -``` - -This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. ## Running unit tests -To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: - -```bash -ng test -``` +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). ## Running end-to-end tests -For end-to-end (e2e) testing, run: - -```bash -ng e2e -``` - -Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. -## Additional Resources +## Further help -For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/client-management/angular.json b/client-management/angular.json index 510ca37..1d73eda 100644 --- a/client-management/angular.json +++ b/client-management/angular.json @@ -15,65 +15,74 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular/build:application", + "builder": "@angular-devkit/build-angular:browser", "options": { - "browser": "src/main.ts", + "outputPath": "dist/client-management", + "index": "src/index.html", + "main": "src/main.ts", "polyfills": [ "zone.js" ], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": [ - { - "glob": "**/*", - "input": "public" - } + "src/favicon.ico", + "src/assets" ], "styles": [ - "src/styles.scss" - ] + "src/styles.scss", + "node_modules/ag-grid-community/styles/ag-grid.css", + "node_modules/ag-grid-community/styles/ag-theme-alpine.css" + ], + "scripts": [] }, "configurations": { "production": { "budgets": [ { "type": "initial", - "maximumWarning": "500kB", - "maximumError": "1MB" + "maximumWarning": "500kb", + "maximumError": "1mb" }, { "type": "anyComponentStyle", - "maximumWarning": "4kB", - "maximumError": "8kB" + "maximumWarning": "2kb", + "maximumError": "4kb" } ], "outputHashing": "all" }, "development": { + "buildOptimizer": false, "optimization": false, + "vendorChunk": true, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "namedChunks": true } }, "defaultConfiguration": "production" }, "serve": { - "builder": "@angular/build:dev-server", + "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { - "buildTarget": "client-management:build:production" + "browserTarget": "client-management:build:production" }, "development": { - "buildTarget": "client-management:build:development" + "browserTarget": "client-management:build:development" } }, "defaultConfiguration": "development" }, "extract-i18n": { - "builder": "@angular/build:extract-i18n" + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "client-management:build" + } }, "test": { - "builder": "@angular/build:karma", + "builder": "@angular-devkit/build-angular:karma", "options": { "polyfills": [ "zone.js", @@ -82,14 +91,13 @@ "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": [ - { - "glob": "**/*", - "input": "public" - } + "src/favicon.ico", + "src/assets" ], "styles": [ "src/styles.scss" - ] + ], + "scripts": [] } } } diff --git a/client-management/package.json b/client-management/package.json index 0d4e630..0d323ec 100644 --- a/client-management/package.json +++ b/client-management/package.json @@ -8,40 +8,36 @@ "watch": "ng build --watch --configuration development", "test": "ng test" }, - "prettier": { - "overrides": [ - { - "files": "*.html", - "options": { - "parser": "angular" - } - } - ] - }, "private": true, "dependencies": { - "@angular/common": "^20.1.0", - "@angular/compiler": "^20.1.0", - "@angular/core": "^20.1.0", - "@angular/forms": "^20.1.0", - "@angular/platform-browser": "^20.1.0", - "@angular/router": "^20.1.0", + "@angular/animations": "^15.2.0", + "@angular/cdk": "^15.2.9", + "@angular/common": "^15.2.0", + "@angular/compiler": "^15.2.0", + "@angular/core": "^15.2.0", + "@angular/forms": "^15.2.0", + "@angular/material": "^15.2.9", + "@angular/platform-browser": "^15.2.0", + "@angular/platform-browser-dynamic": "^15.2.0", + "@angular/router": "^15.2.0", + "ag-grid-angular": "^28.2.1", + "ag-grid-community": "^28.2.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", - "zone.js": "~0.15.0" + "zone.js": "~0.12.0" }, "devDependencies": { - "@angular/build": "^20.1.3", - "@angular/cli": "^20.1.3", - "@angular/compiler-cli": "^20.1.0", - "@types/jasmine": "~5.1.0", - "jasmine-core": "~5.8.0", + "@angular-devkit/build-angular": "^15.2.11", + "@angular/cli": "~15.2.11", + "@angular/compiler-cli": "^15.2.0", + "@types/jasmine": "~4.3.0", + "jasmine-core": "~4.5.0", "json-server": "^1.0.0-beta.3", "karma": "~6.4.0", - "karma-chrome-launcher": "~3.2.0", + "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.8.2" + "karma-jasmine-html-reporter": "~2.0.0", + "typescript": "~4.9.4" } -} +} \ No newline at end of file diff --git a/client-management/public/favicon.ico b/client-management/public/favicon.ico deleted file mode 100644 index 57614f9c967596fad0a3989bec2b1deff33034f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 Nr__JTFzvMr^FKp4S3v*( diff --git a/client-management/src/app/app-routing.module.ts b/client-management/src/app/app-routing.module.ts new file mode 100644 index 0000000..5b7bcb4 --- /dev/null +++ b/client-management/src/app/app-routing.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ClientDetailComponent } from './client-detail/client-detail.component'; +import { ClientFormComponent } from './client-form/client-form.component'; +import { ClientListComponent } from './client-list/client-list.component'; +import { LoginComponent } from './login/login.component'; + +const routes: Routes = [ + { path: '', redirectTo: 'login', pathMatch: 'full' }, + { path: 'login', component: LoginComponent }, + { path: 'clients', component: ClientListComponent }, + { path: 'clients/add', component: ClientFormComponent }, + { path: 'clients/edit/:id', component: ClientFormComponent }, + { path: 'clients/:id', component: ClientDetailComponent } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git a/client-management/src/app/app.component.html b/client-management/src/app/app.component.html new file mode 100644 index 0000000..0680b43 --- /dev/null +++ b/client-management/src/app/app.component.html @@ -0,0 +1 @@ + diff --git a/client-management/src/app/app.scss b/client-management/src/app/app.component.scss similarity index 100% rename from client-management/src/app/app.scss rename to client-management/src/app/app.component.scss diff --git a/client-management/src/app/app.component.spec.ts b/client-management/src/app/app.component.spec.ts new file mode 100644 index 0000000..788ffc6 --- /dev/null +++ b/client-management/src/app/app.component.spec.ts @@ -0,0 +1,35 @@ +import { TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + RouterTestingModule + ], + declarations: [ + AppComponent + ], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'client-management'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('client-management'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('.content span')?.textContent).toContain('client-management app is running!'); + }); +}); diff --git a/client-management/src/app/app.component.ts b/client-management/src/app/app.component.ts new file mode 100644 index 0000000..52ce56b --- /dev/null +++ b/client-management/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent { + title = 'client-management'; +} diff --git a/client-management/src/app/app.config.ts b/client-management/src/app/app.config.ts deleted file mode 100644 index d953f4c..0000000 --- a/client-management/src/app/app.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter } from '@angular/router'; - -import { routes } from './app.routes'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideBrowserGlobalErrorListeners(), - provideZoneChangeDetection({ eventCoalescing: true }), - provideRouter(routes) - ] -}; diff --git a/client-management/src/app/app.html b/client-management/src/app/app.html deleted file mode 100644 index 7528372..0000000 --- a/client-management/src/app/app.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title() }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'}, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - - diff --git a/client-management/src/app/app.module.ts b/client-management/src/app/app.module.ts new file mode 100644 index 0000000..16f5d12 --- /dev/null +++ b/client-management/src/app/app.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { ClientListComponent } from './client-list/client-list.component'; +import { LoginComponent } from './login/login.component'; +import { ClientFormComponent } from './client-form/client-form.component'; +import { ClientDetailComponent } from './client-detail/client-detail.component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatDividerModule } from '@angular/material/divider'; +import { HttpClientModule } from '@angular/common/http'; +import { AgGridModule } from 'ag-grid-angular'; + + +@NgModule({ + declarations: [ + AppComponent, + LoginComponent, + ClientListComponent, + ClientFormComponent, + ClientDetailComponent + ], + imports: [ + BrowserModule, + AppRoutingModule, + BrowserAnimationsModule, + HttpClientModule, + MatDividerModule, + MatInputModule, + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatIconModule, + ReactiveFormsModule, + AgGridModule, + FormsModule, + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/client-management/src/app/app.routes.ts b/client-management/src/app/app.routes.ts deleted file mode 100644 index 3e73375..0000000 --- a/client-management/src/app/app.routes.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Routes } from '@angular/router'; -import { Login } from './components/login/login'; -import { ClientList } from './components/client-list/client-list'; -import { ClientDetail } from './components/client-detail/client-detail'; - -export const routes: Routes = [ - { path: '', redirectTo: 'login', pathMatch: 'full' }, - { path: 'login', component: Login }, - { path: 'clients', component: ClientList }, - { path: 'clients/:id', component: ClientDetail } -]; \ No newline at end of file diff --git a/client-management/src/app/app.spec.ts b/client-management/src/app/app.spec.ts deleted file mode 100644 index cc25f22..0000000 --- a/client-management/src/app/app.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { App } from './app'; - -describe('App', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [App], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(App); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, client-management'); - }); -}); diff --git a/client-management/src/app/app.ts b/client-management/src/app/app.ts deleted file mode 100644 index 154f7c4..0000000 --- a/client-management/src/app/app.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component, signal } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -@Component({ - selector: 'app-root', - imports: [RouterOutlet], - templateUrl: './app.html', - styleUrl: './app.scss' -}) -export class App { - protected readonly title = signal('client-management'); -} diff --git a/client-management/src/app/components/client-detail/client-detail.html b/client-management/src/app/client-detail/client-detail.component.html similarity index 100% rename from client-management/src/app/components/client-detail/client-detail.html rename to client-management/src/app/client-detail/client-detail.component.html diff --git a/client-management/src/app/components/client-detail/client-detail.scss b/client-management/src/app/client-detail/client-detail.component.scss similarity index 100% rename from client-management/src/app/components/client-detail/client-detail.scss rename to client-management/src/app/client-detail/client-detail.component.scss diff --git a/client-management/src/app/components/client-list/client-list.spec.ts b/client-management/src/app/client-detail/client-detail.component.spec.ts similarity index 51% rename from client-management/src/app/components/client-list/client-list.spec.ts rename to client-management/src/app/client-detail/client-detail.component.spec.ts index 7264841..9c30ea9 100644 --- a/client-management/src/app/components/client-list/client-list.spec.ts +++ b/client-management/src/app/client-detail/client-detail.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ClientList } from './client-list'; +import { ClientDetailComponent } from './client-detail.component'; -describe('ClientList', () => { - let component: ClientList; - let fixture: ComponentFixture; +describe('ClientDetailComponent', () => { + let component: ClientDetailComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ClientList] + declarations: [ ClientDetailComponent ] }) .compileComponents(); - fixture = TestBed.createComponent(ClientList); + fixture = TestBed.createComponent(ClientDetailComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client-management/src/app/client-detail/client-detail.component.ts b/client-management/src/app/client-detail/client-detail.component.ts new file mode 100644 index 0000000..aa15a80 --- /dev/null +++ b/client-management/src/app/client-detail/client-detail.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-client-detail', + templateUrl: './client-detail.component.html', + styleUrls: ['./client-detail.component.scss'] +}) +export class ClientDetailComponent { + +} diff --git a/client-management/src/app/components/client-form/client-form.html b/client-management/src/app/client-form/client-form.component.html similarity index 100% rename from client-management/src/app/components/client-form/client-form.html rename to client-management/src/app/client-form/client-form.component.html diff --git a/client-management/src/app/components/client-form/client-form.scss b/client-management/src/app/client-form/client-form.component.scss similarity index 100% rename from client-management/src/app/components/client-form/client-form.scss rename to client-management/src/app/client-form/client-form.component.scss diff --git a/client-management/src/app/components/client-form/client-form.spec.ts b/client-management/src/app/client-form/client-form.component.spec.ts similarity index 52% rename from client-management/src/app/components/client-form/client-form.spec.ts rename to client-management/src/app/client-form/client-form.component.spec.ts index feee444..f52a030 100644 --- a/client-management/src/app/components/client-form/client-form.spec.ts +++ b/client-management/src/app/client-form/client-form.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ClientForm } from './client-form'; +import { ClientFormComponent } from './client-form.component'; -describe('ClientForm', () => { - let component: ClientForm; - let fixture: ComponentFixture; +describe('ClientFormComponent', () => { + let component: ClientFormComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ClientForm] + declarations: [ ClientFormComponent ] }) .compileComponents(); - fixture = TestBed.createComponent(ClientForm); + fixture = TestBed.createComponent(ClientFormComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client-management/src/app/client-form/client-form.component.ts b/client-management/src/app/client-form/client-form.component.ts new file mode 100644 index 0000000..d1a7256 --- /dev/null +++ b/client-management/src/app/client-form/client-form.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-client-form', + templateUrl: './client-form.component.html', + styleUrls: ['./client-form.component.scss'] +}) +export class ClientFormComponent { + +} diff --git a/client-management/src/app/client-list/client-list.component.html b/client-management/src/app/client-list/client-list.component.html new file mode 100644 index 0000000..d19d1a6 --- /dev/null +++ b/client-management/src/app/client-list/client-list.component.html @@ -0,0 +1,18 @@ +
+

Welcome, {{ userName }}

+ +
+ + + +
+ + +
diff --git a/client-management/src/app/client-list/client-list.component.scss b/client-management/src/app/client-list/client-list.component.scss new file mode 100644 index 0000000..c00ceb0 --- /dev/null +++ b/client-management/src/app/client-list/client-list.component.scss @@ -0,0 +1,33 @@ +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + h2 { + margin: 0; + font-weight: 500; + color: #333; + } +} + +.grid-wrapper { + padding: 20px; + background-color: white; + height: calc(100vh - 130px); +} + +.logout { + background: black !important; + color: white !important; +} + +.divider { + margin-top: 10px !important; + margin-bottom: 10px !important; + background-color: black !important; +} + +:host ::ng-deep .ag-theme-alpine { + height: 100%; + width: 100%; +} diff --git a/client-management/src/app/components/client-detail/client-detail.spec.ts b/client-management/src/app/client-list/client-list.component.spec.ts similarity index 52% rename from client-management/src/app/components/client-detail/client-detail.spec.ts rename to client-management/src/app/client-list/client-list.component.spec.ts index 05c5955..61b6969 100644 --- a/client-management/src/app/components/client-detail/client-detail.spec.ts +++ b/client-management/src/app/client-list/client-list.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ClientDetail } from './client-detail'; +import { ClientListComponent } from './client-list.component'; -describe('ClientDetail', () => { - let component: ClientDetail; - let fixture: ComponentFixture; +describe('ClientListComponent', () => { + let component: ClientListComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ClientDetail] + declarations: [ ClientListComponent ] }) .compileComponents(); - fixture = TestBed.createComponent(ClientDetail); + fixture = TestBed.createComponent(ClientListComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client-management/src/app/client-list/client-list.component.ts b/client-management/src/app/client-list/client-list.component.ts new file mode 100644 index 0000000..25ee01d --- /dev/null +++ b/client-management/src/app/client-list/client-list.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthService } from '../services/auth.service'; +import { ClientService } from '../services/client-service.service'; +import { GridOptions } from 'ag-grid-community'; + +@Component({ + selector: 'app-client-list', + templateUrl: './client-list.component.html', + styleUrls: ['./client-list.component.scss'] +}) +export class ClientListComponent implements OnInit { + + userName: string = 'User'; + clients: any[] = []; + + columnDefs = [ + { + headerName: 'Profile', + field: 'picture', + cellRenderer: (params: any) => + `profile`, + width: 80, + hide: true + }, + { field: 'name', headerName: 'Client Name' }, + { field: 'gender', headerName: 'Gender', width: 100, hide: true }, + { field: 'company', headerName: 'Company' }, + { field: 'currency', headerName: 'Currency', width: 100, hide: true }, + { + field: 'subscriptionCost', + headerName: 'Subscription Cost', + valueFormatter: (params: any) => + Number(params.value).toLocaleString(undefined, { + style: 'currency', + currency: params.data.currency || 'USD', + maximumFractionDigits: 2 + }) + }, + { field: 'age', headerName: 'Age', width: 80 }, + { + headerName: 'Registered', + field: 'registered', + hide: true, + valueFormatter: (params: { value: string; }) => { + const cleaned = params.value.replace(' -', '-'); + const date = new Date(cleaned); + return isNaN(date.getTime()) + ? 'Invalid Date' + : date.toLocaleDateString('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric' + }); + } +} +]; + +gridOptions: GridOptions = { + defaultColDef: { + resizable: true, + sortable: true, + filter: true + }, + sideBar: { + toolPanels: [ + { + id: 'columns', + labelDefault: 'Columns', + labelKey: 'columns', + iconKey: 'columns', + toolPanel: 'agColumnsToolPanel' + } + ], + defaultToolPanel: 'columns' +}, + animateRows: true, + rowSelection: 'single' + }; + + constructor(private router: Router, private clientService: ClientService, private authService: AuthService) { + this.userName = this.authService.getUserName(); + } + + ngOnInit(): void { + this.clientService.getClients().subscribe(data => { + this.clients = data; + }); + } + + logout() { + this.authService.logout(); + } + +} diff --git a/client-management/src/app/components/client-detail/client-detail.ts b/client-management/src/app/components/client-detail/client-detail.ts deleted file mode 100644 index 94d66bb..0000000 --- a/client-management/src/app/components/client-detail/client-detail.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-client-detail', - imports: [], - templateUrl: './client-detail.html', - styleUrl: './client-detail.scss' -}) -export class ClientDetail { - -} diff --git a/client-management/src/app/components/client-form/client-form.ts b/client-management/src/app/components/client-form/client-form.ts deleted file mode 100644 index c159c7d..0000000 --- a/client-management/src/app/components/client-form/client-form.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-client-form', - imports: [], - templateUrl: './client-form.html', - styleUrl: './client-form.scss' -}) -export class ClientForm { - -} diff --git a/client-management/src/app/components/client-list/client-list.html b/client-management/src/app/components/client-list/client-list.html deleted file mode 100644 index a1e43bc..0000000 --- a/client-management/src/app/components/client-list/client-list.html +++ /dev/null @@ -1 +0,0 @@ -

client-list works!

diff --git a/client-management/src/app/components/client-list/client-list.ts b/client-management/src/app/components/client-list/client-list.ts deleted file mode 100644 index 6dd619c..0000000 --- a/client-management/src/app/components/client-list/client-list.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-client-list', - imports: [], - templateUrl: './client-list.html', - styleUrl: './client-list.scss' -}) -export class ClientList { - -} diff --git a/client-management/src/app/components/login/login.html b/client-management/src/app/components/login/login.html deleted file mode 100644 index 147cfc4..0000000 --- a/client-management/src/app/components/login/login.html +++ /dev/null @@ -1 +0,0 @@ -

login works!

diff --git a/client-management/src/app/components/login/login.scss b/client-management/src/app/components/login/login.scss deleted file mode 100644 index e69de29..0000000 diff --git a/client-management/src/app/components/login/login.ts b/client-management/src/app/components/login/login.ts deleted file mode 100644 index a9e4eee..0000000 --- a/client-management/src/app/components/login/login.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-login', - imports: [], - templateUrl: './login.html', - styleUrl: './login.scss' -}) -export class Login { - -} diff --git a/client-management/src/app/login/login.component.html b/client-management/src/app/login/login.component.html new file mode 100644 index 0000000..1f677b2 --- /dev/null +++ b/client-management/src/app/login/login.component.html @@ -0,0 +1,32 @@ + diff --git a/client-management/src/app/login/login.component.scss b/client-management/src/app/login/login.component.scss new file mode 100644 index 0000000..dc9c5ff --- /dev/null +++ b/client-management/src/app/login/login.component.scss @@ -0,0 +1,42 @@ +.login-wrapper { + background-color: #ffffff; + padding: 32px; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + width: 100%; + max-width: 400px; + margin: auto; +} + +.login-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; /* Full screen centering */ +} + +.form-container { + display: flex; + flex-direction: column; + gap: 24px; +} + +.help-icon { + position: absolute; + top: 8px; + right: 8px; +} + +.full-width { + width: 100%; +} + +.submit-btn { + margin-top: 16px; +} + +.error-message { + color: #c62828; + margin-top: 10px; + font-weight: 500; +} diff --git a/client-management/src/app/components/login/login.spec.ts b/client-management/src/app/login/login.component.spec.ts similarity index 55% rename from client-management/src/app/components/login/login.spec.ts rename to client-management/src/app/login/login.component.spec.ts index dd8bbb3..10eca24 100644 --- a/client-management/src/app/components/login/login.spec.ts +++ b/client-management/src/app/login/login.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Login } from './login'; +import { LoginComponent } from './login.component'; -describe('Login', () => { - let component: Login; - let fixture: ComponentFixture; +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [Login] + declarations: [ LoginComponent ] }) .compileComponents(); - fixture = TestBed.createComponent(Login); + fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client-management/src/app/login/login.component.ts b/client-management/src/app/login/login.component.ts new file mode 100644 index 0000000..7778a28 --- /dev/null +++ b/client-management/src/app/login/login.component.ts @@ -0,0 +1,56 @@ +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AuthService } from '../services/auth.service'; +import { Router } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; + + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'], +}) +export class LoginComponent { + loginForm: FormGroup; + loginError: boolean = false; + + constructor(private fb: FormBuilder, private http: HttpClient, private authService: AuthService, private router: Router) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: [ + '', + [ + Validators.required, + Validators.minLength(8), + Validators.pattern( + '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$' + ), + ], + ], + }); + } + + get email() { + return this.loginForm.get('email'); + } + + get password() { + return this.loginForm.get('password'); + } + + onSubmit() { + if (this.loginForm.valid) { + const { email, password } = this.loginForm.value; + this.authService.login(email, password).subscribe({ + next: user => { + this.router.navigate(['/clients'], { + state: { name: user.name }, + }); + }, + error: () => { + alert('You are not a registered admin or your credentials are incorrect.'); + }, + }); + } + } +} diff --git a/client-management/src/app/services/client.spec.ts b/client-management/src/app/services/auth.service.spec.ts similarity index 57% rename from client-management/src/app/services/client.spec.ts rename to client-management/src/app/services/auth.service.spec.ts index 935a715..f1251ca 100644 --- a/client-management/src/app/services/client.spec.ts +++ b/client-management/src/app/services/auth.service.spec.ts @@ -1,13 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { Client } from './client'; +import { AuthService } from './auth.service'; -describe('Client', () => { - let service: Client; +describe('AuthService', () => { + let service: AuthService; beforeEach(() => { TestBed.configureTestingModule({}); - service = TestBed.inject(Client); + service = TestBed.inject(AuthService); }); it('should be created', () => { diff --git a/client-management/src/app/services/auth.service.ts b/client-management/src/app/services/auth.service.ts new file mode 100644 index 0000000..a09e4c5 --- /dev/null +++ b/client-management/src/app/services/auth.service.ts @@ -0,0 +1,41 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { catchError, map, Observable, throwError } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + private userName: string = ''; + + constructor(private router: Router, private http: HttpClient) {} + + setUserName(name: string) { + this.userName = name; + } + getUserName(): string { + return this.userName; + } + + login(email: string, password: string): Observable { + return this.http + .get(`https://glowing-fishstick-g6vjrxjwjwpfvxpp-4090.app.github.dev/users?email=${email}`) + .pipe( + map(users => { + const user = users[0]; + if (user && user.password === password) { + this.userName = user.name; + return user; + } else { + throw new Error('Invalid credentials'); + } + }), + catchError(err => throwError(() => new Error('Login failed'))) + ); + } + + logout(): void { + this.router.navigate(['/login']); + } +} diff --git a/client-management/src/app/services/client-service.service.spec.ts b/client-management/src/app/services/client-service.service.spec.ts new file mode 100644 index 0000000..ece22ff --- /dev/null +++ b/client-management/src/app/services/client-service.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ClientServiceService } from './client-service.service'; + +describe('ClientServiceService', () => { + let service: ClientServiceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ClientServiceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/services/client-service.service.ts b/client-management/src/app/services/client-service.service.ts new file mode 100644 index 0000000..af12b09 --- /dev/null +++ b/client-management/src/app/services/client-service.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class ClientService { + private apiUrl = 'https://glowing-fishstick-g6vjrxjwjwpfvxpp-4090.app.github.dev/clients'; + + constructor(private http: HttpClient) {} + + getClients(): Observable { + return this.http.get(this.apiUrl); + } +} diff --git a/client-management/src/app/services/client.ts b/client-management/src/app/services/client.ts deleted file mode 100644 index f4c8a10..0000000 --- a/client-management/src/app/services/client.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class Client { - -} diff --git a/client-management/src/app/components/client-list/client-list.scss b/client-management/src/assets/.gitkeep similarity index 100% rename from client-management/src/app/components/client-list/client-list.scss rename to client-management/src/assets/.gitkeep diff --git a/client-management/src/favicon.ico b/client-management/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..997406ad22c29aae95893fb3d666c30258a09537 GIT binary patch literal 948 zcmV;l155mgP)CBYU7IjCFmI-B}4sMJt3^s9NVg!P0 z6hDQy(L`XWMkB@zOLgN$4KYz;j0zZxq9KKdpZE#5@k0crP^5f9KO};h)ZDQ%ybhht z%t9#h|nu0K(bJ ztIkhEr!*UyrZWQ1k2+YkGqDi8Z<|mIN&$kzpKl{cNP=OQzXHz>vn+c)F)zO|Bou>E z2|-d_=qY#Y+yOu1a}XI?cU}%04)zz%anD(XZC{#~WreV!a$7k2Ug`?&CUEc0EtrkZ zL49MB)h!_K{H(*l_93D5tO0;BUnvYlo+;yss%n^&qjt6fZOa+}+FDO(~2>G z2dx@=JZ?DHP^;b7*Y1as5^uphBsh*s*z&MBd?e@I>-9kU>63PjP&^#5YTOb&x^6Cf z?674rmSHB5Fk!{Gv7rv!?qX#ei_L(XtwVqLX3L}$MI|kJ*w(rhx~tc&L&xP#?cQow zX_|gx$wMr3pRZIIr_;;O|8fAjd;1`nOeu5K(pCu7>^3E&D2OBBq?sYa(%S?GwG&_0-s%_v$L@R!5H_fc)lOb9ZoOO#p`Nn`KU z3LTTBtjwo`7(HA6 z7gmO$yTR!5L>Bsg!X8616{JUngg_@&85%>W=mChTR;x4`P=?PJ~oPuy5 zU-L`C@_!34D21{fD~Y8NVnR3t;aqZI3fIhmgmx}$oc-dKDC6Ap$Gy>a!`A*x2L1v0 WcZ@i?LyX}70000 + + + - + diff --git a/client-management/src/main.ts b/client-management/src/main.ts index 5df75f9..c58dc05 100644 --- a/client-management/src/main.ts +++ b/client-management/src/main.ts @@ -1,6 +1,7 @@ -import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app/app.config'; -import { App } from './app/app'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -bootstrapApplication(App, appConfig) - .catch((err) => console.error(err)); +import { AppModule } from './app/app.module'; + + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/client-management/src/styles.scss b/client-management/src/styles.scss index 90d4ee0..d659e2f 100644 --- a/client-management/src/styles.scss +++ b/client-management/src/styles.scss @@ -1 +1,42 @@ + +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming +@use '@angular/material' as mat; +// Plus imports for other components in your app. + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat.core(); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$client-management-primary: mat.define-palette(mat.$indigo-palette); +$client-management-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$client-management-warn: mat.define-palette(mat.$red-palette); + +// Create the theme object. A theme consists of configurations for individual +// theming systems such as "color" or "typography". +$client-management-theme: mat.define-light-theme(( + color: ( + primary: $client-management-primary, + accent: $client-management-accent, + warn: $client-management-warn, + ) +)); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include mat.all-component-themes($client-management-theme); + /* You can add global styles to this file, and also import other style files */ + +@import 'ag-grid-community/styles/ag-grid.css'; +@import 'ag-grid-community/styles/ag-theme-alpine.css'; + +html, body { height: 100%; } +body { margin: 0; font-family: Gilroy, sans-serif; background-color: #feaa8c; } diff --git a/client-management/tsconfig.app.json b/client-management/tsconfig.app.json index 264f459..374cc9d 100644 --- a/client-management/tsconfig.app.json +++ b/client-management/tsconfig.app.json @@ -1,15 +1,14 @@ -/* 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. */ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, - "include": [ - "src/**/*.ts" + "files": [ + "src/main.ts" ], - "exclude": [ - "src/**/*.spec.ts" + "include": [ + "src/**/*.d.ts" ] } diff --git a/client-management/tsconfig.json b/client-management/tsconfig.json index e4955f2..ed966d4 100644 --- a/client-management/tsconfig.json +++ b/client-management/tsconfig.json @@ -1,34 +1,33 @@ -/* 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. */ +/* 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, - "skipLibCheck": true, - "isolatedModules": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, "experimentalDecorators": true, + "moduleResolution": "node", "importHelpers": true, "target": "ES2022", - "module": "preserve" + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, - "typeCheckHostBindings": true, "strictTemplates": true - }, - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] + } } diff --git a/client-management/tsconfig.spec.json b/client-management/tsconfig.spec.json index 04df34c..be7e9da 100644 --- a/client-management/tsconfig.spec.json +++ b/client-management/tsconfig.spec.json @@ -1,5 +1,4 @@ -/* 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. */ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "./tsconfig.json", "compilerOptions": { @@ -9,6 +8,7 @@ ] }, "include": [ - "src/**/*.ts" + "src/**/*.spec.ts", + "src/**/*.d.ts" ] } diff --git a/clients.json b/clients.json index 15fcbb7..4a71316 100644 --- a/clients.json +++ b/clients.json @@ -1,4 +1,12 @@ { + "users": [ + { + "id": 1, + "email": "admin@virtusize.com", + "password": "Admin@123", + "name": "Virtusize Admin" + } + ], "clients": [ { "id": "0", From c199a2cae60b7726f943491fd25fee1e740326c7 Mon Sep 17 00:00:00 2001 From: Raahim08 <71621239+Raahim08@users.noreply.github.com> Date: Sun, 3 Aug 2025 15:46:19 +0000 Subject: [PATCH 4/5] client management system --- .../action-button-renderer.component.scss | 10 + .../action-button-renderer.component.spec.ts | 23 +++ .../action-button-renderer.component.ts | 56 ++++++ .../src/app/app-routing.module.ts | 4 +- client-management/src/app/app.module.ts | 6 +- .../client-detail.component.html | 1 - .../client-detail.component.scss | 0 .../client-detail.component.spec.ts | 23 --- .../client-detail/client-detail.component.ts | 10 - .../client-form/client-form.component.html | 32 +++- .../client-form/client-form.component.scss | 83 +++++++++ .../app/client-form/client-form.component.ts | 77 +++++++- .../client-list/client-list.component.html | 23 ++- .../client-list/client-list.component.scss | 56 +++++- .../app/client-list/client-list.component.ts | 175 ++++++++++++------ .../src/app/login/login.component.html | 1 + .../src/app/model/client-model.ts | 11 ++ .../app/services/client-service.service.ts | 20 +- clients.json | 48 ++++- 19 files changed, 539 insertions(+), 120 deletions(-) create mode 100644 client-management/src/app/action-button-renderer/action-button-renderer.component.scss create mode 100644 client-management/src/app/action-button-renderer/action-button-renderer.component.spec.ts create mode 100644 client-management/src/app/action-button-renderer/action-button-renderer.component.ts delete mode 100644 client-management/src/app/client-detail/client-detail.component.html delete mode 100644 client-management/src/app/client-detail/client-detail.component.scss delete mode 100644 client-management/src/app/client-detail/client-detail.component.spec.ts delete mode 100644 client-management/src/app/client-detail/client-detail.component.ts create mode 100644 client-management/src/app/model/client-model.ts diff --git a/client-management/src/app/action-button-renderer/action-button-renderer.component.scss b/client-management/src/app/action-button-renderer/action-button-renderer.component.scss new file mode 100644 index 0000000..61780e6 --- /dev/null +++ b/client-management/src/app/action-button-renderer/action-button-renderer.component.scss @@ -0,0 +1,10 @@ +.action-btn { + + mat-icon { + font-size: 18px; + } + + button { + margin: 0 2px; + } +} \ No newline at end of file diff --git a/client-management/src/app/action-button-renderer/action-button-renderer.component.spec.ts b/client-management/src/app/action-button-renderer/action-button-renderer.component.spec.ts new file mode 100644 index 0000000..5b09930 --- /dev/null +++ b/client-management/src/app/action-button-renderer/action-button-renderer.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ActionButtonRendererComponent } from './action-button-renderer.component'; + +describe('ActionButtonRendererComponent', () => { + let component: ActionButtonRendererComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ActionButtonRendererComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ActionButtonRendererComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client-management/src/app/action-button-renderer/action-button-renderer.component.ts b/client-management/src/app/action-button-renderer/action-button-renderer.component.ts new file mode 100644 index 0000000..cb39e6c --- /dev/null +++ b/client-management/src/app/action-button-renderer/action-button-renderer.component.ts @@ -0,0 +1,56 @@ +import { Component } from '@angular/core'; +import { ICellRendererAngularComp } from 'ag-grid-angular'; +import { ClientService } from '../services/client-service.service'; +import { MatDialog } from '@angular/material/dialog'; +import { ClientFormComponent } from '../client-form/client-form.component'; + +@Component({ + selector: 'app-action-buttons-renderer', + template: ` + + + + `, + styleUrls: ['./action-button-renderer.component.scss'] +}) +export class ActionButtonRendererComponent implements ICellRendererAngularComp { + constructor(private clientService: ClientService, private dialog: MatDialog){} + params: any; + + agInit(params: any): void { + this.params = params; + } + + refresh(): boolean { + return false; + } + + onEdit(): void { + this.dialog.open(ClientFormComponent, { + width: '400px', + data: { + mode: 'edit', + client: { ...this.params.data } + } + }).afterClosed().subscribe(updatedClient => { + if (updatedClient) { + this.clientService.updateClient(updatedClient).subscribe(() => { + this.params.api.applyTransaction({ update: [updatedClient] }); + }); + } + }); + } + + onDelete(): void { + const confirmed = confirm(`Delete client "${this.params.data.name}"?`); + if (confirmed) { + this.clientService.deleteClient(this.params.data.id).subscribe(() => { + this.params.api.applyTransaction({ remove: [this.params.data] }); + }); + } + } +} diff --git a/client-management/src/app/app-routing.module.ts b/client-management/src/app/app-routing.module.ts index 5b7bcb4..62d58cb 100644 --- a/client-management/src/app/app-routing.module.ts +++ b/client-management/src/app/app-routing.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ClientDetailComponent } from './client-detail/client-detail.component'; import { ClientFormComponent } from './client-form/client-form.component'; import { ClientListComponent } from './client-list/client-list.component'; import { LoginComponent } from './login/login.component'; @@ -10,8 +9,7 @@ const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: 'clients', component: ClientListComponent }, { path: 'clients/add', component: ClientFormComponent }, - { path: 'clients/edit/:id', component: ClientFormComponent }, - { path: 'clients/:id', component: ClientDetailComponent } + { path: 'clients/edit/:id', component: ClientFormComponent } ]; @NgModule({ diff --git a/client-management/src/app/app.module.ts b/client-management/src/app/app.module.ts index 16f5d12..02b648e 100644 --- a/client-management/src/app/app.module.ts +++ b/client-management/src/app/app.module.ts @@ -6,7 +6,6 @@ import { AppComponent } from './app.component'; import { ClientListComponent } from './client-list/client-list.component'; import { LoginComponent } from './login/login.component'; import { ClientFormComponent } from './client-form/client-form.component'; -import { ClientDetailComponent } from './client-detail/client-detail.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; @@ -17,6 +16,8 @@ import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; import { HttpClientModule } from '@angular/common/http'; import { AgGridModule } from 'ag-grid-angular'; +import { MatDialogModule } from '@angular/material/dialog'; +import { ActionButtonRendererComponent } from './action-button-renderer/action-button-renderer.component'; @NgModule({ @@ -25,7 +26,7 @@ import { AgGridModule } from 'ag-grid-angular'; LoginComponent, ClientListComponent, ClientFormComponent, - ClientDetailComponent + ActionButtonRendererComponent ], imports: [ BrowserModule, @@ -41,6 +42,7 @@ import { AgGridModule } from 'ag-grid-angular'; ReactiveFormsModule, AgGridModule, FormsModule, + MatDialogModule ], providers: [], bootstrap: [AppComponent] diff --git a/client-management/src/app/client-detail/client-detail.component.html b/client-management/src/app/client-detail/client-detail.component.html deleted file mode 100644 index 52bb8c3..0000000 --- a/client-management/src/app/client-detail/client-detail.component.html +++ /dev/null @@ -1 +0,0 @@ -

client-detail works!

diff --git a/client-management/src/app/client-detail/client-detail.component.scss b/client-management/src/app/client-detail/client-detail.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/client-management/src/app/client-detail/client-detail.component.spec.ts b/client-management/src/app/client-detail/client-detail.component.spec.ts deleted file mode 100644 index 9c30ea9..0000000 --- a/client-management/src/app/client-detail/client-detail.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ClientDetailComponent } from './client-detail.component'; - -describe('ClientDetailComponent', () => { - let component: ClientDetailComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ ClientDetailComponent ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(ClientDetailComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/client-management/src/app/client-detail/client-detail.component.ts b/client-management/src/app/client-detail/client-detail.component.ts deleted file mode 100644 index aa15a80..0000000 --- a/client-management/src/app/client-detail/client-detail.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-client-detail', - templateUrl: './client-detail.component.html', - styleUrls: ['./client-detail.component.scss'] -}) -export class ClientDetailComponent { - -} diff --git a/client-management/src/app/client-form/client-form.component.html b/client-management/src/app/client-form/client-form.component.html index c07c1f2..af09cf2 100644 --- a/client-management/src/app/client-form/client-form.component.html +++ b/client-management/src/app/client-form/client-form.component.html @@ -1 +1,31 @@ -

client-form works!

+

+ {{ mode === 'edit' ? 'Edit Client' : mode === 'view' ? 'View Client' : 'Add New Client' }} +

+ +
+
+ + {{ field.label }} + + + {{ field.label }} is required + + +
+
+ + + + + + + + + diff --git a/client-management/src/app/client-form/client-form.component.scss b/client-management/src/app/client-form/client-form.component.scss index e69de29..cc1858d 100644 --- a/client-management/src/app/client-form/client-form.component.scss +++ b/client-management/src/app/client-form/client-form.component.scss @@ -0,0 +1,83 @@ +:host { + display: block; + width: 100%; + max-width: 500px; + margin: auto; +} + +.form-title { + text-align: center; + font-size: 1.5rem; + font-weight: 600; + color: #4a2e29; + background-color: #ffe1d6; + padding: 16px; + border-top-left-radius: 12px; + border-top-right-radius: 12px; + margin: -24px 0px 16px 0px; +} + +.form-wrapper { + display: flex; + flex-direction: column; + gap: 16px; + padding: 8px 24px; + background-color: #fffaf8; + max-height: 60vh; + overflow-y: auto; + + // scroll handling + scrollbar-width: thin; + scrollbar-color: #ffd2bd transparent; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background-color: #ffd2bd; + border-radius: 4px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } +} + +.form-group { + display: flex; + flex-direction: column; +} + +.full-width { + width: 100%; +} + +mat-form-field { + --mat-form-field-outline-color: #ff815f; + --mat-form-field-hover-outline-color: #feaa8c; + --mat-form-field-disabled-color: #ccc; + --mat-form-field-label-color: #4a2e29; +} + +.dialog-actions { + margin-top: 20px; + padding: 0 24px 12px 24px; + background-color: #fffaf8; + border-top: 1px solid #ffe1d6; + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; +} + +:host-context(.view-mode) mat-form-field { + opacity: 1; // override disabled opacity + pointer-events: none; // prevents editing +} + +:host-context(.view-mode) input { + color: #333 !important; + -webkit-text-fill-color: #333 !important; + font-weight: 500; + cursor: default; + border-radius: 4px; +} diff --git a/client-management/src/app/client-form/client-form.component.ts b/client-management/src/app/client-form/client-form.component.ts index d1a7256..0db18c2 100644 --- a/client-management/src/app/client-form/client-form.component.ts +++ b/client-management/src/app/client-form/client-form.component.ts @@ -1,10 +1,83 @@ -import { Component } from '@angular/core'; +import { Component, ElementRef, Inject, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @Component({ selector: 'app-client-form', templateUrl: './client-form.component.html', styleUrls: ['./client-form.component.scss'] }) -export class ClientFormComponent { +export class ClientFormComponent implements OnInit { + clientForm: FormGroup; + mode: 'add' | 'edit' | 'view'; + readonly: boolean = false; + + fieldSchema = [ + { key: 'name', label: 'Name', type: 'text', required: true }, + { key: 'company', label: 'Company', type: 'text', required: true }, + { key: 'age', label: 'Age', type: 'number', required: true }, + { key: 'gender', label: 'Gender', type: 'text' }, + { key: 'picture', label: 'Picture URL', type: 'text' }, + { key: 'registered', label: 'Registered Date', type: 'date' }, + { key: 'currency', label: 'Currency', type: 'text' }, + { key: 'subscriptionCost', label: 'Subscription Cost', type: 'number', required: true } + ]; + + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private elementRef: ElementRef + ) { + this.mode = data.mode; + + this.readonly = data.readonly || false; + + const client = data.client || {}; + const today = new Date().toISOString().split('T')[0]; + + this.clientForm = this.fb.group({}); + + this.fieldSchema.forEach(field => { + let value = client[field.key] ?? (field.key === 'registered' ? today : field.type === 'number' ? 0 : ''); + + if (field.type === 'date' && typeof value === 'string') { + value = value.replace(/\s(?=[+-]\d{2}:\d{2})/, ''); + const parsed = new Date(value); + if (!isNaN(parsed.getTime())) { + value = parsed.toISOString().split('T')[0]; + } + } + + const controlConfig = { + value, + disabled: this.readonly || (field.key === 'registered' && this.mode === 'edit') + }; + + this.clientForm.addControl( + field.key, + this.fb.control(controlConfig, field.required ? Validators.required : undefined) + ); + }); + + this.clientForm.addControl('id', this.fb.control(client.id || null)); + } + + ngOnInit() { + if (this.mode === 'view') { + this.elementRef.nativeElement.classList.add('view-mode'); + } +} + + submit(): void { + if (this.clientForm.valid) { + const formValue = this.clientForm.getRawValue(); + this.dialogRef.close(formValue); + } + } + + cancel(): void { + this.dialogRef.close(); + } } diff --git a/client-management/src/app/client-list/client-list.component.html b/client-management/src/app/client-list/client-list.component.html index d19d1a6..1132b08 100644 --- a/client-management/src/app/client-list/client-list.component.html +++ b/client-management/src/app/client-list/client-list.component.html @@ -6,13 +6,18 @@

Welcome, {{ userName }}

- - +
+ +
+ +
+ + +
diff --git a/client-management/src/app/client-list/client-list.component.scss b/client-management/src/app/client-list/client-list.component.scss index c00ceb0..1488d07 100644 --- a/client-management/src/app/client-list/client-list.component.scss +++ b/client-management/src/app/client-list/client-list.component.scss @@ -10,24 +10,66 @@ } } -.grid-wrapper { - padding: 20px; - background-color: white; - height: calc(100vh - 130px); -} - .logout { background: black !important; color: white !important; } .divider { - margin-top: 10px !important; margin-bottom: 10px !important; background-color: black !important; } + + +.grid-wrapper { + display: flex; + flex-direction: column; + padding: 20px; + height: calc(100vh - 160px); + max-width: 800px; + margin: 20px auto; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + border-radius: 12px; + background-color: #fffaf8; + overflow: hidden; +} + +.client-actions { + margin-bottom: 2px; + display: flex; + justify-content: flex-end; +} + +.grid-container { + flex: 1; + overflow: auto; + min-height: 0; +} + :host ::ng-deep .ag-theme-alpine { height: 100%; width: 100%; } + + +::ng-deep .ag-theme-alpine { + --ag-background-color: #fffaf8; + --ag-header-background-color: #ffe1d6; + --ag-header-foreground-color: #4a2e29; + --ag-row-hover-color: #ffd2bd; + --ag-selected-row-background-color: #ffc3a4; + --ag-row-border-color: #ffd2bd; + --ag-odd-row-background-color: #fff2ee; + --ag-even-row-background-color: #ffffff; + --ag-font-family: 'Segoe UI', sans-serif; + --ag-font-size: 14px; + + --ag-input-border-color: #feaa8c; + --ag-input-focus-border-color: #ff815f; + --ag-checkbox-checked-color: #feaa8c; + --ag-range-selection-border-color: #feaa8c; + + --ag-chip-background-color: #ffd2bd; + --ag-chip-color: #4a2e29; +} \ No newline at end of file diff --git a/client-management/src/app/client-list/client-list.component.ts b/client-management/src/app/client-list/client-list.component.ts index 25ee01d..7593b99 100644 --- a/client-management/src/app/client-list/client-list.component.ts +++ b/client-management/src/app/client-list/client-list.component.ts @@ -1,8 +1,11 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { AuthService } from '../services/auth.service'; import { ClientService } from '../services/client-service.service'; import { GridOptions } from 'ag-grid-community'; +import { MatDialog } from '@angular/material/dialog'; +import { ClientFormComponent } from '../client-form/client-form.component'; +import { ActionButtonRendererComponent } from '../action-button-renderer/action-button-renderer.component'; +import { AgGridAngular } from 'ag-grid-angular'; @Component({ selector: 'app-client-list', @@ -10,75 +13,95 @@ import { GridOptions } from 'ag-grid-community'; styleUrls: ['./client-list.component.scss'] }) export class ClientListComponent implements OnInit { + @ViewChild(AgGridAngular) agGrid!: AgGridAngular; userName: string = 'User'; + clients: any[] = []; columnDefs = [ - { - headerName: 'Profile', - field: 'picture', - cellRenderer: (params: any) => - `profile`, - width: 80, - hide: true - }, - { field: 'name', headerName: 'Client Name' }, - { field: 'gender', headerName: 'Gender', width: 100, hide: true }, - { field: 'company', headerName: 'Company' }, - { field: 'currency', headerName: 'Currency', width: 100, hide: true }, - { - field: 'subscriptionCost', - headerName: 'Subscription Cost', - valueFormatter: (params: any) => - Number(params.value).toLocaleString(undefined, { - style: 'currency', - currency: params.data.currency || 'USD', - maximumFractionDigits: 2 - }) - }, - { field: 'age', headerName: 'Age', width: 80 }, - { - headerName: 'Registered', - field: 'registered', - hide: true, - valueFormatter: (params: { value: string; }) => { - const cleaned = params.value.replace(' -', '-'); - const date = new Date(cleaned); - return isNaN(date.getTime()) - ? 'Invalid Date' - : date.toLocaleDateString('en-GB', { - day: '2-digit', - month: 'short', - year: 'numeric' - }); - } -} -]; + { + headerName: 'Actions', + field: 'actions', + cellRenderer: 'actionButtonsRenderer', + width: 150 + }, + { + headerName: 'Profile', + field: 'picture', + cellRenderer: (params: any) => + `profile`, + width: 80, + hide: true + }, + { field: 'name', headerName: 'Client Name' }, + { field: 'gender', headerName: 'Gender', width: 100, hide: true }, + { field: 'company', headerName: 'Company', width: 150 }, + { field: 'currency', headerName: 'Currency', width: 100, hide: true }, + { + field: 'subscriptionCost', + headerName: 'Subscription Cost', + valueFormatter: (params: any) => + Number(params.value).toLocaleString(undefined, { + style: 'currency', + currency: params.data.currency || 'USD', + maximumFractionDigits: 2 + }) + }, + { field: 'age', headerName: 'Age', width: 110 }, + { + headerName: 'Registered', + field: 'registered', + hide: true, + valueFormatter: (params: { value: string }) => { + const cleaned = params.value.replace(' -', '-'); + const date = new Date(cleaned); + return isNaN(date.getTime()) + ? 'Invalid Date' + : date.toLocaleDateString('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric' + }); + } + } + ]; -gridOptions: GridOptions = { + gridOptions: GridOptions = { defaultColDef: { resizable: true, sortable: true, filter: true }, sideBar: { - toolPanels: [ - { - id: 'columns', - labelDefault: 'Columns', - labelKey: 'columns', - iconKey: 'columns', - toolPanel: 'agColumnsToolPanel' - } - ], - defaultToolPanel: 'columns' -}, + toolPanels: [ + { + id: 'columns', + labelDefault: 'Columns', + labelKey: 'columns', + iconKey: 'columns', + toolPanel: 'agColumnsToolPanel' + } + ], + defaultToolPanel: 'columns' + }, animateRows: true, - rowSelection: 'single' + rowSelection: 'single', + getRowId: params => params.data.id, + frameworkComponents: { + actionButtonsRenderer: ActionButtonRendererComponent + }, + context: { + componentParent: this + }, + onRowDoubleClicked: event => this.onRowDoubleClicked(event) }; - constructor(private router: Router, private clientService: ClientService, private authService: AuthService) { + constructor( + private clientService: ClientService, + private authService: AuthService, + private dialog: MatDialog + ) { this.userName = this.authService.getUserName(); } @@ -88,8 +111,46 @@ gridOptions: GridOptions = { }); } - logout() { + logout(): void { this.authService.logout(); } + onRowDoubleClicked(event: any): void { + const dialogRef = this.dialog.open(ClientFormComponent, { + width: '400px', + data: { mode: 'view', client: event.data, readonly: true } + }); + + dialogRef.afterClosed().subscribe(); +} + + openClientDialog(mode: 'add'): void { + const maxId = Math.max(...this.clients.map(c => +c.id), 0); + const today = new Date().toISOString().split('T')[0]; + + const newClientPayload = { + id: (maxId + 1).toString(), + name: '', + company: '', + age: null, + gender: '', + picture: 'https://via.placeholder.com/32', + registered: today, + currency: 'USD', + subscriptionCost: '' + }; + + const dialogRef = this.dialog.open(ClientFormComponent, { + width: '400px', + data: { mode, client: newClientPayload } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.clientService.addClient(result).subscribe(client => { + this.agGrid.api.applyTransaction({ add: [client] }); + }); + } + }); + } } diff --git a/client-management/src/app/login/login.component.html b/client-management/src/app/login/login.component.html index 1f677b2..be537aa 100644 --- a/client-management/src/app/login/login.component.html +++ b/client-management/src/app/login/login.component.html @@ -1,5 +1,6 @@