🏗️

ספרינט 1

הרשמה + כניסה — מקצה לקצה
אנו בניך — פלטפורמת קהילה לשכולות | פרקטיקום סניף
📅 שבועות 1–2 👩‍💻 2 ג'וניוריות 🎯 14 משימות 🏷️ FAB-32 עד FAB-45 מטרה: Auth עובד מקומית
🎯 מטרת הספרינט בסוף השבועיים: משתמשת נרשמת, מקבלת OTP במייל, מאמתת, ונכנסת לאפליקציה. Guard חוסם דפים מוגנים. Shared components מוכנים לשאר הספרינטים.

📋 תוכן עניינים

  1. לפני ואחרי — מה משנים
  2. מפת תלויות — סדר עבודה
  3. ציר זמן — 2 שבועות
  4. חוקי Angular — לקרוא פעם אחת
  5. FAB-32 🔴 Alembic Init (BLOCKER)
  6. FAB-33 🔴 auth_service: register/OTP
  7. FAB-34 auth_service: login/refresh
  8. FAB-35 Wire Auth Endpoints
  9. FAB-36 Email OTP
  10. FAB-37 🔴 Shared UI Components (BLOCKER)
  11. FAB-38 Confirm Dialog
  12. FAB-39 File Upload Component
  13. FAB-40 🔴 auth.service.ts (BLOCKER)
  14. FAB-41 Guard Fix + Interceptor
  15. FAB-42 Login Component
  16. FAB-43 Register Steps 1–3
  17. FAB-44 Home + Header
  18. FAB-45 ⬛ Register Step 4 (BACKLOG)
  19. Definition of Done
1

לפני ואחרי — מה משנים

הסקאפולד מלא — אל תכתבו קוד חדש מאפס. כל קובץ כבר קיים. המשימה שלכן היא למלא את ה-stubs שמחכים בפנים.

Backend

קובץעכשיואחרי Sprint 1משימה
alembic.ini ❌ לא קיים קיים + migrations/ FAB-32
services/auth_service.py raise NotImplementedError 5 פונקציות עובדות FAB-33, FAB-34
api/v1/endpoints/auth.py 501 Not Implemented מחוברים ל-service FAB-35
services/email_service.py ⚠️ logger stub שולח מייל אמיתי (Mailtrap) FAB-36
api/v1/endpoints/users.py ✅ GET /users/me עובד ללא שינוי
api/v1/endpoints/health.py ✅ עובד ללא שינוי

Frontend

קובץעכשיואחרי Sprint 1משימה
shared/components/error-display/ ❌ לא קיים קומפוננטה עובדת FAB-37
shared/components/loading-spinner/ ⚠️ חסר message input size + message FAB-37
shared/components/confirm-dialog/ ❌ לא קיים קומפוננטה עובדת FAB-38
shared/components/file-upload/ ❌ לא קיים קומפוננטה עובדת FAB-39
core/services/auth.service.ts 5 stubs ריקים 5 פונקציות HTTP אמיתיות FAB-40
core/guards/auth.guard.ts 🐛 redirect → /home redirect → /login ✅ FAB-41
core/interceptors/auth.interceptor.ts ⚠️ JWT בלבד, אין 401 + auto-refresh על 401 FAB-41
features/auth/login/ ⚠️ shell ריק טופס עובד מחובר FAB-42
features/auth/register/ ⚠️ שלבים ללא forms 3 שלבים + OTP עובדים FAB-43
features/home/ + layout/header/ ⚠️ HTML ריק ניווט לפי תפקיד + header FAB-44
2

מפת תלויות — סדר עבודה

