Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 3 additions & 16 deletions ClientApp/src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,11 @@
}
}

// RTL & Font setup
// Application font
* {
font-family: Vazir, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
font-family: Roboto, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
}

// Material theme setup
.mat-typography {
font-family: Vazir, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
font-family: Roboto, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
}

// Material Components RTL setup
[dir='rtl'] {
.mat-form-field-appearance-outline {
.mat-form-field-outline-start {
border-radius: 0 4px 4px 0;
}
.mat-form-field-outline-end {
border-radius: 4px 0 0 4px;
}
}
}
37 changes: 37 additions & 0 deletions ClientApp/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
import { ApiBaseSettings } from './environmets/ApiSettings';
import { AuthenticationModel } from './environmets/AuthenticationModel';

describe('API configuration', () => {
it('uses a same-origin API URL', () => {
expect(new ApiBaseSettings().baseUrl).toBe('/api');
});
});

describe('AuthenticationModel', () => {
const authentication = new AuthenticationModel();

afterEach(() => localStorage.clear());

it('accepts a token that has not expired', () => {
authentication.Set(createToken(Math.floor(Date.now() / 1000) + 60), 'test-user');

expect(authentication.IsUserLogin()).toBeTrue();
expect(authentication.GetUserName()).toBe('test-user');
});

it('clears an expired token instead of keeping the user in a login loop', () => {
authentication.Set(createToken(Math.floor(Date.now() / 1000) - 60), 'test-user');

expect(authentication.IsUserLogin()).toBeFalse();
expect(authentication.GetToken()).toBeNull();
expect(authentication.GetUserName()).toBeNull();
});

it('clears malformed tokens', () => {
authentication.Set('not-a-jwt', 'test-user');

expect(authentication.IsUserLogin()).toBeFalse();
expect(authentication.GetToken()).toBeNull();
});
});

function createToken(exp: number): string {
const payload = btoa(JSON.stringify({ exp }))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
return `header.${payload}.signature`;
}
13 changes: 7 additions & 6 deletions ClientApp/src/app/authentication/login/login.component.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
<!-- filepath: /d:/Projects/Private/NoteBookKeeper/Client/NoteKeeper/src/app/authentication/login/login.component.html -->
<div class="login-container" dir="rtl">
<div class="login-container">
<mat-card class="login-card">
<mat-card-header>
<mat-card-title>ورود به سایت</mat-card-title> <mat-card-title>اکانت ندارید؟ <a href="/sign-up"> ثبت نام کنید</a></mat-card-title>
<mat-card-title>Sign in</mat-card-title>
<mat-card-subtitle>Don't have an account? <a href="/sign-up">Create one</a></mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<form class="login-form" (ngSubmit)="login()" style="margin-top: 2rem;">
<mat-form-field appearance="fill">
<mat-label>نام کاربری</mat-label>
<mat-label>Username</mat-label>
<input matInput [(ngModel)]="userName" name="username" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>رمز عبور</mat-label>
<mat-label>Password</mat-label>
<input matInput [(ngModel)]="password" name="password" type="password" required>
</mat-form-field>
<button mat-raised-button color="primary" type="submit">
ثبت نام
Sign in
</button>
</form>
</mat-card-content>
</mat-card>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
<!-- filepath: /d:/Projects/Private/NoteBookKeeper/Client/NoteKeeper/src/app/authentication/register/register.component.html -->
<div class="register-container" dir="rtl">
<div class="register-container">
<mat-card class="register-card">
<mat-card-header>
<mat-card-title>ثبت نام</mat-card-title>
<mat-card-title>Create an account</mat-card-title>
</mat-card-header>
<mat-card-content>
<form class="register-form" (ngSubmit)="register()">
<mat-form-field appearance="fill">
<mat-label>نام کاربری</mat-label>
<mat-label>Username</mat-label>
<input matInput type="text" [(ngModel)]="userName" name="username" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>ایمیل</mat-label>
<mat-label>Email</mat-label>
<input matInput type="email"[(ngModel)]="email" name="email" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>رمز عبور</mat-label>
<mat-label>Password</mat-label>
<input matInput type="password" [(ngModel)]="password" name="password" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>تکرار رمز عبور</mat-label>
<input matInput type="repassword" [(ngModel)]="repassword" name="repassword" required>
<mat-label>Confirm password</mat-label>
<input matInput type="password" [(ngModel)]="repassword" name="repassword" required>
</mat-form-field>
<button mat-raised-button color="primary" type="submit">
ثبت نام
Sign up
</button>
</form>
</mat-card-content>
</mat-card>
</div>
</div>
43 changes: 21 additions & 22 deletions ClientApp/src/app/components/add-note/add-note.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,58 @@
<div class="container">
<mat-card>
<mat-card-header>
<mat-card-title>افزودن جزوه جدید</mat-card-title>
<mat-card-title>Create a note</mat-card-title>
</mat-card-header>

