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

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 @@ -
- - - -
- Please enter your name (min 2 characters). -
- - - -
- Please enter a valid email. -
- - - -
- Password must be at least 8 characters. -
- - - -
- Please confirm your password. -
- - - -
- You must accept the terms to continue. -
- - - -

- Already have an account? - Sign in -

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

+ +
+ +
+
+

Your Workspaces

+ Create New Workspace +
+ +
+
+

{{ 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); + } +}