🚨 לפני שמתחילות: FAB-32 ו-FAB-37 חייבות להיות מוכנות לפני הכל. ללא FAB-32 — אין טבלאות ב-DB, כל endpoint קורס. ללא FAB-37 — אין shared components, כל UI תקוע.
🔴 FAB-32Alembic Init — BLOCKER
FAB-33register / OTP
FAB-34login / refresh
FAB-36Email OTP
FAB-35Wire Endpoints
🔴 FAB-37Shared UI — BLOCKER
FAB-38Confirm Dialog
FAB-39File Upload
🔴 FAB-40auth.service.ts
FAB-41Guard + Interceptor
FAB-42Login Component
FAB-43Register 1–3
FAB-44Home + Header
FAB-45 — BACKLOGRegister Step 4

טבלת בחירה — Self-Select

משימהJiraגודלתלויה בהערה
🔴 Alembic InitFAB-32 S BLOCKER — יום 1
auth_service: register/OTPFAB-33 M FAB-32
auth_service: login/refreshFAB-34 S FAB-32
Wire Auth EndpointsFAB-35 S FAB-33, FAB-34
Email OTPFAB-36 S FAB-32Mailtrap בחינם
🔴 Shared UI ComponentsFAB-37 S BLOCKER — יום 1
Confirm DialogFAB-38 S FAB-37
File UploadFAB-39 M FAB-37
🔴 auth.service.tsFAB-40 S FAB-37 BLOCKER frontend
Guard Fix + InterceptorFAB-41 S FAB-40bug חד-שורתי + 401 refresh
Login ComponentFAB-42 M FAB-40
Register Steps 1–3FAB-43 L FAB-40, FAB-39הכי גדול בספרינט
Home + HeaderFAB-44 S FAB-40
Register Step 4 (הצהרות)FAB-45 S FAB-43 BACKLOG — אם יש זמן
3

ציר זמן — 2 שבועות

0

יום 0 — לפני שמתחילות

וודאו שהשרת עולה: uvicorn app.main:app --reloadGET /api/v1/health ✅. Frontend: ng serve → localhost:4200 נפתח. קראו יחד את הטבלה "לפני ואחרי".

1–2

ימים 1–2 — BLOCKERS במקביל

מפתחת 1 (Backend): FAB-32 — Alembic init + migration ראשונה + seed. בסוף: alembic upgrade head עובד, טבלאות קיימות.

מפתחת 2 (Frontend): FAB-37 — error-display + loading-spinner message input. בסוף: שתי הקומפוננטות עובדות standalone עם Signals.

3–4

ימים 3–4 — לוגיקה עיקרית

Backend: FAB-33 (register/OTP/resend) + FAB-34 (login/refresh). בדיקה ב-Swagger: register → verify-otp → login עובד.

Frontend: FAB-38 (confirm-dialog) + FAB-39 (file-upload) + FAB-40 (auth.service 5 methods). FAB-40 קטנה מאוד — רק 5 פונקציות HTTP.

5–6

ימים 5–6 — חיבורים

Backend: FAB-35 (wire endpoints) + FAB-36 (email Mailtrap). בסוף: flow שלם ב-Swagger.

Frontend: FAB-41 (guard bug + 401 interceptor) + FAB-42 (login component). בסוף: login עובד ומנתב ל-/home.

7–9

ימים 7–9 — UI

שתיהן: FAB-43 (register steps 1–3) — הכי גדול. שלב 2 של ההרשמה קורא register() שמפעיל FAB-33. הגיע הזמן לחבר frontend + backend יחד.

במקביל: FAB-44 (home + header) — קטן, ניתן לעשות בסוף יום.

10

יום 10 — בדיקות + PR reviews

End-to-end: נרשמות כמשתמשת חדשה, מקבלות OTP, מאמתות, נכנסות. FAB-45 אם יש זמן. כל ה-PRs עוברים review של מפתחת שנייה לפני merge.

4

חוקי Angular 22 — לקרוא פעם אחת לפני שמתחילות

