Skip to content
Open
6 changes: 3 additions & 3 deletions .claude/rules/frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ uniplanWeb/ # All Angular code lives here, not at repo ro
│ ├── app.config.ts # providers (router, http)
│ ├── app.routes.ts # currently a single "" → LayoutComponent route
│ ├── core/
│ │ ├── interfaces/ # <Entity>Elm types in <entity>-elm.ts
│ │ ├── interfaces/ # <Entity> types in <entity>.ts
│ │ └── shared/ # cross-feature UI shells (add-form, edit-form,
│ │ # delete-form, filters-form, input-filter,
│ │ # add-button, main-panel, navmenu-component)
Expand All @@ -54,7 +54,7 @@ All `npm` / `ng` commands run from `uniplanWeb/`, not the repo root.
- Class names are **PascalCase without the `Component` / `Service` suffix**:
`FacultyAddForm` (not `FacultyAddFormComponent`), `MajorService` (not `MajorServiceService`).
- Feature service files are named `<feature>-service.ts` (single hyphen), not `<feature>.service.ts` — this is intentional and predates Angular's default. **Do not "fix" it.**
- Domain interface files end in `-elm.ts` and export `<Entity>Elm` (e.g. `major-elm.ts` → `MajorElm`). One existing exception is `UniversityElm` co-located in `university-service.ts`.
- Domain interface files are named `<entity>.ts` and export `<Entity>` (e.g. `student-profile.ts` → `StudentProfile`, `lector-profile.ts` → `LectorProfile`). One existing exception is `UniversityElm` co-located in `university-service.ts`.
- Test files live alongside source as `*.spec.ts`.

## Component Conventions
Expand All @@ -70,7 +70,7 @@ All `npm` / `ng` commands run from `uniplanWeb/`, not the repo root.
- **`core/shared/`** — cross-feature primitives (form skeletons, the layout panel, the navmenu, generic table). No feature-specific logic here.
- **`features/<feature>/`** — every feature owns its options bar, table, dialog forms, filters bar, and service in a flat directory.
- **No barrel `index.ts` files.** Import directly from the source path.
- **No re-export shims** between features — if feature A needs a type from feature B's interface file, import from `core/interfaces/<entity>-elm.ts` directly.
- **No re-export shims** between features — if feature A needs a type from feature B's interface file, import from `core/interfaces/<entity>.ts` directly.

## View Switching (project-specific)

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Each feature service (e.g. [faculty-service.ts](uniplanWeb/src/app/features/facu

### Folder layout

- `src/app/core/interfaces/` — domain types named `<entity>-elm.ts` exporting `<Entity>Elm` (e.g. `MajorElm`). One exception: `UniversityElm` is co-located inside [university-service.ts](uniplanWeb/src/app/features/university/university-service.ts).
- `src/app/core/interfaces/` — domain types named `<entity>.ts` exporting `<Entity>` (e.g. `student-profile.ts` → `StudentProfile`, `lector-profile.ts` → `LectorProfile`). One exception: `UniversityElm` is co-located inside [university-service.ts](uniplanWeb/src/app/features/university/university-service.ts).
- `src/app/core/shared/` — reusable UI shells: `add-button`, `add-form`, `edit-form`, `delete-form`, `filters-form`, `input-filter`, `main-panel` (with its `table` child), `navmenu-component`. Feature-specific forms wrap these via `imports` and an `@Output() saveClicked` event.
- `src/app/features/{faculty,major,student,university}/` — each feature owns its `*-service.ts` plus `*-options`, `*-table`, `*-add-form`, `*-edit-form`, `*-delete-form`, `*-filters` components. Note the `*-service.ts` naming (with hyphen) is non-default Angular style — keep it consistent if you add a new service in this layer.
- `src/app/services/` — cross-cutting services. Currently just [login-auth-service.ts](uniplanWeb/src/app/services/login-auth-service.ts), which is a `localStorage`-backed stub (no real auth).
Expand Down
12 changes: 8 additions & 4 deletions uniplanWeb/public/i18n/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@
}
},
"settings": {
"title": "Заглавие",
"span": "Смени езика",
"title": "Настройки",
"profile-title": "Моят профил",
"language-title": "Език",
"loading": "Зареждане…",
"load-error": "Неуспешно зареждане на профила",
"bulgarian": "български",
"english": "английски"
"english": "английски",
"email-label": "Имейл"
},
"shared": {
"add-button": "Добави",
Expand Down Expand Up @@ -93,4 +97,4 @@
"logout": "Изход",
"login": "Вход"
}
}
}
10 changes: 7 additions & 3 deletions uniplanWeb/public/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@
}
},
"settings": {
"title": "Title",
"span": "Change language",
"title": "Settings",
"profile-title": "My profile",
"language-title": "Language",
"loading": "Loading…",
"load-error": "Failed to load your profile",
"bulgarian": "Bulgarian",
"english": "English"
"english": "English",
"email-label": "Email"
},
"shared": {
"add-button": "Add",
Expand Down
5 changes: 3 additions & 2 deletions uniplanWeb/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient, withXhr } from '@angular/common/http';
import { DEFAULT_LANGUAGE, LANGUAGE_STORAGE_KEY } from './config/language';

