- Header search restyled as one clean RTL pill (input + button flush).
- Google-style autocomplete: typing ≥2 chars fetches /search/suggest and
shows up to 5 live matches (round-robin across shifts/jobs/applicants)
with the query highlighted, plus a «همه نتایج» link. Debounced, closes on
outside-click/Escape.
- Search results page now RANKS by relevance (term hits in role/title/
facility/city/tags weighted ×3, description ×1) instead of date-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /Search: searches shifts, hiring openings, and applicants together via
Postgres ILIKE (every term must match across role/city/facility/title/
description/tags/person). Results grouped per type.
- Keyword highlighting (<mark>) extended to shift & job cards (was talent-only),
so matches stand out everywhere.
- Persistent header search box (.nav-search) → /Search; big hero box on the
page itself.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaced the plain "interest recorded" alert with a styled .contact-reveal
card that fades/slides in and lists each channel as its own row (icon +
label + value + action button). Shift/job show facility phone + Bale;
talent shows all its ContactMethods in the same table style.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Tags: parser extracts cert/skill keywords (mmt, ICU/CCU, دیالیز, اتاق عمل,
اورژانس, مسئول فنی, پروانهدار…) + role + city into TalentListing.Tags
(+ migration); shown as chips on cards.
- Deep search on /Talent: «جستجوی عمیق» box does Postgres ILIKE across
tags, description, person, area, role, city (every term must match);
matches are highlighted with <mark> via SearchHighlight.
- Never delete: ShiftStatus.Archived + the admin «بایگانی گروهی» action now
ARCHIVES aggregated posts (hidden from site, kept in DB) and leaves the
raw crawl rows intact — a permanent archive for future analytics.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sources panel (Telegram/Bale/Divar/Medjobs/Websites/Proxy) ran
together as one flat list. Each is now wrapped in a bordered .source-box
with an icon + hint, so it's clear where one source's settings end and the
next begins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a third listing kind alongside Shift/Job for healthcare staff who
advertise their own availability (very common in Iranian medical
channels, e.g. "دندانپزشک آماده همکاری… ۵۰٪ تسویه"). These have no
facility; the contact phone is the key field.
- Model: TalentListing (role, person name, years, licensed, city/district,
area note, availability, gender, comp, phone) + ListingKind.Talent +
RawListing.LinkedTalentId + DbSet/relations/indexes + EF migration.
- Parser: detect آمادهبهکار/جویای کار → Kind=Talent; extract person name,
years of experience, licensed flag, area («منطقه ۱»), phone. Facility
name extraction now skipped for talent.
- Validator: talent path scores role + phone + medical (no facility/pay
required).
- Ingestion auto-publish: creates a TalentListing for talent kind.
- Review (manual publish): Talent option + talent fields; publishes a
TalentListing without a facility. Shift/Job facility now falls back to a
shared «نامشخص / ثبت نشده» record when the ad names none — publishing
never fails on a missing facility.
- Browse /Talent (indexable, filters: city/district/role/gender),
details /Talent/Details (noindex — personal contact, tel: call button),
_TalentCard, badge-talent, nav link, home section.
- Sitemap includes /Talent; robots disallows /Talent/Details. Archiver
expires stale talent listings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The profile dropdown was doing three jobs at once (account actions, the
job-seeker panel menu, and the admin panel menu) and a stray inline @if
for the notification badge leaked into the markup as literal text.
- Profile dropdown is now account-only: identity card (avatar + name +
phone), one role-aware dashboard entry, edit profile, logout. This
removes the leaked @if and de-clutters the menu.
- Dashboard menu is centralized in _PanelNav and auto-rendered by the
layout on every logged-in panel page (/Admin, /Me, /Employer,
/Preferences) instead of being duplicated in the dropdown and pages.
- Drop the now-duplicate manual <partial name="_PanelNav" /> from
Overview, Ingested, Me/Index, Employer/Index.
- CSS: identity-card (.pd-id) styles + mobile tweaks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OnPostPublishAsync inserted a Shift/Job with FacilityId=0 when no
facility was selected (e.g. the dropdown is empty because no facilities
exist yet), throwing FK_Shifts_Facilities_FacilityId and surfacing the
production error page.
- Resolve-or-create the facility before insert: use the picked one, else
create an unverified Facility from a typed name (reusing same-named).
- Guard the role too; on missing facility/role redirect back with a
Persian error message instead of 500.
- Review form: add "new facility name" input + "— none —" option +
error alert; add .alert-error style.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New /Admin/Ingested page lists every crawled item with its outcome, filterable by status (همه/در صف/پرچمخورده/منتشرشده/ردشده) with per-status counts and a link to the published shift or the review page. Linked from the run-history header and the admin panel nav. Plus an inline ✕رد (quick-discard) button on each queue/flagged row so admins can audit without opening the review page; full accept/reject stays on /Admin/Review.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Logged-in panels (admin/employer/job-seeker) now show a sticky role-based dashboard menu (_PanelNav) on Employer/Index, Me/Index and Admin/Overview, with the active section highlighted — so users have an obvious menu and dashboard, not just a hidden avatar. Profile button: avatar fallback shows a 👤 glyph instead of the phone's first digit (the confusing '0'), and the desktop button now shows the user's name (or «حساب من») so it reads as a profile menu.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Header gets a prominent accent +ثبت آگهی CTA → /Employer/Index (auth redirect handles login → register/post). Main nav trimmed to the 5 core public links (خانه/شیفتها/استخدام/مراکز/تقویم); دریافت اپ + راهنما live in the footer and علاقهمندیها in the profile menu, so the bar is far less crowded. Added active-page highlight (accent underline on desktop, soft background on mobile). Login now sends admins to /Admin/Overview (dashboard) instead of the ingestion queue; employers→/Employer/Index, job-seekers→/Me already in place.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New /Facilities/Details public page: verified badge, info, Neshan map + directions, the facility's open shifts & jobs, and a complaint form; facility cards on /Facilities link to it. Ratings & reviews: Review model (1-5 stars + comment, one per user/facility, unique index, migration); logged-in users rate/review on the facility page; average + count shown in the header and the review list; admins moderate (hide/delete) at /Admin/Reviews.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Employer Listings: each applicant row now shows their avatar (or initials) and a «مشاهده رزومه» link when they uploaded one (served via /resume/{id}, already access-controlled to the receiving employer). Applicant projection avoids loading avatar/resume blobs. Me panel: a nudge banner prompts users to complete their profile (name/photo/resume) when any is missing, linking to /Me/Profile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every user gets a full editable profile at /Me/Profile: name, role, city, specialty/title, license, years, bio + avatar image upload + resume upload (PDF/image). Avatar/resume stored in-DB on User (migration, 5 nullable columns). Endpoints: /avatar/{id} (public) and /resume/{id} (owner, admin, or an employer who received that user's application). Nav: replaced the scattered action links with an avatar button + dropdown listing all of the user's pages by role (profile, کارجو panel, alerts, preferences, notifications; employer panel; admin panel + settings; logout) — shows the avatar image or initials; collapses into the burger menu on mobile; closes on outside-click.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On mobile the action panel (CTA + map) stacked at the very bottom, so users had to scroll the whole page to act. Add a fixed bottom action bar (<=860px) with the primary «اعلام تمایل» button + a quick save, always reachable like a native app; when contact is revealed it becomes a «تماس با مرکز» tel: button. The in-aside primary CTA is hidden on mobile (.aside-apply) to avoid duplication, and pages get bottom padding (.has-action-bar) so the bar never covers content. Desktop layout unchanged (bar hidden, sidebar CTA shown).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each ingestion source now decides independently whether to route through the proxy: added TelegramUseProxy/BaleUseProxy/DivarUseProxy/MedjobsUseProxy/WebsitesUseProxy flags (migration). ScrapeHttpClients.For(s, useProxy) takes the source's own flag; a source is proxied only when its flag is on AND a proxy URL is set. Settings 'sources' tab: removed the global enable checkbox, kept the proxy address field, and added an «از پروکسی استفاده شود» checkbox under each source. Old IngestProxyEnabled column kept for compatibility but no longer gates routing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Split the long settings page into 7 sidebar tabs (publish+AI, sources, channels, SMS, push, map, demo) with a single form so one Save persists everything; seed/clear/test are submit buttons targeting their handlers via asp-page-handler. Boolean settings now render as clean .toggle-row cards. CSS fix: the form input rule omitted input[type=password] (and url/email/search), so API-key/VAPID/token fields were unstyled — added them, plus accent-color + sizing for checkboxes/radios. Active tab persists across handler posts via sessionStorage; layout collapses to a horizontal tab strip on mobile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hamburger bars used the undefined var(--text) (this theme defines --ink), so they rendered transparent and the button looked blank on mobile. Switch the three var(--text) uses (burger bars, toast, tour bubble) to var(--ink). Tour: visible() used offsetParent/rect, which the collapsed mobile nav (visibility:hidden) still satisfies, so the tour tried to spotlight hidden links and looked broken on mobile. Use element.checkVisibility({checkVisibilityCSS}) (with a computed-style ancestor-walk fallback) so only truly-visible targets are toured — mobile now shows welcome + menu, desktop shows the full nav walkthrough.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New /Help page: quick-start, separate guides for کادر درمان (search/near-me/preferences/اعلام تمایل/saved) and مراکز درمانی (register/post/verify-with-docs/applicants), notifications+install, report/complaint, and an FAQ accordion. Self-hosted tour.js (no CDN, RTL): spotlights elements via data-tour hooks in the nav, auto-runs once for new visitors on the home page (localStorage flag), re-runnable from the Help page button or ?tour=1; skips steps whose target is hidden so it works on mobile/other pages. Help linked from nav + footer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Browsers do not inherit font-family on button/input/select/textarea, so all buttons (the contact-reveal box is mostly buttons) rendered in the UA default font instead of Vazirmatn, clashing with the rest of the page. Add a font-family: inherit reset for form controls and on .btn. Verified the CTA and action buttons now compute to Vazirmatn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Web Push is delivered by the browser vendor's push service (Chrome to Google FCM), which is filtered in Iran, so background push is unreliable. Add a Server-Sent Events channel over our own origin that always reaches users while the tab/PWA is open: NotificationHub (in-memory pub/sub), a /notifications/stream SSE endpoint (auth-gated, keep-alive pings, nginx no-buffer), and NotificationService now publishes each saved notification to the hub. Client updates the bell badge instantly, shows a toast, and fires a local OS notification via the service worker when permission is granted (no push server). Web Push stays as best-effort for closed-app reach. Verified end-to-end: login, open stream, broadcast, event delivered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap the bell label in a span so the conditional parses as Razor code instead of leaking as literal text (Persian word char before the at-sign triggered email detection). Bell is icon-only on desktop, label shows in the mobile dropdown. Add a Settings link to admin actions so the demo-mode page is reachable from the navbar. Tighten desktop nav/action spacing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _Layout: wrap nav + actions in .nav-collapse; add CSS-only burger
(checkbox toggle) + always-visible mobile bell next to it
- site.css: desktop layout unchanged; <=860px collapses nav into a
full-width dropdown (column links, stacked actions), animated open
- Login (ورود) + logout render as full-width buttons in the menu
instead of overflowing the header row
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Notification entity + NotificationService: on publish, notify users whose saved prefs match the listing (role/city/+shift type); users with no preference aren't spammed
- Wired into PostShift, PostJob, and Admin Review publish
- 🔔 bell with unread count in the header (@inject) + /Me/Notifications page (mark-all-read on open)
- Reliable in-app delivery (works in Iran without FCM); Web Push can ride the same records later
- Verified: employee pref → employer posts matching shift → employee bell=۱ + 'شیفت جدید: پزشک عمومی'
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MedjobsListingSource: crawls medjobs.ir sitemaps (ad_listing-sitemapN) → fetches ad pages → title+description → engine (dedupe/parse/validate/publish as SEO job pages). Configured in /Admin/Settings (enable + max ads/run).
- Login/register now asks 'کادر درمان' vs 'کارفرما/مرکز': new accounts get Doctor vs FacilityAdmin role; post-login routes to /Me, /Employer, or /Admin accordingly.
- Verified live: medjobs run fetched real ads into the review queue; employer signup → /Employer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Mobile nav was display:none (no navigation on phones) → now a scrollable strip on a second header row
- Fluid typography via clamp() for hero/page/section headings + spacing
- Long pay/choice labels (… یا … به انتخاب شما) now wrap in card footers instead of overflowing
- Calendar table scrolls horizontally on small screens (cal-wrap + min-width)
- Filter sidebar non-sticky/full-width on mobile; tighter small-screen padding & buttons
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>