🚨 Angular 22 != Angular מהקורס. אם לא תקראו את זה — תבזבזו שעות על שגיאות שנובעות מדפוסים ישנים.
✅ standalone: true — תמיד
@Component({
  standalone: true,  // ← חובה
  imports: [...],
  ...
})
✅ Signals — לא @Input()/@Output()
// ✅ נכון:
size = input<'sm'|'md'>('md');
clicked = output<void>();

// ❌ לא נכון (Angular ישן):
@Input() size: string = 'md';
@Output() clicked = new EventEmitter();
✅ @if / @for — לא *ngIf / *ngFor
// ✅ נכון:
@if (isLoading()) { <app-spinner /> }
@for (item of items(); track item.id) { ... }

// ❌ לא נכון:
<div *ngIf="isLoading">
<li *ngFor="let item of items">
✅ inject() בתוך הפונקציה — בinterceptor
// ✅ נכון (אין circular dependency):
export const authInterceptor: HttpInterceptorFn =
  (req, next) => {
    const auth = inject(AuthService); // ← בתוך
    ...
  };

// ❌ לא נכון:
const auth = inject(AuthService); // ← בחוץ
FAB-32
🔴 Alembic Init + Migration ראשונה
S · ~3–4 שעות · BLOCKER — יום 1
🚨 ללא FAB-32 אין טבלאות ב-DB — כל endpoint קורס עם 500. זו המשימה הראשונה שצריך לסיים.

❌ עכשיו

  • alembic.ini לא קיים
  • migrations/ לא קיים
  • אין טבלאות ב-DB
  • כל קריאת DB מחזירה 500

✅ אחרי

  • alembic upgrade head רץ ללא שגיאות
  • כל הטבלאות קיימות ב-DB
  • GET /api/v1/health → {"status":"ok"}

שלבי ביצוע

cd backend

# שלב 1: אתחול Alembic
alembic init migrations

# שלב 2: עדכון migrations/env.py
# מצאו את השורה: target_metadata = None
# החליפו ב:
from app.db.base import Base
target_metadata = Base.metadata

# שלב 3: עדכון alembic.ini
# מצאו: sqlalchemy.url = driver://user:pass@localhost/dbname
# החליפו ב:
sqlalchemy.url = sqlite:///./app.db

# שלב 4: יצירת migration
alembic revision --autogenerate -m "initial"

# שלב 5: הרצה
alembic upgrade head

✅ Definition of Done

  • alembic upgrade head רץ ללא שגיאות
  • קובץ migration קיים ב-migrations/versions/
  • GET /api/v1/health מחזיר 200
  • PR עבר review של מפתחת שנייה + מוזג
FAB-33
🔴 auth_service: register / verify_otp / resend_otp
M · ~1–2 ימים · תלוי ב-FAB-32
💡 _generate_otp() ו-_create_token() כבר קיימות בקובץ — השתמשו בהן. get_password_hash() קיים ב-core/security.py.

קובץ לעריכה: backend/app/services/auth_service.py

מחפשות raise NotImplementedError ומחליפות ב-לוגיקה האמיתית.

register(db, data) — יצירת משתמש חדש

def register(db: Session, data: RegisterRequest) -> dict:
    # 1. בדוק שהמייל לא קיים
    if db.query(User).filter(User.email == data.email).first():
        raise HTTPException(400, "Email already registered")

    # 2. צור OTP
    otp = _generate_otp()
    otp_expires = datetime.utcnow() + timedelta(minutes=settings.OTP_EXPIRE_MINUTES)

    # 3. שמור משתמש
    user = User(
        email=data.email,
        password_hash=get_password_hash(data.password),
        display_name=data.display_name,
        otp_code=otp,
        otp_expires_at=otp_expires,
        account_status=AccountStatus.PENDING_OTP,
    )
    db.add(user)
    db.commit()

    # 4. שלח OTP במייל
    email_service.send_otp(user.email, otp)

    return {"message": "OTP sent", "email": user.email}

verify_otp(db, data) — אימות OTP