<mat-card-content>
<form [formGroup]="noteForm" (ngSubmit)="onSubmit()" class="note-form">
<!-- Rich Text Editor -->
<div class="editor-container">
<label class="form-label">توضیحات یا جزوه</label>
<label class="form-label">Note content</label>
<mat-card class="editor-card">
<mat-card-content>
<div class="editor-toolbar">
<div class="toolbar-group">
<button mat-icon-button type="button" (click)="formatDoc('bold')" matTooltip="پررنگ">
<button mat-icon-button type="button" (click)="formatDoc('bold')" matTooltip="Bold">
<mat-icon>format_bold</mat-icon>
</button>
<button mat-icon-button type="button" (click)="formatDoc('italic')" matTooltip="مورب">
<button mat-icon-button type="button" (click)="formatDoc('italic')" matTooltip="Italic">
<mat-icon>format_italic</mat-icon>
</button>
<button mat-icon-button type="button" (click)="formatDoc('underline')" matTooltip="زیرخط">
<button mat-icon-button type="button" (click)="formatDoc('underline')" matTooltip="Underline">
<mat-icon>format_underline</mat-icon>
</button>
</div>

<div class="toolbar-divider"></div>

<div class="toolbar-group">
<button mat-icon-button type="button" (click)="formatDoc('justifyRight')" matTooltip="راست چین">
<button mat-icon-button type="button" (click)="formatDoc('justifyRight')" matTooltip="Align right">
<mat-icon>format_align_right</mat-icon>
</button>
<button mat-icon-button type="button" (click)="formatDoc('justifyCenter')" matTooltip="وسط چین">
<button mat-icon-button type="button" (click)="formatDoc('justifyCenter')" matTooltip="Align center">
<mat-icon>format_align_center</mat-icon>
</button>
<button mat-icon-button type="button" (click)="formatDoc('justifyLeft')" matTooltip="چپ چین">
<button mat-icon-button type="button" (click)="formatDoc('justifyLeft')" matTooltip="Align left">
<mat-icon>format_align_left</mat-icon>
</button>
</div>

<div class="toolbar-divider"></div>

<div class="toolbar-group">
<button mat-icon-button type="button" (click)="formatDoc('insertUnorderedList')" matTooltip="لیست">
<button mat-icon-button type="button" (click)="formatDoc('insertUnorderedList')" matTooltip="Bulleted list">
<mat-icon>format_list_bulleted</mat-icon>
</button>
<button mat-icon-button type="button" (click)="formatDoc('insertOrderedList')" matTooltip="لیست شماره‌دار">
<button mat-icon-button type="button" (click)="formatDoc('insertOrderedList')" matTooltip="Numbered list">
<mat-icon>format_list_numbered</mat-icon>
</button>
</div>

<div class="toolbar-divider"></div>

<div class="toolbar-group">
<button mat-icon-button type="button" (click)="formatDoc('createLink')" matTooltip="افزودن لینک">
<button mat-icon-button type="button" (click)="formatDoc('createLink')" matTooltip="Add link">
<mat-icon>link</mat-icon>
</button>
</div>
Expand All @@ -64,21 +64,20 @@
class="editor-content"
contenteditable="true"
(input)="onEditorInput($event)"
dir="rtl"
></div>
</mat-card-content>
</mat-card>
</div>

<!-- Redirect Link -->
<mat-form-field appearance="outline" class="full-width">
<mat-label>لینک مرتبط</mat-label>
<input matInput formControlName="redirectLink" placeholder="لینک مرتبط را وارد کنید">
<mat-label>Related link</mat-label>
<input matInput formControlName="redirectLink" placeholder="Enter a related link">
</mat-form-field>

<!-- Search Words -->
<mat-form-field appearance="outline" class="full-width">
<mat-label>کلمات کلیدی</mat-label>
<mat-label>Keywords</mat-label>
<mat-chip-grid #chipGrid>
<mat-chip-row
*ngFor="let word of searchWords"
Expand All @@ -90,15 +89,15 @@
</mat-chip-row>
</mat-chip-grid>
<input
placeholder="کلمه کلیدی جدید..."
placeholder="Add a keyword..."
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addSearchWord($event)">
</mat-form-field>