export const appConfig: ApplicationConfig = {
providers: [
Expand All @@ -18,8 +19,8 @@ export const appConfig: ApplicationConfig = {
provideRouter(routes),
provideHttpClient(withXhr()),
provideTranslateService({
lang: 'bg',
fallbackLang: 'bg',
lang: localStorage.getItem(LANGUAGE_STORAGE_KEY) ?? DEFAULT_LANGUAGE,
fallbackLang: DEFAULT_LANGUAGE,
loader: provideTranslateHttpLoader({
prefix: '/i18n/',
suffix: '.json'
Expand Down
5 changes: 5 additions & 0 deletions uniplanWeb/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export const routes: Routes = [
loadComponent: () =>
import('./features/room/room-panel/room-panel').then(m => m.RoomPanel),
},
{
path: 'settings',
loadComponent: () =>
import('./features/settings/settings-panel/settings-panel').then(m => m.SettingsPanel),
},
{
path: '**',
loadComponent: () =>
Expand Down
2 changes: 2 additions & 0 deletions uniplanWeb/src/app/config/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ export const API_ENDPOINTS = {
courses: `${environment.baseUrl}/courses`,
faculties: `${environment.baseUrl}/faculties`,
majors: `${environment.baseUrl}/majors`,
students: `${environment.baseUrl}/students`,
lectors: `${environment.baseUrl}/lectors`,
rooms: `${environment.baseUrl}/rooms`,
};
2 changes: 2 additions & 0 deletions uniplanWeb/src/app/config/language.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const LANGUAGE_STORAGE_KEY = 'lang';
export const DEFAULT_LANGUAGE = 'bg';
7 changes: 7 additions & 0 deletions uniplanWeb/src/app/core/interfaces/lector-profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface LectorProfile {
id: string;
facultyId: string;
email: string;
firstName: string;
lastName: string;
}
10 changes: 10 additions & 0 deletions uniplanWeb/src/app/core/interfaces/student-profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface StudentProfile {
id: string;
name: string;
facultyNumber: string;
majorId: string;
majorName: string;
courseType: string;
courseSubtype: string;
courseYear: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ <h2>Logo</h2>

<ul class="list">
<li class="list">
<a href="#" class="nav-link">
<a [routerLink]="'/settings'" routerLinkActive="active" class="nav-link">
<i class="bx bx-cog icon"></i><span class="link">{{ 'navmenu.settings' | translate }}</span>
</a>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ export class NavmenuComponent implements OnInit {
}
if (!this.isMobileView) {
this.isSidebarCollapsed = false;


}
}

toggleSidebar(): void {
protected toggleSidebar(): void {
this.isSidebarCollapsed = !this.isSidebarCollapsed;
}
}
6 changes: 0 additions & 6 deletions uniplanWeb/src/app/core/shared/settings/settings.html

This file was deleted.

Empty file.
26 changes: 0 additions & 26 deletions uniplanWeb/src/app/core/shared/settings/settings.spec.ts

This file was deleted.

18 changes: 0 additions & 18 deletions uniplanWeb/src/app/core/shared/settings/settings.ts

This file was deleted.

6 changes: 3 additions & 3 deletions uniplanWeb/src/app/features/room/room-panel/room-panel.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<app-room-options></app-room-options>
<app-room-table></app-room-table>
</div>
<app-room-options></app-room-options>
<app-room-table></app-room-table>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<div class="component-panel settings">
<h1>{{ 'settings.title' | translate }}</h1>

<div class="settings-grid">
<section class="settings-card profile-card">
<h2>{{ 'settings.profile-title' | translate }}</h2>

<div aria-live="polite">
@if (loading) {
<p>{{ 'settings.loading' | translate }}</p>
} @else if (loadError) {
<p>{{ 'settings.load-error' | translate }}</p>
} @else if (currentRole === 'student' && studentProfile) {
<dl class="profile-info">
<dt>{{ 'features.name-label' | translate }}</dt>
<dd>{{ studentProfile.name }}</dd>

<dt>{{ 'student.faculty-number-label' | translate }}</dt>
<dd>{{ studentProfile.facultyNumber }}</dd>

<dt>{{ 'features.major-label' | translate }}</dt>
<dd>{{ studentProfile.majorName }}</dd>

<dt>{{ 'student.qualification-level-label' | translate }}</dt>
<dd>{{ studentProfile.courseType }}</dd>

<dt>{{ 'features.study-mode-label' | translate }}</dt>
<dd>{{ studentProfile.courseSubtype }}</dd>

<dt>{{ 'student.course-label' | translate }}</dt>
<dd>{{ studentProfile.courseYear }}</dd>
</dl>
} @else if (currentRole === 'lector' && lectorProfile) {
<dl class="profile-info">
<dt>{{ 'features.name-label' | translate }}</dt>
<dd>{{ lectorProfile.firstName }} {{ lectorProfile.lastName }}</dd>

<dt>{{ 'settings.email-label' | translate }}</dt>
<dd>{{ lectorProfile.email }}</dd>

<dt>{{ 'features.faculty-label' | translate }}</dt>
<dd>{{ lectorProfile.facultyId }}</dd>
</dl>
}
</div>
</section>

<section class="settings-card language-card">
<h2>{{ 'settings.language-title' | translate }}</h2>
<mat-button-toggle-group
[value]="activeLanguage"
(change)="useLanguage($event.value)"
[attr.aria-label]="'settings.language-title' | translate"
>
<mat-button-toggle value="bg">{{ 'settings.bulgarian' | translate }}</mat-button-toggle>
<mat-button-toggle value="en">{{ 'settings.english' | translate }}</mat-button-toggle>
</mat-button-toggle-group>
</section>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@use '../../../../styles/panel' as *;
@include panel-styles();

.settings-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, max-content);
gap: 1.5rem;
margin-top: 1.5rem;
}

.settings-card {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 8px;
padding: 1.5rem;
background: #fff;
min-width: 0;
min-height: 14rem;

h2 {
margin-bottom: 1rem;
}
}

.profile-info {
display: grid;
grid-template-columns: minmax(min-content, max-content) minmax(0, 1fr);
gap: 0.5rem 1rem;

dt {
font-weight: 600;
overflow-wrap: break-word;
}

dd {
margin: 0;
overflow-wrap: break-word;
}
}

.language-card {
mat-button-toggle-group {
flex-wrap: wrap;
max-width: 100%;
}
}
Loading