def verify_otp(db: Session, data: OtpVerifyRequest) -> dict:
    user = db.query(User).filter(User.email == data.email).first()
    if not user:
        raise HTTPException(404, "User not found")
    if user.otp_code != data.otp_code:
        raise HTTPException(400, "Invalid OTP")
    if user.otp_expires_at < datetime.utcnow():
        raise HTTPException(400, "OTP expired")

    user.account_status = AccountStatus.PENDING_APPROVAL
    user.otp_code = None
    db.commit()
    return {"message": "Email verified"}

✅ Definition of Done (בדיקה ב-Swagger: localhost:8000/docs)

  • POST /api/v1/auth/register — משתמש נוצר ב-DB עם otp_code
  • POST /api/v1/auth/verify-otp — סטטוס משתנה ל-PENDING_APPROVAL
  • POST /api/v1/auth/resend-otp — OTP חדש נוצר
  • מייל שגוי → 400, OTP פג → 400, הכל ברור
FAB-34
auth_service: login / refresh_token
S · ~3–5 שעות · תלוי ב-FAB-32
💡 verify_password() ו-decode_token() קיימים ב-core/security.py. _create_token() קיים ב-auth_service — access 15 דק, refresh 7 ימים.

login(db, data) — כניסה + הנפקת tokens

def login(db: Session, data: LoginRequest) -> TokenResponse:
    user = db.query(User).filter(User.email == data.email).first()
    if not user or not verify_password(data.password, user.password_hash):
        raise HTTPException(401, "Invalid credentials")
    if user.account_status == AccountStatus.PENDING_OTP:
        raise HTTPException(403, "Please verify your email first")

    access  = _create_token(str(user.id), "access",  minutes=15)
    refresh = _create_token(str(user.id), "refresh", days=7)
    return TokenResponse(access_token=access, refresh_token=refresh)

✅ Definition of Done

  • POST /api/v1/auth/login → מחזיר access_token + refresh_token
  • סיסמה שגויה → 401
  • משתמש שלא אימת OTP → 403 עם הסבר
  • POST /api/v1/auth/refresh → access_token חדש
FAB-35
Wire Auth Endpoints
S · ~2–3 שעות · תלוי ב-FAB-33 + FAB-34

קובץ לעריכה: backend/app/api/v1/endpoints/auth.py

כל endpoint מחזיר כרגע raise HTTPException(status_code=501). מחברות ל-service:

@router.post("/register")
def register(data: RegisterRequest, db: Session = Depends(get_db)):
    return auth_service.register(db=db, data=data)

@router.post("/verify-otp")
def verify_otp(data: OtpVerifyRequest, db: Session = Depends(get_db)):
    return auth_service.verify_otp(db=db, data=data)

@router.post("/login", response_model=TokenResponse)
def login(data: LoginRequest, db: Session = Depends(get_db)):
    return auth_service.login(db=db, data=data)

# + resend-otp, refresh

✅ Definition of Done

  • זרימה שלמה ב-Swagger: register → verify-otp → login → GET /users/me
  • אף endpoint לא מחזיר 501
FAB-36
Email Service — OTP אמיתי
S · ~2–4 שעות · תלוי ב-FAB-32
💡 ב-dev: אם SMTP_HOST ריק → OTP מודפס ל-console. זה בכוונה! לבדיקה אמיתית — הירשמו ל-Mailtrap (חינם).

הגדרת Mailtrap

# backend/.env
SMTP_HOST=sandbox.smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USER=<מ-Mailtrap>
SMTP_PASSWORD=<מ-Mailtrap>
SMTP_FROM=noreply@anu-banecha.local

קובץ לעריכה: backend/app/services/email_service.py

