diff --git a/angular.json b/angular.json
index 130860a..df96668 100644
--- a/angular.json
+++ b/angular.json
@@ -2,7 +2,8 @@
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
- "packageManager": "npm"
+ "packageManager": "npm",
+ "analytics": false
},
"newProjectRoot": "projects",
"projects": {
diff --git a/frontend-neethika/.gitignore b/frontend-neethika/.gitignore
deleted file mode 100644
index c2658d7..0000000
--- a/frontend-neethika/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules/
diff --git a/frontend-neethika/src/app/app.html b/frontend-neethika/src/app/app.html
deleted file mode 100644
index 90c6b64..0000000
--- a/frontend-neethika/src/app/app.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend-neethika/src/app/app.routes.ts b/frontend-neethika/src/app/app.routes.ts
deleted file mode 100644
index 735452e..0000000
--- a/frontend-neethika/src/app/app.routes.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Routes } from '@angular/router';
-import { AuthPage } from './auth/auth-page/auth-page';
-
-export const routes: Routes = [
- { path: '', redirectTo: 'auth', pathMatch: 'full' },
- { path: 'auth', component: AuthPage },
-];
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/auth-page/auth-page.css b/frontend-neethika/src/app/auth/auth-page/auth-page.css
deleted file mode 100644
index 221e350..0000000
--- a/frontend-neethika/src/app/auth/auth-page/auth-page.css
+++ /dev/null
@@ -1,464 +0,0 @@
-/* --------- Reset --------- */
-html, body {
- margin: 0;
- padding: 0;
- height: 100%;
-}
-
-* {
- box-sizing: border-box;
-}
-
-/* --------- Layout --------- */
-.auth-shell {
- min-height: 100vh;
- display: grid;
- grid-template-columns: 60% 40%;
- font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
-}
-
-/* --------- LEFT PANEL --------- */
-.left {
- position: relative;
- overflow: hidden;
- background: #ffffff;
- padding: 64px 72px;
-}
-
-/* soft background glow */
-.left::before {
- content: "";
- position: absolute;
- inset: -60px;
- background:
- radial-gradient(circle at 18% 12%, rgba(88,150,255,0.18), transparent 46%),
- radial-gradient(circle at 86% 28%, rgba(160,120,255,0.16), transparent 50%),
- radial-gradient(circle at 55% 88%, rgba(90,220,200,0.10), transparent 45%);
- filter: blur(2px);
- pointer-events: none;
-}
-
-.left-content {
- position: relative;
- z-index: 1;
- max-width: 980px;
- margin: 0 auto;
-}
-
-/* Brand */
-.brand {
- display: inline-flex;
- align-items: center;
- gap: 12px;
- padding: 10px 12px;
- border-radius: 14px;
- background: rgba(0,0,0,0.03);
- border: 1px solid rgba(0,0,0,0.06);
-}
-
-.logo {
- width: 34px;
- height: 34px;
- border-radius: 10px;
- background: linear-gradient(135deg, #2f6bff, #1c2b6b);
- color: #fff;
- display: grid;
- place-items: center;
- font-weight: 900;
- letter-spacing: 0.02em;
-}
-
-.name {
- font-weight: 800;
- font-size: 16px;
- color: #111;
-}
-
-/* Stack layout (headline + bullets + signals below) */
-.left-stack {
- display: flex;
- flex-direction: column;
- gap: 36px;
- margin-top: 22px;
-}
-
-.left-main {
- max-width: 620px;
-}
-
-.headline {
- margin: 0 0 12px;
- font-size: 46px;
- line-height: 1.08;
- letter-spacing: -0.02em;
- color: #0b0b0b;
-}
-
-.sub {
- margin: 0 0 22px;
- color: rgba(0,0,0,0.62);
- font-size: 15px;
- line-height: 1.7;
- max-width: 560px;
-}
-
-/* Bullets */
-.bullets {
- list-style: none;
- padding: 0;
- margin: 0;
- display: grid;
- gap: 10px;
- max-width: 520px;
-}
-
-.bullets li {
- display: flex;
- align-items: flex-start;
- gap: 10px;
- padding: 10px 12px;
- border-radius: 14px;
- background: rgba(255,255,255,0.70);
- border: 1px solid rgba(0,0,0,0.06);
- box-shadow: 0 10px 25px rgba(0,0,0,0.06);
- color: rgba(0,0,0,0.78);
- font-size: 14px;
-}
-
-.check {
- width: 20px;
- height: 20px;
- border-radius: 999px;
- background: rgba(47,107,255,0.12);
- color: #2f6bff;
- display: grid;
- place-items: center;
- font-weight: 900;
- margin-top: 1px;
-}
-
-/* Signals section */
-.left-side {
- max-width: 640px;
-}
-
-.side-title {
- margin: 0 0 10px;
- font-size: 22px;
- letter-spacing: -0.01em;
- color: #0b0b0b;
-}
-
-.side-sub {
- margin: 0 0 16px;
- color: rgba(0,0,0,0.62);
- font-size: 14px;
- line-height: 1.6;
- max-width: 520px;
-}
-
-/* Signals card - premium */
-.signals-card {
- width: 100%;
- max-width: 520px;
- padding: 18px;
- border-radius: 20px;
- background: linear-gradient(135deg,
- rgba(255,255,255,0.92),
- rgba(245,248,255,0.86)
- );
- border: 1px solid rgba(0,0,0,0.08);
- box-shadow:
- 0 20px 50px rgba(0,0,0,0.12),
- inset 0 1px 0 rgba(255,255,255,0.6);
- display: grid;
- gap: 10px;
-}
-
-.signal-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 14px;
- border-radius: 999px;
- background: #fff;
- border: 1px solid rgba(0,0,0,0.05);
- box-shadow: 0 6px 14px rgba(0,0,0,0.06);
- transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
-}
-
-.signal-row::after {
- content: "";
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: #2f6bff;
-}
-
-.signal-row:hover {
- transform: translateX(4px);
- background: rgba(0,0,0,0.03);
- box-shadow: 0 10px 22px rgba(0,0,0,0.10);
-}
-
-.signal-name {
- font-size: 14px;
- font-weight: 700;
- color: rgba(0,0,0,0.85);
-}
-
-/* --------- RIGHT PANEL --------- */
-.right {
- min-height: 100vh;
- background: radial-gradient(circle at 30% 20%, #2a2a2a, #0b0b0b 60%);
- display: grid;
- place-items: center;
- padding: 32px;
-}
-
-.card {
- width: min(460px, 95%);
- background: rgba(20, 20, 20, 0.72);
- border: 1px solid rgba(255, 255, 255, 0.10);
- border-radius: 18px;
- padding: 28px;
- color: #fff;
- box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
- backdrop-filter: blur(14px);
-}
-
-.welcome {
- margin: 0 0 6px;
- font-size: 24px;
-}
-
-.tagline {
- margin: 0 0 18px;
- color: rgba(255, 255, 255, 0.75);
- font-size: 13px;
-}
-
-/* Google button */
-.google-btn {
- width: 100%;
- padding: 12px 14px;
- border-radius: 10px;
- border: 1px solid rgba(255, 255, 255, 0.18);
- background: rgba(255, 255, 255, 0.06);
- color: #fff;
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 10px;
-}
-
-.google-btn:hover {
- background: rgba(255, 255, 255, 0.10);
-}
-
-.google-icon {
- width: 18px;
- height: 18px;
- background: white;
- border-radius: 4px;
- padding: 2px;
-}
-
-/* Divider */
-.divider {
- margin: 16px 0 14px;
- display: grid;
- place-items: center;
- position: relative;
- color: rgba(255, 255, 255, 0.55);
- font-size: 11px;
- letter-spacing: 0.08em;
-}
-
-.divider::before {
- content: "";
- width: 100%;
- height: 1px;
- background: rgba(255, 255, 255, 0.12);
- position: absolute;
- top: 50%;
-}
-
-.divider span {
- position: relative;
- padding: 0 10px;
- background: rgba(20, 20, 20, 0.72);
-}
-
-/* Tabs */
-.tabs {
- display: grid;
- grid-template-columns: 1fr 1fr;
- background: rgba(255, 255, 255, 0.06);
- border-radius: 12px;
- padding: 6px;
- gap: 6px;
-}
-
-.tab {
- border: 0;
- border-radius: 10px;
- padding: 10px 12px;
- background: transparent;
- color: rgba(255, 255, 255, 0.80);
- cursor: pointer;
- font-weight: 700;
-}
-
-.tab.active {
- background: rgba(255, 255, 255, 0.18);
- color: #fff;
-}
-
-.tab-body {
- margin-top: 16px;
-}
-
-/* --------- Theme Toggle Button --------- */
-.theme-toggle {
- position: fixed;
- top: 24px;
- right: 24px;
- background: rgba(0,0,0,0.05);
- border: 1px solid rgba(0,0,0,0.1);
- border-radius: 50%;
- width: 40px;
- height: 40px;
- font-size: 18px;
- cursor: pointer;
- display: grid;
- place-items: center;
- z-index: 1000;
-}
-
-.auth-shell.dark .theme-toggle {
- background: rgba(255,255,255,0.1);
- border-color: rgba(255,255,255,0.2);
- color: white;
-}
-
-/* --------- DARK MODE SWAP (left becomes dark, right becomes light) --------- */
-.auth-shell.dark .left {
- background: #0b0b0b;
- color: white;
-}
-
-.auth-shell.dark .brand {
- background: rgba(255,255,255,0.06);
- border-color: rgba(255,255,255,0.12);
-}
-
-.auth-shell.dark .name,
-.auth-shell.dark .headline,
-.auth-shell.dark .side-title,
-.auth-shell.dark .signal-name {
- color: white;
-}
-
-.auth-shell.dark .sub,
-.auth-shell.dark .side-sub {
- color: rgba(255,255,255,.62);
-}
-
-.auth-shell.dark .bullets li {
- background: rgba(255,255,255,.06);
- border-color: rgba(255,255,255,.12);
- color: white;
-}
-
-.auth-shell.dark .signals-card {
- background: rgba(255,255,255,.08);
- border-color: rgba(255,255,255,.15);
-}
-
-.auth-shell.dark .signal-row {
- background: rgba(0,0,0,.55);
- border-color: rgba(255,255,255,.10);
-}
-
-.auth-shell.dark .right {
- background: #ffffff;
-}
-
-.auth-shell.dark .card {
- background: #f6f6f6;
- color: #111;
- border-color: rgba(0,0,0,0.10);
-}
-
-.auth-shell.dark .tagline,
-.auth-shell.dark .divider {
- color: rgba(0,0,0,0.55);
-}
-
-.auth-shell.dark .divider::before {
- background: rgba(0,0,0,0.12);
-}
-
-.auth-shell.dark .divider span {
- background: #f6f6f6;
-}
-
-.auth-shell.dark .tab {
- color: rgba(0,0,0,0.75);
-}
-
-.auth-shell.dark .tab.active {
- background: #111;
- color: #fff;
-}
-
-.auth-shell.dark .google-btn {
- border-color: rgba(0,0,0,0.20);
- color: #111;
- background: rgba(0,0,0,0.04);
-}
-
-.auth-shell.dark .google-btn:hover {
- background: rgba(0,0,0,0.08);
-}
-
-/* --------- Responsive: ONLY RIGHT PANEL on small screens --------- */
-@media (max-width: 900px) {
- .auth-shell {
- grid-template-columns: 1fr;
- }
-
- .left {
- display: none !important;
- }
-
- .right {
- min-height: 100vh;
- width: 100%;
- padding: 24px;
- }
-
- .card {
- width: min(460px, 96%);
- }
-}
-@media (max-width: 900px) {
- html, body {
- height: 100%;
- overflow: hidden; /* ✅ stops page scroll */
- }
-
- .right {
- height: 100vh; /* ✅ lock to screen height */
- min-height: 100vh;
- padding: 16px; /* smaller padding */
- }
-
- .card {
- max-height: calc(100vh - 32px); /* ✅ keep inside screen */
- overflow: auto; /* ✅ scroll INSIDE card only if needed */
- padding: 18px; /* smaller padding */
- }
-}
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/auth-page/auth-page.html b/frontend-neethika/src/app/auth/auth-page/auth-page.html
deleted file mode 100644
index a2beb27..0000000
--- a/frontend-neethika/src/app/auth/auth-page/auth-page.html
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
-
-
-
-
-
The System of Record for Decisions
-
-
- Join enterprise teams who replace chaotic Slack threads with structured,
- auditable decision-making.
-
-
-
- - ✓ Unified signals from your ecosystem
- - ✓ Evidence-backed problem clusters
- - ✓ Decisions and PRD outputs
-
-
-
-
-
Connect signals across your stack
-
- Bring Jira, Slack, email, support tools, and docs into a single decision workflow.
-
-
-
-
Jira
-
Slack
-
Gmail
-
Drive
-
Zendesk
-
-
-
-
-
-
-
-
-
-
Welcome back
-
Sign in to access your Decision Ledger
-
-
-
-
OR CONTINUE WITH EMAIL
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/auth-page/auth-page.spec.ts b/frontend-neethika/src/app/auth/auth-page/auth-page.spec.ts
deleted file mode 100644
index d99c47f..0000000
--- a/frontend-neethika/src/app/auth/auth-page/auth-page.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AuthPage } from './auth-page';
-
-describe('AuthPage', () => {
- let component: AuthPage;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [AuthPage]
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(AuthPage);
- component = fixture.componentInstance;
- await fixture.whenStable();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend-neethika/src/app/auth/auth-page/auth-page.ts b/frontend-neethika/src/app/auth/auth-page/auth-page.ts
deleted file mode 100644
index c309389..0000000
--- a/frontend-neethika/src/app/auth/auth-page/auth-page.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { LoginForm } from '../login-form/login-form';
-import { RegisterForm } from '../register-form/register-form';
-
-type Tab = 'login' | 'register';
-
-@Component({
- selector: 'app-auth-page',
- standalone: true,
- imports: [CommonModule, LoginForm, RegisterForm],
- templateUrl: './auth-page.html',
- styleUrl: './auth-page.css',
-})
-export class AuthPage {
- activeTab: Tab = 'login';
- isDarkMode = false;
- setTab(tab: Tab) {
- this.activeTab = tab;
-
- }
- toggleTheme() {
- this.isDarkMode = !this.isDarkMode;
-}}
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/login-form/login-form.css b/frontend-neethika/src/app/auth/login-form/login-form.css
deleted file mode 100644
index e69de29..0000000
diff --git a/frontend-neethika/src/app/auth/login-form/login-form.html b/frontend-neethika/src/app/auth/login-form/login-form.html
deleted file mode 100644
index d189308..0000000
--- a/frontend-neethika/src/app/auth/login-form/login-form.html
+++ /dev/null
@@ -1 +0,0 @@
-login-form works!
diff --git a/frontend-neethika/src/app/auth/login-form/login-form.spec.ts b/frontend-neethika/src/app/auth/login-form/login-form.spec.ts
deleted file mode 100644
index c6d792b..0000000
--- a/frontend-neethika/src/app/auth/login-form/login-form.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { LoginForm } from './login-form';
-
-describe('LoginForm', () => {
- let component: LoginForm;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [LoginForm]
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(LoginForm);
- component = fixture.componentInstance;
- await fixture.whenStable();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend-neethika/src/app/auth/login-form/login-form.ts b/frontend-neethika/src/app/auth/login-form/login-form.ts
deleted file mode 100644
index 7187f24..0000000
--- a/frontend-neethika/src/app/auth/login-form/login-form.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-login-form',
- imports: [],
- templateUrl: './login-form.html',
- styleUrl: './login-form.css',
-})
-export class LoginForm {
-
-}
diff --git a/frontend-neethika/src/app/auth/register-form/register-form.css b/frontend-neethika/src/app/auth/register-form/register-form.css
deleted file mode 100644
index de1bcdd..0000000
--- a/frontend-neethika/src/app/auth/register-form/register-form.css
+++ /dev/null
@@ -1,107 +0,0 @@
-/* ---------- Theme Variables ---------- */
-
-:host {
- --text: rgba(255,255,255,0.85);
- --muted: rgba(255,255,255,0.70);
- --input-bg: rgba(255,255,255,0.06);
- --input-border: rgba(255,255,255,0.14);
- --placeholder: rgba(255,255,255,0.40);
- --btn-bg: #ffffff;
- --btn-text: #111;
-}
-
-/* When AuthPage switches theme */
-:host-context(.auth-shell.dark) {
- --text: rgba(0,0,0,0.85);
- --muted: rgba(0,0,0,0.60);
- --input-bg: rgba(0,0,0,0.06);
- --input-border: rgba(0,0,0,0.14);
- --placeholder: rgba(0,0,0,0.40);
- --btn-bg: #111;
- --btn-text: #fff;
-}
-
-/* ---------- Layout ---------- */
-
-.form {
- display: grid;
- gap: 10px;
- margin-top: 14px;
-}
-
-.label {
- font-size: 12px;
- color: var(--muted);
- margin-top: 6px;
-}
-
-/* ---------- Inputs ---------- */
-
-.input {
- width: 100%;
- padding: 11px 12px;
- border-radius: 10px;
- background: var(--input-bg);
- border: 1px solid var(--input-border);
- color: var(--text);
- outline: none;
-}
-
-.input::placeholder {
- color: var(--placeholder);
-}
-
-.input:focus {
- border-color: rgba(120,120,120,.4);
-}
-
-/* ---------- Errors ---------- */
-
-.error {
- font-size: 12px;
- color: rgba(255, 120, 120, 0.95);
-}
-
-/* ---------- Checkbox ---------- */
-
-.checkline {
- display: flex;
- align-items: flex-start;
- gap: 10px;
- font-size: 12px;
- color: var(--muted);
- margin-top: 6px;
-}
-
-/* ---------- Primary Button ---------- */
-
-.primary {
- width: 100%;
- margin-top: 10px;
- padding: 12px 14px;
- border-radius: 10px;
- border: none;
- background: var(--btn-bg);
- color: var(--btn-text);
- font-weight: 800;
- cursor: pointer;
-}
-
-.primary:hover {
- opacity: 0.9;
-}
-
-/* ---------- Footer Text ---------- */
-
-.small {
- margin: 10px 0 0;
- font-size: 12px;
- color: var(--muted);
- text-align: center;
-}
-
-.link {
- color: var(--text);
- text-decoration: underline;
- cursor: pointer;
-}
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/register-form/register-form.html b/frontend-neethika/src/app/auth/register-form/register-form.html
deleted file mode 100644
index 24f9959..0000000
--- a/frontend-neethika/src/app/auth/register-form/register-form.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend-neethika/src/app/auth/register-form/register-form.spec.ts b/frontend-neethika/src/app/auth/register-form/register-form.spec.ts
deleted file mode 100644
index 3ad7a6c..0000000
--- a/frontend-neethika/src/app/auth/register-form/register-form.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { RegisterForm } from './register-form';
-
-describe('RegisterForm', () => {
- let component: RegisterForm;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [RegisterForm]
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(RegisterForm);
- component = fixture.componentInstance;
- await fixture.whenStable();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend-neethika/src/app/auth/register-form/register-form.ts b/frontend-neethika/src/app/auth/register-form/register-form.ts
deleted file mode 100644
index 7961ec2..0000000
--- a/frontend-neethika/src/app/auth/register-form/register-form.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Component,Output, EventEmitter } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
-
-@Component({
- selector: 'app-register-form',
- standalone: true,
- imports: [CommonModule, ReactiveFormsModule],
- templateUrl: './register-form.html',
- styleUrl: './register-form.css',
-})
-export class RegisterForm {
-
- form: any;
- submitted = false;
-@Output() goToLogin = new EventEmitter();
- constructor(private fb: FormBuilder) {
-
- this.form = this.fb.group({
- fullName: ['', [Validators.required, Validators.minLength(2)]],
- email: ['', [Validators.required, Validators.email]],
- password: ['', [Validators.required, Validators.minLength(8)]],
- confirmPassword: ['', [Validators.required]],
- agree: [false, [Validators.requiredTrue]],
- });
-
- }
- switchToLogin() {
- this.goToLogin.emit();
-}
-
- submit() {
- this.submitted = true;
-
- if (this.form.invalid) return;
-
- console.log(this.form.value);
- }
-
- get f() {
- return this.form.controls;
- }
-}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 25f90c5..a0e2829 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"@angular/cli": "^21.1.4",
"@angular/compiler-cli": "^21.1.0",
"@types/jasmine": "^6.0.0",
+ "@types/mocha": "^10.0.10",
"jasmine-core": "^6.0.1",
"karma": "^6.4.4",
"karma-chrome-launcher": "^3.2.0",
@@ -5816,6 +5817,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/mocha": {
+ "version": "10.0.10",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
+ "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "25.2.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
diff --git a/package.json b/package.json
index f43cbe8..52fc4ef 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"@angular/cli": "^21.1.4",
"@angular/compiler-cli": "^21.1.0",
"@types/jasmine": "^6.0.0",
+ "@types/mocha": "^10.0.10",
"jasmine-core": "^6.0.1",
"karma": "^6.4.4",
"karma-chrome-launcher": "^3.2.0",
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 21af735..5eadc2b 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import { Login } from './components/login/login';
import { Signup } from './components/signup/signup';
import { authGuard } from './guards/auth-guard';
+import { CreateWorkspacePlaceholder } from './components/workspace/create-workspace-placeholder';
export const routes: Routes = [
{ path: 'login', component: Login },
@@ -11,5 +12,10 @@ export const routes: Routes = [
loadComponent: () => import('./components/dashboard/dashboard').then(m => m.Dashboard),
canActivate: [authGuard]
},
+ {
+ path: 'workspace/create',
+ component: CreateWorkspacePlaceholder,
+ canActivate: [authGuard]
+ },
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }
];
diff --git a/src/app/components/dashboard/dashboard.css b/src/app/components/dashboard/dashboard.css
index e69de29..84888f0 100644
--- a/src/app/components/dashboard/dashboard.css
+++ b/src/app/components/dashboard/dashboard.css
@@ -0,0 +1,72 @@
+.dashboard-container {
+ padding: 20px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 40px;
+ border-bottom: 1px solid #eee;
+ padding-bottom: 20px;
+}
+
+.logout-btn {
+ background-color: #f44336;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.create-btn {
+ background-color: #4CAF50;
+ color: white;
+ text-decoration: none;
+ padding: 10px 20px;
+ border-radius: 4px;
+ font-weight: bold;
+}
+
+.workspace-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 20px;
+}
+
+.workspace-card {
+ border: 1px solid #ddd;
+ padding: 20px;
+ border-radius: 8px;
+ background-color: white;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ transition: transform 0.2s;
+}
+
+.workspace-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+}
+
+.workspace-card h3 {
+ margin-top: 0;
+ color: #333;
+}
+
+.empty-state {
+ text-align: center;
+ padding: 40px;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ color: #666;
+}
diff --git a/src/app/components/dashboard/dashboard.html b/src/app/components/dashboard/dashboard.html
index dbcd8b4..70e8929 100644
--- a/src/app/components/dashboard/dashboard.html
+++ b/src/app/components/dashboard/dashboard.html
@@ -1,5 +1,25 @@
-
-
Sentinent Dashboard
-
Welcome to your unified aggregator dashboard!
-
+
+
+ Sentinent Dashboard
+
+
+
+
+
+
+
+
+
{{ ws.name }}
+
{{ ws.description }}
+
Created: {{ ws.createdDate | date }}
+
+
+
+
+
No workspaces found. Create one to get started!
+
+
diff --git a/src/app/components/dashboard/dashboard.spec.ts b/src/app/components/dashboard/dashboard.spec.ts
new file mode 100644
index 0000000..e0ec9dc
--- /dev/null
+++ b/src/app/components/dashboard/dashboard.spec.ts
@@ -0,0 +1,55 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Dashboard } from './dashboard';
+import { WorkspaceService } from '../../services/workspace';
+import { AuthService } from '../../services/auth';
+import { of } from 'rxjs';
+import { RouterTestingModule } from '@angular/router/testing';
+import { Router } from '@angular/router';
+
+describe('Dashboard', () => {
+ let component: Dashboard;
+ let fixture: ComponentFixture
;
+ let mockWorkspaceService: any;
+ let mockAuthService: any;
+ let router: Router;
+
+ beforeEach(async () => {
+ mockWorkspaceService = {
+ getWorkspaces: jasmine.createSpy('getWorkspaces').and.returnValue(of([
+ { id: '1', name: 'Test Workspace', description: 'Test Desc', createdDate: new Date(), ownerId: 'u1' }
+ ]))
+ };
+
+ mockAuthService = {
+ logout: jasmine.createSpy('logout')
+ };
+
+ await TestBed.configureTestingModule({
+ imports: [Dashboard, RouterTestingModule],
+ providers: [
+ { provide: WorkspaceService, useValue: mockWorkspaceService },
+ { provide: AuthService, useValue: mockAuthService }
+ ]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(Dashboard);
+ component = fixture.componentInstance;
+ router = TestBed.inject(Router);
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should render workspaces', () => {
+ const compiled = fixture.nativeElement as HTMLElement;
+ expect(compiled.querySelector('.workspace-card h3')?.textContent).toContain('Test Workspace');
+ });
+
+ it('should call logout on button click', () => {
+ const button = fixture.nativeElement.querySelector('.logout-btn');
+ button.click();
+ expect(mockAuthService.logout).toHaveBeenCalled();
+ });
+});
diff --git a/src/app/components/dashboard/dashboard.ts b/src/app/components/dashboard/dashboard.ts
index fd0a1c0..453e81f 100644
--- a/src/app/components/dashboard/dashboard.ts
+++ b/src/app/components/dashboard/dashboard.ts
@@ -1,17 +1,30 @@
-import { Component, inject } from '@angular/core';
-import { Router } from '@angular/router';
+import { Component, inject, OnInit } from '@angular/core';
+import { Router, RouterLink } from '@angular/router';
import { AuthService } from '../../services/auth';
+import { WorkspaceService } from '../../services/workspace';
+import { Workspace } from '../../models/workspace';
+import { CommonModule } from '@angular/common';
@Component({
selector: 'app-dashboard',
standalone: true,
+ imports: [CommonModule, RouterLink],
templateUrl: './dashboard.html',
styleUrl: './dashboard.css',
})
-export class Dashboard {
+export class Dashboard implements OnInit {
private authService = inject(AuthService);
+ private workspaceService = inject(WorkspaceService);
private router = inject(Router);
+ workspaces: Workspace[] = [];
+
+ ngOnInit() {
+ this.workspaceService.getWorkspaces().subscribe(ws => {
+ this.workspaces = ws;
+ });
+ }
+
logout() {
this.authService.logout();
this.router.navigate(['/login']);
diff --git a/src/app/components/workspace/create-workspace-placeholder.ts b/src/app/components/workspace/create-workspace-placeholder.ts
new file mode 100644
index 0000000..e785b87
--- /dev/null
+++ b/src/app/components/workspace/create-workspace-placeholder.ts
@@ -0,0 +1,16 @@
+import { Component } from '@angular/core';
+import { RouterLink } from '@angular/router';
+
+@Component({
+ selector: 'app-create-workspace-placeholder',
+ standalone: true,
+ imports: [RouterLink],
+ template: `
+
+
Create Workspace
+
This feature is being implemented in Issue #4.
+
Back to Dashboard
+
+ `
+})
+export class CreateWorkspacePlaceholder { }
diff --git a/src/app/models/workspace.ts b/src/app/models/workspace.ts
new file mode 100644
index 0000000..59f1482
--- /dev/null
+++ b/src/app/models/workspace.ts
@@ -0,0 +1,7 @@
+export interface Workspace {
+ id: string;
+ name: string;
+ description: string;
+ createdDate: Date;
+ ownerId: string;
+}
diff --git a/src/app/services/workspace.ts b/src/app/services/workspace.ts
new file mode 100644
index 0000000..2065013
--- /dev/null
+++ b/src/app/services/workspace.ts
@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { Workspace } from '../models/workspace';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class WorkspaceService {
+ private mockWorkspaces: Workspace[] = [
+ {
+ id: '1',
+ name: 'Engineering',
+ description: 'Engineering team workspace',
+ createdDate: new Date(),
+ ownerId: 'user1'
+ },
+ {
+ id: '2',
+ name: 'Product',
+ description: 'Product team workspace',
+ createdDate: new Date(),
+ ownerId: 'user1'
+ },
+ {
+ id: '3',
+ name: 'Marketing',
+ description: 'Marketing team workspace',
+ createdDate: new Date(),
+ ownerId: 'user1'
+ }
+ ];
+
+ getWorkspaces(): Observable {
+ return of(this.mockWorkspaces);
+ }
+
+ createWorkspace(name: string): Observable {
+ const newWorkspace: Workspace = {
+ id: Math.random().toString(36).substring(7),
+ name,
+ description: '',
+ createdDate: new Date(),
+ ownerId: 'user1' // Mock owner
+ };
+ this.mockWorkspaces.push(newWorkspace);
+ return of(newWorkspace);
+ }
+}