<!-- File Upload -->
<div class="file-upload">
<label class="form-label">فایل‌ها</label>
<label class="form-label">Files</label>
<div class="upload-zone" (click)="fileInput.click()">
<input
#fileInput
Expand All @@ -107,7 +106,7 @@
(change)="onFileSelected($event)"
style="display: none">
<mat-icon>cloud_upload</mat-icon>
<span>برای آپلود فایل کلیک کنید یا فایل را اینجا رها کنید</span>
<span>Click to upload files or drop them here</span>
</div>

<!-- File List -->
Expand All @@ -124,19 +123,19 @@
<!-- Submit Buttons -->
<div class="actions">
<button mat-stroked-button type="button" (click)="goBack()">
<mat-icon>arrow_forward</mat-icon>
برگشت
<mat-icon>arrow_back</mat-icon>
Back
</button>
<button
mat-flat-button
color="primary"
type="submit"
[disabled]="!noteForm.valid || loading">
<mat-icon>save</mat-icon>
{{ loading ? 'در حال ثبت...' : 'ثبت جزوه' }}
{{ loading ? 'Saving...' : 'Save note' }}
</button>
</div>
</form>
</mat-card-content>
</mat-card>
</div>
</div>
13 changes: 5 additions & 8 deletions ClientApp/src/app/components/add-note/add-note.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class AddNoteComponent implements OnInit {

formatDoc(command: string) {
if (command === 'createLink') {
const url = prompt('لطفاً URL را وارد کنید:');
const url = prompt('Enter a URL:');
if (url) {
document.execCommand(command, false, url);
}
Expand Down Expand Up @@ -125,7 +125,7 @@ export class AddNoteComponent implements OnInit {
if (this.noteForm.valid) {
if(this.searchWords.length == 0)
{
this.showErrorMessage('کلمات کلیدی نمیتواند خالی باشد');
this.showErrorMessage('Add at least one keyword.');
return;
}
this.loading = true;
Expand All @@ -142,7 +142,6 @@ export class AddNoteComponent implements OnInit {
formData.append('files', file);
});

// contentType parameter رو حذف کنید
this.apiService.post(this.baseUrls.createNoteSettingItem, formData)
.subscribe({
next: () => {
Expand All @@ -159,20 +158,18 @@ export class AddNoteComponent implements OnInit {
}

private showSuccessMessage(): void {
this.snackBar.open('نوت با موفقیت ذخیره شد', 'باشه', {
this.snackBar.open('Note saved successfully.', 'OK', {
duration: 3000,
horizontalPosition: 'start',
direction: 'rtl',
panelClass: 'success-snackbar'
});
this.router.navigate(['note-list'])
}

private showErrorMessage(errMsg:string='خطا در ذخیره نوت'): void {
this.snackBar.open(errMsg, 'تلاش مجدد', {
private showErrorMessage(errMsg:string='Could not save the note.'): void {
this.snackBar.open(errMsg, 'Try again', {
duration: 5000,
horizontalPosition: 'start',
direction: 'rtl',
panelClass: 'error-snackbar'
});
}
Expand Down
17 changes: 8 additions & 9 deletions ClientApp/src/app/components/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,23 @@
<div class="content-container" [@fadeIn]>
<!-- Header Section -->
<div class="header-section">
<h1 class="main-title">نوت هاتو بزار جا ما</h1>
<p class="subtitle">با قابلیت جست و جو توی جزوت میتونی</p>
<p class="extra-text">دنیارو بدست بیاری</p>
<h1 class="main-title">Keep every idea within reach</h1>
<p class="subtitle">Organize your notes and find anything in seconds.</p>
<p class="extra-text">Your knowledge, always ready when you need it.</p>
</div>

<!-- Welcome Section -->
<div class="welcome-section">
<h2 class="welcome-title">به نوت کیپر خوش اومدی</h2>
<h2 class="welcome-title">Welcome to NoteKeeper</h2>
<p class="welcome-description">
در اینجا میتونی نوت هاتو نگه داری و توشون جست و جو کنی، فقط کافیه
از منو نوت جدید رو انتخاب کنی
Create categories, save detailed notes, attach files, and search your content whenever you need it.
</p>

<!-- Action Button -->
<button class="new-note-btn" (click)="navigateToNewNote()">
<span class="btn-text">شروع نوت جدید</span>
<span class="btn-icon">←</span>
<span class="btn-text">Create a category</span>
<span class="btn-icon">→</span>
</button>
</div>
</div>
</div>
</div>
Loading
Loading