def send_otp(to_email: str, otp: str) -> None:
    if not settings.SMTP_HOST:
        # dev fallback — intentional
        logger.info(f"[DEV EMAIL] OTP for {to_email}: {otp}")
        return

    msg = MIMEMultipart()
    msg["From"]    = settings.SMTP_FROM
    msg["To"]      = to_email
    msg["Subject"] = "קוד האימות שלך — אנו בניך"
    msg.attach(MIMEText(f"קוד האימות שלך: {otp}\nתוקף: 10 דקות.", "plain"))

    with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as server:
        server.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
        server.sendmail(settings.SMTP_FROM, to_email, msg.as_string())

✅ Definition of Done

  • OTP מגיע ל-inbox ב-Mailtrap (לא רק ב-console)
  • ללא SMTP_HOST — ממשיך לעבוד (מדפיס לconsole)
FAB-37
🔴 Shared UI: error-display + loading-spinner
S · ~3–4 שעות · BLOCKER — יום 1
🚨 כל קומפוננטת UI בספרינט הזה תשתמש באלה. בנו אותן לפני שנוגעות בכל דף אחר.

❌ עכשיו

  • error-display לא קיים
  • loading-spinner קיים אבל חסר message input

✅ אחרי

  • <app-error-display message="שגיאה!" /> עובד
  • <app-loading-spinner message="טוען..." /> עובד

1. error-display (חדש לגמרי)

// frontend/src/app/shared/components/error-display/error-display.component.ts
@Component({
  standalone: true,
  selector: 'app-error-display',
  template: `
    @if (message()) {
      <div class="error-box">⚠️ {{ message() }}</div>
    }
  `,
  styles: [`
    .error-box {
      background: #fff5f5; border: 1px solid #fc8181;
      color: #c53030; border-radius: 8px;
      padding: 12px 16px; font-size: 14px;
    }
  `]
})
export class ErrorDisplayComponent {
  message = input<string>('');
}

2. loading-spinner (עריכה — הוספת message)

// frontend/src/app/shared/components/loading-spinner/loading-spinner.component.ts
// הוסיפו ל-class הקיים — אל תמחקו את size:
message = input<string>('');
/* בtemplate — הוסיפו מתחת לאייקון הספינר: */
@if (message()) {
  <p class="spinner-message">{{ message() }}</p>
}

✅ Definition of Done

  • error-display קיים, standalone, מקבל message signal input
  • loading-spinner מקבל גם size וגם message
  • אין @Input() בשום מקום — רק input()
  • אין *ngIf — רק @if
FAB-38
Confirm Dialog Component
S · ~2–3 שעות · תלוי ב-FAB-37
ישמש בכל ספרינט עתידי: מחיקת פוסט, השעיה, ביטול חשבון. בנו פעם אחת ונכון.

API שנדרש

title        = input<string>('האם אתה בטוחה?');
message      = input<string>('');
confirmLabel = input<string>('אישור');
cancelLabel  = input<string>('ביטול');
confirmed    = output<void>();
cancelled    = output<void>();

✅ Definition of Done

  • מוצג מעל שאר התוכן (overlay כהה)
  • "אישור" → event confirmed
  • "ביטול" / לחיצה מחוץ → event cancelled
  • standalone, signals, @if
FAB-39
File Upload Component
M · ~1 יום · תלוי ב-FAB-37

API שנדרש

acceptedTypes = input<string>('image/*,.pdf');
maxSizeMB     = input<number>(5);
label         = input<string>('בחרי קובץ');
fileSelected  = output<File>();
uploadError   = output<string>();

תכונות חובה

  • לחיצה לבחירת קובץ
  • Preview לתמונות / שם קובץ ל-PDF
  • ולידציה: קובץ גדול מדי → uploadError.emit("הקובץ גדול מ-5MB")
  • ולידציה: סוג לא מותר → uploadError.emit("סוג קובץ לא נתמך")
  • כפתור "הסר" לאחר בחירה

✅ Definition of Done

  • בחירת תמונה → preview מוצג
  • בחירת PDF → שם הקובץ מוצג
  • קובץ גדול / סוג שגוי → הודעת שגיאה ברורה
  • standalone, signals
FAB-40
🔴 auth.service.ts — 5 Methods
S · ~3–5 שעות · BLOCKER frontend
💡 refreshToken() ו-loadCurrentUser() כבר מומשו — אל תגעו בהן. רק 5 הפונקציות האלה הן stubs.

קובץ לעריכה: frontend/src/app/core/services/auth.service.ts

פונקציהמה עושה
register(data)POST /api/v1/auth/register
verifyOtp(data)POST /api/v1/auth/verify-otp
login(data)POST /api/v1/auth/login + saveTokens()
logout()clearTokens() + navigate('/login')
resendOtp(email)POST /api/v1/auth/resend-otp

תבנית

login(data: LoginRequest): Observable<TokenResponse> {
  return this.apiService.post<TokenResponse>('/api/v1/auth/login', data).pipe(
    tap(tokens => this.saveTokens(tokens))
  );
}

logout(): void {
  this.clearTokens();
  this.router.navigate(['/login']);
}

✅ Definition of Done

  • כל 5 פונקציות מממשות HTTP אמיתי (לא throw)
  • login() שומר tokens → getAccessToken() מחזיר אותם
  • logout() מנקה + מנתב ל-/login
FAB-41
Guard Bug Fix + Interceptor 401
S · ~3–4 שעות · תלוי ב-FAB-40

❌ עכשיו

  • auth.guard.ts שורה 10: redirect → /home 🐛
  • interceptor: JWT בלבד, אין 401 handling

✅ אחרי

  • redirect → /login
  • 401 → refreshToken() → retry אוטומטי

תיקון 1 — auth.guard.ts (שורה 10)

// ❌ לפני:
return this.router.createUrlTree(['/home']);

// ✅ אחרי:
return this.router.createUrlTree(['/login']);

תיקון 2 — auth.interceptor.ts (הוספת 401)

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const auth = inject(AuthService);  // ← inject בתוך הפונקציה!

  const token = auth.getAccessToken();
  const authReq = token
    ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
    : req;

  return next(authReq).pipe(
    catchError((err: HttpErrorResponse) => {
      if (err.status === 401) {
        return auth.refreshToken().pipe(
          switchMap(() => next(req.clone({
            setHeaders: { Authorization: `Bearer ${auth.getAccessToken()}` }
          })))
        );
      }
      return throwError(() => err);
    })
  );
};

✅ Definition of Done

  • כניסה ל-/forum ללא token → redirect ל-/login (לא /home)
  • access token פג → מתרענן אוטומטית ומנסה שוב
  • inject() נמצא בתוך הפונקציה — לא ברמת המודול
FAB-42
Login Component
M · ~1 יום · תלוי ב-FAB-40
💡 ב-ts: form, isLoading, errorMessage כבר קיימים ב-shell. צריך לממש את onSubmit() ולכתוב את ה-HTML.

onSubmit() — מה להוסיף

onSubmit(): void {
  if (this.form.invalid) return;
  this.isLoading.set(true);
  this.errorMessage.set('');

  this.authService.login(this.form.value as LoginRequest).subscribe({
    next: () => this.router.navigate(['/home']),
    error: (e: HttpErrorResponse) => {
      this.errorMessage.set(e.error?.detail ?? 'שגיאה בהתחברות');
      this.isLoading.set(false);
    }
  });
}

Template HTML (login.component.html)

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <h1>כניסה</h1>

  <input formControlName="email"    type="email"    placeholder="אימייל" />
  <input formControlName="password" type="password" placeholder="סיסמה" />

  <app-error-display [message]="errorMessage()" />

  @if (isLoading()) {
    <app-loading-spinner message="מתחברת..." />
  } @else {
    <button type="submit" [disabled]="form.invalid">כניסה</button>
  }

  <a routerLink="/register">אין לך חשבון? הירשמי</a>
</form>

✅ Definition of Done

  • פרטים נכונים → navigate ל-/home
  • פרטים שגויים → app-error-display מציג שגיאה
  • loading → spinner מוצג, כפתור מסתתר
  • Enter → שולח טופס
FAB-43
Register Steps 1–3 + OTP Screen
L · ~3–4 ימים · תלוי ב-FAB-40 + FAB-39
💡 ה-component קיים עם currentStep, nextStep(), prevStep(). צריך להוסיף forms ו-service calls.

3 שלבים + OTP

שלבשדותמה קורה בסוף
שלב 1 שם מלא, ת"ז, תאריך לידה, מגדר, מגזר, סוג (אלמן/נה/יתום) nextStep() → שלב 2
שלב 2 אימייל + טלפון register() → OTP נשלח → מסך OTP
OTP קוד 6 ספרות verifyOtp() → שלב 3
שלב 3 3 מסמכים (תעודת פטירה, selfie, ת"ז) — <app-file-upload> שמירה → navigate('/home')

onStep2Submit() — הלוגיקה הכי חשובה

onStep2Submit(): void {
  this.isLoading.set(true);
  this.authService.register(this.buildPayload()).subscribe({
    next: () => this.currentStep.set('otp'),   // → מסך OTP
    error: (e) => {
      this.errorMessage.set(e.error?.detail ?? 'שגיאה');
      this.isLoading.set(false);
    }
  });
}

✅ Definition of Done

  • שלב 1 — ולידציה: שדות חובה לא ריקים
  • שלב 2 — POST register עובד → OTP נשלח
  • מסך OTP → verifyOtp() → מגיע לשלב 3
  • שלב 3 — 3 קבצים נבחרים ומוצגים (app-file-upload)
  • כפתור "חזור" עובד בין שלבים
FAB-44
Home + Header Components
S · ~3–4 שעות · תלוי ב-FAB-40

דף הבית — stub עם ניווט לפי תפקיד

"שלום [שם משתמש]!" Admin → כפתור "לוח ניהול" → /admin Moderator → כפתור "דיווחים ממתינים" → /moderator Professional → כפתור "שאלות ממתינות" → /advice User → כפתור "לפורום" → /forum

ניווט לפי תפקיד — בtemplate

user = this.authService.currentUser();

@if (user()?.role === 'Admin') {
  <button routerLink="/admin">לוח ניהול</button>
} @else if (user()?.role === 'Moderator') {
  <button routerLink="/moderator">דיווחים ממתינים</button>
} @else {
  <button routerLink="/forum">לפורום</button>
}

Header — שם משתמש + כפתור יציאה

<header>
  <span class="logo">אנו בניך</span>
  <span class="username">{{ user()?.display_name }}</span>
  <button (click)="authService.logout()">יציאה</button>
</header>

✅ Definition of Done

  • Header: שם משתמש + יציאה → מנקה session + /login
  • דף הבית: תוכן שונה לפי תפקיד
  • currentUser() קרוא מ-authService (signal)
FAB-45
⬛ Register Step 4 — הצהרות (BACKLOG)
S · תלוי ב-FAB-43 · לא חלק מ-Sprint 1
⏸️ אל תתחילו בזה לפני שסיימתם FAB-43 ויש לכם זמן פנוי. אם לא הגעתם אליו — ספרינט 2 יתחיל ממנו.

שלב 4 של ההרשמה: שלושה checkboxes:

  • קראתי ואני מסכימה לתנאי השימוש
  • אני מסכימה לעיבוד מידע אישי
  • אני מצהירה שהמסמכים שהעלאתי אמיתיים

כפתור "סיום" פעיל רק כשכל שלושה מסומנים.

📌

נקודות סנכרון — תיאומים חשובים

📅 יום 0 — 15 דקות ביחד לפני שמתחילות:
וודאו שהשרת עולה אצל שתיכן. קראו יחד את טבלת "לפני ואחרי". חלקו משימות.
📅 לפני שמתחילות FAB-43 ו-FAB-33 — Contract ביחד:
FAB-33 מחזיר RegisterRequest schema. FAB-43 שולחת את אותו payload מה-frontend. 5 דקות לוודא שה-fields תואמים — חוסכות שעות של debugging.
⚠️ FAB-43 ו-FAB-39 חולקות קוד — תאמו לפני שמתחילות:
FAB-43 משתמשת ב-<app-file-upload> שנבנה ב-FAB-39. וודאו ש-FAB-39 מוזגה לפני שמתחילות את שלב 3 של FAB-43.
🔗 חיבור Backend + Frontend — יום 6:
ברגע שFAB-35 (wire endpoints) מוזג + FAB-40 (auth.service) מוזג — עשו יחד end-to-end test: register → verify-otp → login → home.

🚫 חוק ברזל — PR Reviews

אין merge ללא review של המפתחת השנייה. אין יוצא מן הכלל. PR שממתין לreview → תמשיכו בפיצ'ר אחר, אל תחכו בטל.
🖼️

האפליקציה בסוף הספרינט — איך נראה

דף Login

┌─────────────────────────────────────┐ │ אנו בניך │ │ כניסה לחשבון │ │ │ │ ┌─────────────────────────────┐ │ │ │ 📧 אימייל │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │ │ 🔒 סיסמה │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ כניסה ▶ │ │ │ └─────────────────────────────┘ │ │ │ │ אין לך חשבון? הירשמי │ └─────────────────────────────────────┘

הרשמה — שלב 1 מתוך 3

┌─────────────────────────────────────┐ │ ● ──────── ○ ──────── ○ │ │ שלב 1 שלב 2 OTP │ │ │ │ שם מלא: [________________] │ │ ת"ז: [________________] │ │ מגזר: [חסידי ▼ ] │ │ סוג: [אלמנה ▼ ] │ │ │ │ ┌──────────┐ ┌────────────────┐ │ │ │ ← חזור │ │ המשך ← │ │ │ └──────────┘ └────────────────┘ │ └─────────────────────────────────────┘

דף הבית — Admin

┌─────────────────────────────────────┐ │ [אנו בניך] שרה מנהל [יציאה] │ ├─────────────────────────────────────┤ │ │ │ שלום שרה! 👋 │ │ │ │ ┌─────────────────────────────┐ │ │ │ 🛠️ לוח ניהול │ │ │ └─────────────────────────────┘ │ │ │ │ (Sprint 2: אישור הרשמות כאן) │ │ │ └─────────────────────────────────────┘

מה לא קיים בסוף Sprint 1

פיצ'רמצבספרינט
אישור הרשמה ע"י AdminSprint 2Sprint 2
Dockerfiles + CI/CDSprint 2Sprint 2
פורוםSprint 3Sprint 3
ייעוץ מקצועי + מודרציהSprint 4Sprint 4
הודעות פרטיותSprint 5Sprint 5

Definition of Done — ספרינט 1 סגור כשכל זה עובד

✅ בדקו את הצ'קליסט הזה ביחד לפני שסוגרות את הספרינט.

Backend — בדיקה ב-Swagger (localhost:8000/docs)

Frontend — בדיקה ב-ng serve (localhost:4200)

Shared Components — בדיקה ויזואלית

קוד — Quality

🗺️ מה מחכה בספרינטים הבאים

ספרינטנושאמה בונים
Sprint 2אישור + ענןAdmin אישור הרשמה (dual-approval), Dockerfiles, CI/CD, PostgreSQL
Sprint 3פורוםפוסטים + סינון קבוצה/מגזר + דיווח 3 שלבי
Sprint 4ייעוץ + מודרציהאנשי מקצוע + מבקרים + חריגות אוטומטיות
Sprint 5הודעות + סיוםהודעות פרטיות + Audit Log + RTL polish + מחיקת חשבון
אנו בניך · ספרינט 1 · 2026 · גרסה 1.0