From bcb9c86541c75e051e0c3d81a63d1301454bc322 Mon Sep 17 00:00:00 2001 From: ismail Date: Thu, 9 Apr 2026 13:46:34 +0300 Subject: [PATCH] pre dep --- PX360/settings.py | 141 +- apps/accounts/ui_views.py | 64 +- apps/analytics/tasks.py | 2 +- apps/analytics/ui_views.py | 29 + apps/analytics/urls.py | 1 + apps/complaints/forms.py | 34 +- apps/complaints/models.py | 54 +- apps/complaints/ui_views.py | 39 +- apps/complaints/ui_views_oncall.py | 460 +-- apps/core/config_urls.py | 13 +- apps/core/config_views.py | 148 +- apps/core/encryption.py | 71 + .../commands/send_example_emails.py | 226 +- apps/core/templatetags/national_id_tags.py | 10 + apps/integrations/services/his_adapter.py | 19 +- apps/integrations/services/his_client.py | 40 +- apps/integrations/tasks.py | 195 +- apps/organizations/admin.py | 210 +- apps/organizations/models.py | 15 +- apps/organizations/serializers.py | 15 +- apps/organizations/ui_views.py | 7 +- apps/organizations/views.py | 20 +- apps/physicians/adapter.py | 5 + apps/physicians/forms.py | 138 +- apps/physicians/import_views.py | 80 +- .../commands/import_his_doctor_ratings.py | 2 +- apps/physicians/tasks.py | 242 +- apps/physicians/urls.py | 2 + apps/px_sources/ui_views.py | 16 +- apps/surveys/ui_views.py | 6 +- config/celery.py | 20 +- locale/ar/LC_MESSAGES/django.mo | Bin 407431 -> 404634 bytes locale/ar/LC_MESSAGES/django.po | 3098 ++++++++++------- requirements.txt | 131 +- .../accounts/onboarding/invitation_email.html | 151 +- .../accounts/onboarding/step_content.html | 99 +- templates/analytics/dashboard.html | 29 +- templates/callcenter/complaint_form.html | 2 +- templates/callcenter/inquiry_form.html | 2 +- templates/complaints/complaint_form.html | 69 + templates/complaints/oncall/admin_form.html | 43 +- templates/complaints/partials/ai_panel.html | 17 +- .../complaints/public_complaint_success.html | 4 +- .../complaints/public_complaint_track.html | 18 +- templates/config/dashboard.html | 16 +- .../config/emails/reset_password_email.html | 79 + templates/config/hospital_users.html | 509 +++ templates/core/public_submit.html | 6 - templates/emails/base_email_template.html | 183 +- templates/emails/explanation_request.html | 205 +- .../emails/new_observation_notification.html | 192 +- templates/emails/simple_email.html | 8 + templates/emails/survey_invitation.html | 110 +- templates/layouts/partials/sidebar.html | 5 + templates/layouts/public_base.html | 43 +- templates/observations/observation_list.html | 4 +- templates/observations/public_new.html | 2 +- templates/observations/public_success.html | 2 +- templates/observations/public_track.html | 5 - templates/organizations/patient_detail.html | 11 +- templates/organizations/patient_list.html | 22 +- .../organizations/patient_visit_journey.html | 7 +- templates/physicians/doctor_rating_fetch.html | 221 ++ .../physicians/doctor_rating_import.html | 7 +- .../physicians/doctor_rating_job_list.html | 17 +- templates/px_sources/source_detail.html | 170 +- 66 files changed, 4698 insertions(+), 3113 deletions(-) create mode 100644 apps/core/encryption.py create mode 100644 apps/core/templatetags/national_id_tags.py create mode 100644 templates/config/emails/reset_password_email.html create mode 100644 templates/config/hospital_users.html create mode 100644 templates/emails/simple_email.html create mode 100644 templates/physicians/doctor_rating_fetch.html diff --git a/PX360/settings.py b/PX360/settings.py index cdcaf23..853958e 100644 --- a/PX360/settings.py +++ b/PX360/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/6.0/ref/settings/ """ +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -17,10 +18,12 @@ BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/6.0/howto/deployment-checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-b!)avw-ibl#m*p-_vw%k4#)*b8a*-(7k4-#6eb8un@=-mksed(' +SECRET_KEY = "django-insecure-b!)avw-ibl#m*p-_vw%k4#)*b8a*-(7k4-#6eb8un@=-mksed(" + +CIPHER_KEY = os.environ.get("CIPHER_KEY", SECRET_KEY) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -31,63 +34,62 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", # Apps - 'apps.core', - 'apps.accounts', - 'apps.dashboard', - 'apps.social', - 'django_celery_beat', + "apps.core", + "apps.accounts", + "apps.dashboard", + "apps.social", + "django_celery_beat", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", # PX Source User Access Control - 'apps.px_sources.middleware.SourceUserRestrictionMiddleware', - 'apps.px_sources.middleware.SourceUserSessionMiddleware', + "apps.px_sources.middleware.SourceUserRestrictionMiddleware", + "apps.px_sources.middleware.SourceUserSessionMiddleware", ] -ROOT_URLCONF = 'PX360.urls' +ROOT_URLCONF = "PX360.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'] - , - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'apps.core.context_processors.hospital_context', - 'apps.core.context_processors.sidebar_counts', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "apps.core.context_processors.hospital_context", + "apps.core.context_processors.sidebar_counts", ], }, }, ] -WSGI_APPLICATION = 'PX360.wsgi.application' +WSGI_APPLICATION = "PX360.wsgi.application" # Database # https://docs.djangoproject.com/en/6.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -97,16 +99,16 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -114,9 +116,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/6.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -126,35 +128,34 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/6.0/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" # Celery Configuration -CELERY_BROKER_URL = 'redis://localhost:6379/0' -CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' -CELERY_ACCEPT_CONTENT = ['json'] -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' +CELERY_BROKER_URL = "redis://localhost:6379/0" +CELERY_RESULT_BACKEND = "redis://localhost:6379/0" +CELERY_ACCEPT_CONTENT = ["json"] +CELERY_TASK_SERIALIZER = "json" +CELERY_RESULT_SERIALIZER = "json" CELERY_TIMEZONE = TIME_ZONE CELERY_ENABLE_UTC = True # Django Celery Beat Scheduler -CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' -LINKEDIN_CLIENT_SECRET ='WPL_AP1.Ek4DeQDXuv4INg1K.mGo4CQ==' -LINKEDIN_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/LI/' +CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" +LINKEDIN_CLIENT_SECRET = "WPL_AP1.Ek4DeQDXuv4INg1K.mGo4CQ==" +LINKEDIN_REDIRECT_URI = "http://127.0.0.1:8000/social/callback/LI/" LINKEDIN_WEBHOOK_VERIFY_TOKEN = "your_random_secret_string_123" # YOUTUBE API CREDENTIALS # Ensure this matches your Google Cloud Console settings -YOUTUBE_CLIENT_SECRETS_FILE = BASE_DIR / 'secrets' / 'yt_client_secrets.json' -YOUTUBE_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/YT/' - +YOUTUBE_CLIENT_SECRETS_FILE = BASE_DIR / "secrets" / "yt_client_secrets.json" +YOUTUBE_REDIRECT_URI = "http://127.0.0.1:8000/social/callback/YT/" # Google REVIEWS Configuration # Ensure you have your client_secrets.json file at this location -GMB_CLIENT_SECRETS_FILE = BASE_DIR / 'secrets' / 'gmb_client_secrets.json' -GMB_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/GO/' +GMB_CLIENT_SECRETS_FILE = BASE_DIR / "secrets" / "gmb_client_secrets.json" +GMB_REDIRECT_URI = "http://127.0.0.1:8000/social/callback/GO/" # Data upload settings @@ -162,11 +163,10 @@ GMB_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/GO/' DATA_UPLOAD_MAX_NUMBER_FIELDS = 20000 - # X API Configuration -X_CLIENT_ID = 'your_client_id' -X_CLIENT_SECRET = 'your_client_secret' -X_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/X/' +X_CLIENT_ID = "your_client_id" +X_CLIENT_SECRET = "your_client_secret" +X_REDIRECT_URI = "http://127.0.0.1:8000/social/callback/X/" # TIER CONFIGURATION # Set to True if you have Enterprise Access # Set to False for Free/Basic/Pro @@ -174,16 +174,15 @@ X_USE_ENTERPRISE = False # --- TIKTOK CONFIG --- -TIKTOK_CLIENT_KEY = 'your_client_key' -TIKTOK_CLIENT_SECRET = 'your_client_secret' -TIKTOK_REDIRECT_URI = 'http://127.0.0.1:8000/social/callback/TT/' - +TIKTOK_CLIENT_KEY = "your_client_key" +TIKTOK_CLIENT_SECRET = "your_client_secret" +TIKTOK_REDIRECT_URI = "http://127.0.0.1:8000/social/callback/TT/" # --- META API CONFIG --- -META_APP_ID = '1229882089053768' -META_APP_SECRET = 'b80750bd12ab7f1c21d7d0ca891ba5ab' -META_REDIRECT_URI = 'https://micha-nonparabolic-lovie.ngrok-free.dev/social/callback/META/' -META_WEBHOOK_VERIFY_TOKEN = 'random_secret_string_khanfaheed123456' +META_APP_ID = "1229882089053768" +META_APP_SECRET = "b80750bd12ab7f1c21d7d0ca891ba5ab" +META_REDIRECT_URI = "https://micha-nonparabolic-lovie.ngrok-free.dev/social/callback/META/" +META_WEBHOOK_VERIFY_TOKEN = "random_secret_string_khanfaheed123456" -EMAIL_LOGO_URL = 'http://127.0.0.1:8000/static/img/HH_R_H_Logo.png' \ No newline at end of file +EMAIL_LOGO_URL = "http://127.0.0.1:8000/static/img/HH_R_H_Logo.png" diff --git a/apps/accounts/ui_views.py b/apps/accounts/ui_views.py index 708596c..3a569f3 100644 --- a/apps/accounts/ui_views.py +++ b/apps/accounts/ui_views.py @@ -281,6 +281,9 @@ def onboarding_step_content(request, step): # Get content for user's role content_list = get_wizard_content_for_user(user) + if not content_list: + return redirect("/accounts/onboarding/wizard/checklist/") + # Get current step content try: current_content = content_list[step - 1] @@ -288,6 +291,12 @@ def onboarding_step_content(request, step): # Step doesn't exist, go to checklist return redirect("/accounts/onboarding/wizard/checklist/") + if request.method == "POST": + OnboardingService.save_wizard_step(user, step) + if step < len(content_list): + return redirect("/accounts/onboarding/wizard/step/{}/".format(step + 1)) + return redirect("/accounts/onboarding/wizard/checklist/") + # Get completed steps completed_steps = user.wizard_completed_steps or [] @@ -624,28 +633,30 @@ def bulk_invite_users(request): if not csv_file.name.endswith(".csv"): messages.error(request, "Please upload a valid CSV file.") return redirect("accounts:bulk-invite-users") - + try: decoded_file = csv_file.read().decode("utf-8") io_string = io.StringIO(decoded_file) reader = csv.DictReader(io_string) - + required_fields = ["email", "first_name", "last_name", "role"] if reader.fieldnames: missing_fields = [f for f in required_fields if f not in reader.fieldnames] if missing_fields: messages.error(request, f"Missing required columns in CSV: {', '.join(missing_fields)}") return redirect("accounts:bulk-invite-users") - + for row in reader: - rows_to_process.append({ - "email": row.get("email", "").strip(), - "first_name": row.get("first_name", "").strip(), - "last_name": row.get("last_name", "").strip(), - "role": row.get("role", "").strip(), - "hospital_id": row.get("hospital_id", "").strip(), - "department_id": row.get("department_id", "").strip() - }) + rows_to_process.append( + { + "email": row.get("email", "").strip(), + "first_name": row.get("first_name", "").strip(), + "last_name": row.get("last_name", "").strip(), + "role": row.get("role", "").strip(), + "hospital_id": row.get("hospital_id", "").strip(), + "department_id": row.get("department_id", "").strip(), + } + ) except Exception as e: messages.error(request, f"Error processing CSV file: {str(e)}") @@ -653,14 +664,14 @@ def bulk_invite_users(request): if emails_text: role_id = request.POST.get("role_id") hospital_id = request.POST.get("hospital_id") - + if not role_id: messages.error(request, "Please select a role for manual email entries.") else: try: role_obj = Role.objects.get(id=role_id) role_name = role_obj.name - + for email in emails_text.splitlines(): email = email.strip() if email and "@" in email: @@ -669,15 +680,17 @@ def bulk_invite_users(request): parts = name_part.split() f_name = parts[0] if parts else "Staff" l_name = " ".join(parts[1:]) if len(parts) > 1 else "Member" - - rows_to_process.append({ - "email": email, - "first_name": f_name, - "last_name": l_name, - "role": role_name, - "hospital_id": hospital_id, - "department_id": "" - }) + + rows_to_process.append( + { + "email": email, + "first_name": f_name, + "last_name": l_name, + "role": role_name, + "hospital_id": hospital_id, + "department_id": "", + } + ) except Exception as e: messages.error(request, f"Error processing manual entries: {str(e)}") @@ -685,7 +698,7 @@ def bulk_invite_users(request): for row in rows_to_process: results["total"] += 1 email = row.get("email", "").strip() - + try: first_name = row.get("first_name", "").strip() last_name = row.get("last_name", "").strip() @@ -775,7 +788,6 @@ def bulk_invite_users(request): if results["errors"]: messages.warning(request, f"Failed to invite {len(results['errors'])} users. See details below.") - # Get data for template roles = Role.objects.all() hospitals = Hospital.objects.filter(status="active").order_by("name") @@ -1110,7 +1122,9 @@ def provisional_user_list(request): # Filter by hospital based on user role # Check PX Admin first to avoid logic issues when user has multiple roles if request.user.is_px_admin() and request.tenant_hospital: - provisional_users = provisional_users.filter(hospital=request.tenant_hospital) + from django.db.models import Q + + provisional_users = provisional_users.filter(Q(hospital=request.tenant_hospital) | Q(hospital__isnull=True)) elif request.user.is_hospital_admin() and request.user.hospital: provisional_users = provisional_users.filter(hospital=request.user.hospital) diff --git a/apps/analytics/tasks.py b/apps/analytics/tasks.py index dce151b..16b0039 100644 --- a/apps/analytics/tasks.py +++ b/apps/analytics/tasks.py @@ -110,7 +110,7 @@ def generate_action_recommendations_task(self, user_id=None, hospital_id=None, d def precompute_dashboard_cache_task(self): """ Async task: Pre-compute all cacheable analytics data for all active hospitals. - Run every 5 minutes so the dashboard is always fast. + Run daily at 3 AM. Users can trigger on-demand refresh via dashboard button. """ from apps.analytics.services.ai_analytics import ( ExecutiveSummaryGenerator, diff --git a/apps/analytics/ui_views.py b/apps/analytics/ui_views.py index fa8f08d..c53cd2c 100644 --- a/apps/analytics/ui_views.py +++ b/apps/analytics/ui_views.py @@ -504,6 +504,35 @@ def refresh_ai_analytics(request): ) +@block_source_user +@login_required +def refresh_dashboard_cache(request): + """ + API endpoint: Trigger dashboard cache refresh on demand. + POST to trigger refresh, returns immediately with task status. + """ + if request.method != "POST": + return JsonResponse({"error": "POST method required"}, status=405) + + from .tasks import precompute_dashboard_cache_task + + user = request.user + + # Trigger async cache refresh + task = precompute_dashboard_cache_task.delay() + + # Clear user's dashboard cache so next load gets fresh data + cache.delete(f"analytics_dashboard_{user.id}_all") + if hasattr(request, "tenant_hospital") and request.tenant_hospital: + cache.delete(f"analytics_dashboard_{user.id}_{request.tenant_hospital.id}") + + return JsonResponse({ + "status": "triggered", + "message": "Dashboard cache refresh queued. Please reload the page in a few seconds.", + "task_id": str(task.id), + }) + + @block_source_user @login_required def kpi_list(request): diff --git a/apps/analytics/urls.py b/apps/analytics/urls.py index 671ebc9..dfb5aaa 100644 --- a/apps/analytics/urls.py +++ b/apps/analytics/urls.py @@ -16,6 +16,7 @@ urlpatterns = [ # AI Analytics API path('api/ai-analytics/refresh/', ui_views.refresh_ai_analytics, name='refresh_ai_analytics'), + path('api/dashboard/refresh-cache/', ui_views.refresh_dashboard_cache, name='refresh_dashboard_cache'), path('api/ask-data/query/', ask_views.ask_data_query, name='ask_data_query'), # KPI Reports diff --git a/apps/complaints/forms.py b/apps/complaints/forms.py index 8a6e0d9..edcb79a 100644 --- a/apps/complaints/forms.py +++ b/apps/complaints/forms.py @@ -556,42 +556,12 @@ class ComplaintForm(HospitalFieldMixin, forms.ModelForm): self.fields["main_section"].queryset = MainSection.objects.none() self.fields["subsection"].queryset = SubSection.objects.none() - # Load all locations (no filtering needed) - self.fields["location"].queryset = Location.objects.all().order_by("name_en") + # Load locations: Inpatient, Outpatient Clinics, Emergency, Others + self.fields["location"].queryset = Location.objects.filter(id__in=[48, 49, 82, 110]).order_by("name_en") # Load active PX sources for optional selection self.fields["source"].queryset = PXSource.objects.filter(is_active=True).order_by("name_en") - # Check both initial data and POST data for location to load sections - location_id = None - if "location" in self.initial: - location_id = self.initial["location"] - elif "location" in self.data: - location_id = self.data["location"] - - if location_id: - # Filter sections based on selected location - from apps.organizations.models import SubSection - - available_sections = ( - SubSection.objects.filter(location_id=location_id).values_list("main_section_id", flat=True).distinct() - ) - self.fields["main_section"].queryset = MainSection.objects.filter(id__in=available_sections).order_by( - "name_en" - ) - - # Load subsections if section is selected - section_id = None - if "main_section" in self.initial: - section_id = self.initial["main_section"] - elif "main_section" in self.data: - section_id = self.data["main_section"] - - if section_id: - self.fields["subsection"].queryset = SubSection.objects.filter( - location_id=location_id, main_section_id=section_id - ).order_by("name_en") - # Hospital field is configured by HospitalFieldMixin # Now filter departments and staff based on hospital hospital_id = None diff --git a/apps/complaints/models.py b/apps/complaints/models.py index 5b6d1eb..26cc180 100644 --- a/apps/complaints/models.py +++ b/apps/complaints/models.py @@ -16,6 +16,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from apps.core.encryption import EncryptedCharField, compute_national_id_hash, mask_national_id from apps.core.models import PriorityChoices, SeverityChoices, TenantModel, TimeStampedModel, UUIDModel @@ -213,9 +214,10 @@ class Complaint(UUIDModel, TimeStampedModel): max_length=200, blank=True, verbose_name="Patient Name", help_text="Name of the patient involved" ) - national_id = models.CharField( + national_id = EncryptedCharField( max_length=20, blank=True, verbose_name="National ID/Iqama No.", help_text="Saudi National ID or Iqama number" ) + national_id_hash = models.CharField(max_length=64, blank=True, db_index=True) incident_date = models.DateField( null=True, blank=True, verbose_name="Incident Date", help_text="Date when the incident occurred" @@ -512,6 +514,9 @@ class Complaint(UUIDModel, TimeStampedModel): return reverse("complaints:complaint_detail", kwargs={"pk": self.pk}) + def get_masked_national_id(self): + return mask_national_id(self.national_id) + def save(self, *args, **kwargs): """Calculate SLA due date on creation, generate reference number, and sync complaint_type from metadata""" # Track status change for signals @@ -543,6 +548,11 @@ class Complaint(UUIDModel, TimeStampedModel): if self.complaint_type == "complaint" and ai_complaint_type != "complaint": self.complaint_type = ai_complaint_type + if self.national_id: + self.national_id_hash = compute_national_id_hash(self.national_id) + else: + self.national_id_hash = "" + super().save(*args, **kwargs) def calculate_sla_due_date(self): @@ -658,6 +668,45 @@ class Complaint(UUIDModel, TimeStampedModel): ComplaintStatus.CONTACTED_NO_RESPONSE, ] + PUBLIC_STATUS_MAP = { + ComplaintStatus.OPEN: {"label": _("Received"), "slug": "received", "progress": 15, "css": "amber"}, + ComplaintStatus.IN_PROGRESS: {"label": _("In Progress"), "slug": "in_progress", "progress": 50, "css": "blue"}, + ComplaintStatus.PARTIALLY_RESOLVED: { + "label": _("In Progress"), + "slug": "in_progress", + "progress": 75, + "css": "blue", + }, + ComplaintStatus.RESOLVED: {"label": _("Resolved"), "slug": "resolved", "progress": 100, "css": "emerald"}, + ComplaintStatus.CLOSED: {"label": _("Closed"), "slug": "closed", "progress": 100, "css": "slate"}, + ComplaintStatus.CANCELLED: {"label": _("Cancelled"), "slug": "cancelled", "progress": 0, "css": "rose"}, + ComplaintStatus.CONTACTED: {"label": _("In Progress"), "slug": "in_progress", "progress": 50, "css": "blue"}, + ComplaintStatus.CONTACTED_NO_RESPONSE: { + "label": _("In Progress"), + "slug": "in_progress", + "progress": 50, + "css": "blue", + }, + } + + @property + def public_status(self): + mapping = self.PUBLIC_STATUS_MAP.get( + self.status, + { + "label": _("Received"), + "slug": "received", + "progress": 15, + "css": "amber", + }, + ) + return { + "label": str(mapping["label"]), + "slug": mapping["slug"], + "progress": mapping["progress"], + "css": mapping["css"], + } + @property def short_description_en(self): """Get AI-generated short description (English) from metadata""" @@ -2225,8 +2274,7 @@ class OnCallAdmin(UUIDModel, TimeStampedModel): "accounts.User", on_delete=models.CASCADE, related_name="on_call_schedules", - help_text="PX Admin user who is on-call", - limit_choices_to={"groups__name": "PX Admin"}, + help_text="User who is on-call (PX Admin, PX Coordinator, or Hospital Admin)", ) # Optional: date range for this on-call assignment diff --git a/apps/complaints/ui_views.py b/apps/complaints/ui_views.py index 5c08d27..5aec756 100644 --- a/apps/complaints/ui_views.py +++ b/apps/complaints/ui_views.py @@ -1977,6 +1977,8 @@ def public_complaint_submit(request): severity="medium", # Default, AI will update priority="medium", # Default, AI will update status="open", # Start as open + complaint_source_type=ComplaintSourceType.INTERNAL, + source=PXSource.objects.filter(name_en="Public Form").first(), reference_number=reference_number, # Location hierarchy (FK relationships) location=location, @@ -2112,15 +2114,39 @@ def public_complaint_track(request): except Complaint.DoesNotExist: error_message = _("No complaint found with this reference number. Please check and try again.") - # Get public updates only (exclude internal notes) + public_status = None public_updates = [] if complaint: - public_updates = complaint.updates.filter( - update_type__in=["status_change", "resolution", "communication"] - ).order_by("-created_at") + public_status = complaint.public_status + + public_updates = list( + complaint.updates.filter(update_type__in=["status_change", "resolution", "communication"]).order_by( + "-created_at" + ) + ) + + _status_map = { + "open": str(_("Received")), + "in_progress": str(_("In Progress")), + "partially_resolved": str(_("In Progress")), + "contacted": str(_("In Progress")), + "contacted_no_response": str(_("In Progress")), + "resolved": str(_("Resolved")), + "closed": str(_("Closed")), + "cancelled": str(_("Cancelled")), + } + for update in public_updates: + if update.comments: + for internal, public_label in _status_map.items(): + update.comments = update.comments.replace(internal, public_label) + if hasattr(update, "old_status") and update.old_status: + update.old_status = _status_map.get(update.old_status, update.old_status) + if hasattr(update, "new_status") and update.new_status: + update.new_status = _status_map.get(update.new_status, update.new_status) context = { "complaint": complaint, + "public_status": public_status, "public_updates": public_updates, "error_message": error_message, "reference_number": reference_number, @@ -2288,7 +2314,10 @@ def api_lookup_patient(request): lookup_method = None if national_id: - patient = Patient.objects.filter(national_id=national_id, status="active").first() + from apps.core.encryption import compute_national_id_hash + + nid_hash = compute_national_id_hash(national_id) + patient = Patient.objects.filter(national_id_hash=nid_hash, status="active").first() lookup_method = "national_id" if not patient and phone: diff --git a/apps/complaints/ui_views_oncall.py b/apps/complaints/ui_views_oncall.py index 2b67d91..6ce1295 100644 --- a/apps/complaints/ui_views_oncall.py +++ b/apps/complaints/ui_views_oncall.py @@ -28,8 +28,8 @@ logger = logging.getLogger(__name__) def check_px_admin(request): """Check if user is PX Admin, return redirect if not.""" if not request.user.is_px_admin(): - messages.error(request, _('You do not have permission to access this page.')) - return redirect('dashboard') + messages.error(request, _("You do not have permission to access this page.")) + return redirect("dashboard") return None @@ -41,15 +41,15 @@ def oncall_schedule_list(request): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - - schedules = OnCallAdminSchedule.objects.select_related('hospital').all() - + + schedules = OnCallAdminSchedule.objects.select_related("hospital").all() + context = { - 'schedules': schedules, - 'title': _('On-Call Admin Schedules'), + "schedules": schedules, + "title": _("On-Call Admin Schedules"), } - - return render(request, 'complaints/oncall/schedule_list.html', context) + + return render(request, "complaints/oncall/schedule_list.html", context) @login_required @@ -60,29 +60,29 @@ def oncall_schedule_create(request): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - - hospitals = Hospital.objects.filter(status='active') - - if request.method == 'POST': + + hospitals = Hospital.objects.filter(status="active") + + if request.method == "POST": try: # Parse working days from checkboxes working_days = [] for day in range(7): - if request.POST.get(f'working_day_{day}'): + if request.POST.get(f"working_day_{day}"): working_days.append(day) - + if not working_days: working_days = [0, 1, 2, 3, 4] # Default to Mon-Fri - + # Get form data - hospital_id = request.POST.get('hospital') + hospital_id = request.POST.get("hospital") hospital = Hospital.objects.get(id=hospital_id) if hospital_id else None - - work_start_time = request.POST.get('work_start_time', '08:00') - work_end_time = request.POST.get('work_end_time', '17:00') - timezone_str = request.POST.get('timezone', 'Asia/Riyadh') - is_active = request.POST.get('is_active') == 'on' - + + work_start_time = request.POST.get("work_start_time", "08:00") + work_end_time = request.POST.get("work_end_time", "17:00") + timezone_str = request.POST.get("timezone", "Asia/Riyadh") + is_active = request.POST.get("is_active") == "on" + # Create schedule schedule = OnCallAdminSchedule.objects.create( hospital=hospital, @@ -90,40 +90,48 @@ def oncall_schedule_create(request): work_start_time=work_start_time, work_end_time=work_end_time, timezone=timezone_str, - is_active=is_active + is_active=is_active, ) - + # Log audit AuditService.log_event( - event_type='oncall_schedule_created', + event_type="oncall_schedule_created", description=f"On-call schedule created: {schedule}", user=request.user, content_object=schedule, metadata={ - 'hospital': str(hospital) if hospital else 'system-wide', - 'working_days': working_days, - 'work_hours': f"{work_start_time}-{work_end_time}" - } + "hospital": str(hospital) if hospital else "system-wide", + "working_days": working_days, + "work_hours": f"{work_start_time}-{work_end_time}", + }, ) - - messages.success(request, _('On-call schedule created successfully.')) - return redirect('complaints:oncall_schedule_detail', pk=schedule.id) - + + messages.success(request, _("On-call schedule created successfully.")) + return redirect("complaints:oncall_schedule_detail", pk=schedule.id) + except Exception as e: logger.error(f"Error creating on-call schedule: {str(e)}") - messages.error(request, _('Error creating on-call schedule. Please try again.')) - + messages.error(request, _("Error creating on-call schedule. Please try again.")) + context = { - 'hospitals': hospitals, - 'timezones': [ - 'Asia/Riyadh', 'Asia/Dubai', 'Asia/Kuwait', 'Asia/Qatar', - 'Asia/Bahrain', 'Asia/Muscat', 'Asia/Amman', 'Asia/Beirut', - 'Asia/Cairo', 'Asia/Jerusalem', 'Asia/Baghdad' + "hospitals": hospitals, + "timezones": [ + "Asia/Riyadh", + "Asia/Dubai", + "Asia/Kuwait", + "Asia/Qatar", + "Asia/Bahrain", + "Asia/Muscat", + "Asia/Amman", + "Asia/Beirut", + "Asia/Cairo", + "Asia/Jerusalem", + "Asia/Baghdad", ], - 'title': _('Create On-Call Schedule'), + "title": _("Create On-Call Schedule"), } - - return render(request, 'complaints/oncall/schedule_form.html', context) + + return render(request, "complaints/oncall/schedule_form.html", context) @login_required @@ -134,25 +142,22 @@ def oncall_schedule_detail(request, pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - - schedule = get_object_or_404( - OnCallAdminSchedule.objects.select_related('hospital'), - pk=pk - ) - - on_call_admins = schedule.on_call_admins.select_related('admin_user').all() - + + schedule = get_object_or_404(OnCallAdminSchedule.objects.select_related("hospital"), pk=pk) + + on_call_admins = schedule.on_call_admins.select_related("admin_user").all() + # Check if currently working hours is_working_hours = schedule.is_working_time() - + context = { - 'schedule': schedule, - 'on_call_admins': on_call_admins, - 'is_working_hours': is_working_hours, - 'title': _('On-Call Schedule Details'), + "schedule": schedule, + "on_call_admins": on_call_admins, + "is_working_hours": is_working_hours, + "title": _("On-Call Schedule Details"), } - - return render(request, 'complaints/oncall/schedule_detail.html', context) + + return render(request, "complaints/oncall/schedule_detail.html", context) @login_required @@ -163,65 +168,73 @@ def oncall_schedule_edit(request, pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - + schedule = get_object_or_404(OnCallAdminSchedule, pk=pk) - hospitals = Hospital.objects.filter(status='active') - - if request.method == 'POST': + hospitals = Hospital.objects.filter(status="active") + + if request.method == "POST": try: # Parse working days from checkboxes working_days = [] for day in range(7): - if request.POST.get(f'working_day_{day}'): + if request.POST.get(f"working_day_{day}"): working_days.append(day) - + if not working_days: working_days = [0, 1, 2, 3, 4] # Default to Mon-Fri - + # Get form data - hospital_id = request.POST.get('hospital') + hospital_id = request.POST.get("hospital") schedule.hospital = Hospital.objects.get(id=hospital_id) if hospital_id else None - + schedule.working_days = working_days - schedule.work_start_time = request.POST.get('work_start_time', '08:00') - schedule.work_end_time = request.POST.get('work_end_time', '17:00') - schedule.timezone = request.POST.get('timezone', 'Asia/Riyadh') - schedule.is_active = request.POST.get('is_active') == 'on' - + schedule.work_start_time = request.POST.get("work_start_time", "08:00") + schedule.work_end_time = request.POST.get("work_end_time", "17:00") + schedule.timezone = request.POST.get("timezone", "Asia/Riyadh") + schedule.is_active = request.POST.get("is_active") == "on" + schedule.save() - + # Log audit AuditService.log_event( - event_type='oncall_schedule_updated', + event_type="oncall_schedule_updated", description=f"On-call schedule updated: {schedule}", user=request.user, content_object=schedule, metadata={ - 'hospital': str(schedule.hospital) if schedule.hospital else 'system-wide', - 'working_days': working_days, - 'is_active': schedule.is_active - } + "hospital": str(schedule.hospital) if schedule.hospital else "system-wide", + "working_days": working_days, + "is_active": schedule.is_active, + }, ) - - messages.success(request, _('On-call schedule updated successfully.')) - return redirect('complaints:oncall_schedule_detail', pk=schedule.id) - + + messages.success(request, _("On-call schedule updated successfully.")) + return redirect("complaints:oncall_schedule_detail", pk=schedule.id) + except Exception as e: logger.error(f"Error updating on-call schedule: {str(e)}") - messages.error(request, _('Error updating on-call schedule. Please try again.')) - + messages.error(request, _("Error updating on-call schedule. Please try again.")) + context = { - 'schedule': schedule, - 'hospitals': hospitals, - 'timezones': [ - 'Asia/Riyadh', 'Asia/Dubai', 'Asia/Kuwait', 'Asia/Qatar', - 'Asia/Bahrain', 'Asia/Muscat', 'Asia/Amman', 'Asia/Beirut', - 'Asia/Cairo', 'Asia/Jerusalem', 'Asia/Baghdad' + "schedule": schedule, + "hospitals": hospitals, + "timezones": [ + "Asia/Riyadh", + "Asia/Dubai", + "Asia/Kuwait", + "Asia/Qatar", + "Asia/Bahrain", + "Asia/Muscat", + "Asia/Amman", + "Asia/Beirut", + "Asia/Cairo", + "Asia/Jerusalem", + "Asia/Baghdad", ], - 'title': _('Edit On-Call Schedule'), + "title": _("Edit On-Call Schedule"), } - - return render(request, 'complaints/oncall/schedule_form.html', context) + + return render(request, "complaints/oncall/schedule_form.html", context) @login_required @@ -233,29 +246,29 @@ def oncall_schedule_delete(request, pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - + schedule = get_object_or_404(OnCallAdminSchedule, pk=pk) - + try: # Log before deletion AuditService.log_event( - event_type='oncall_schedule_deleted', + event_type="oncall_schedule_deleted", description=f"On-call schedule deleted: {schedule}", user=request.user, metadata={ - 'hospital': str(schedule.hospital) if schedule.hospital else 'system-wide', - 'schedule_id': str(pk) - } + "hospital": str(schedule.hospital) if schedule.hospital else "system-wide", + "schedule_id": str(pk), + }, ) - + schedule.delete() - messages.success(request, _('On-call schedule deleted successfully.')) - + messages.success(request, _("On-call schedule deleted successfully.")) + except Exception as e: logger.error(f"Error deleting on-call schedule: {str(e)}") - messages.error(request, _('Error deleting on-call schedule.')) - - return redirect('complaints:oncall_schedule_list') + messages.error(request, _("Error deleting on-call schedule.")) + + return redirect("complaints:oncall_schedule_list") @login_required @@ -266,70 +279,77 @@ def oncall_admin_add(request, schedule_pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - + schedule = get_object_or_404(OnCallAdminSchedule, pk=schedule_pk) - + # Get all PX Admins not already on this schedule - existing_admin_ids = schedule.on_call_admins.values_list('admin_user_id', flat=True) - available_admins = User.objects.filter( - groups__name='PX Admin', - is_active=True - ).exclude(id__in=existing_admin_ids) - - if request.method == 'POST': + existing_admin_ids = schedule.on_call_admins.values_list("admin_user_id", flat=True) + available_admins = ( + User.objects.filter( + Q(groups__name="PX Admin") | Q(groups__name="PX Coordinator") | Q(groups__name="Hospital Admin"), + is_active=True, + ) + .exclude(id__in=existing_admin_ids) + .distinct() + ) + + if request.method == "POST": try: - admin_user_id = request.POST.get('admin_user') + admin_user_id = request.POST.get("admin_user") if not admin_user_id: - messages.error(request, _('Please select an admin user.')) - return redirect('complaints:oncall_admin_add', schedule_pk=schedule_pk) - + messages.error(request, _("Please select an admin user.")) + return redirect("complaints:oncall_admin_add", schedule_pk=schedule_pk) + admin_user = User.objects.get(id=admin_user_id) - + # Parse dates - start_date = request.POST.get('start_date') or None - end_date = request.POST.get('end_date') or None - + start_date = request.POST.get("start_date") or None + end_date = request.POST.get("end_date") or None + # Create on-call admin assignment on_call_admin = OnCallAdmin.objects.create( schedule=schedule, admin_user=admin_user, start_date=start_date, end_date=end_date, - notification_priority=int(request.POST.get('notification_priority', 1)), - is_active=request.POST.get('is_active') == 'on', - notify_email=request.POST.get('notify_email') == 'on', - notify_sms=request.POST.get('notify_sms') == 'on', - sms_phone=request.POST.get('sms_phone', '') + notification_priority=int(request.POST.get("notification_priority", 1)), + is_active=request.POST.get("is_active") == "on", + notify_email=request.POST.get("notify_email") == "on", + notify_sms=request.POST.get("notify_sms") == "on", + sms_phone=request.POST.get("sms_phone", ""), ) - + # Log audit AuditService.log_event( - event_type='oncall_admin_added', + event_type="oncall_admin_added", description=f"Admin {admin_user.get_full_name()} added to on-call schedule", user=request.user, content_object=on_call_admin, metadata={ - 'schedule': str(schedule), - 'admin_user': str(admin_user), - 'start_date': start_date, - 'end_date': end_date - } + "schedule": str(schedule), + "admin_user": str(admin_user), + "start_date": start_date, + "end_date": end_date, + }, ) - - messages.success(request, _('On-call admin added successfully.')) - return redirect('complaints:oncall_schedule_detail', pk=schedule_pk) - + + messages.success(request, _("On-call admin added successfully.")) + return redirect("complaints:oncall_schedule_detail", pk=schedule_pk) + except Exception as e: logger.error(f"Error adding on-call admin: {str(e)}") - messages.error(request, _('Error adding on-call admin. Please try again.')) - + messages.error(request, _("Error adding on-call admin. Please try again.")) + context = { - 'schedule': schedule, - 'available_admins': available_admins, - 'title': _('Add On-Call Admin'), + "schedule": schedule, + "available_admins": available_admins, + "available_px_admins": available_admins.filter(groups__name="PX Admin"), + "available_coordinators": available_admins.filter(groups__name="PX Coordinator"), + "available_hospital_admins": available_admins.filter(groups__name="Hospital Admin"), + "title": _("Add On-Call Admin"), } - - return render(request, 'complaints/oncall/admin_form.html', context) + + return render(request, "complaints/oncall/admin_form.html", context) @login_required @@ -340,56 +360,53 @@ def oncall_admin_edit(request, pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - - on_call_admin = get_object_or_404( - OnCallAdmin.objects.select_related('schedule', 'admin_user'), - pk=pk - ) - - if request.method == 'POST': + + on_call_admin = get_object_or_404(OnCallAdmin.objects.select_related("schedule", "admin_user"), pk=pk) + + if request.method == "POST": try: # Parse dates - start_date = request.POST.get('start_date') or None - end_date = request.POST.get('end_date') or None - + start_date = request.POST.get("start_date") or None + end_date = request.POST.get("end_date") or None + # Update fields on_call_admin.start_date = start_date on_call_admin.end_date = end_date - on_call_admin.notification_priority = int(request.POST.get('notification_priority', 1)) - on_call_admin.is_active = request.POST.get('is_active') == 'on' - on_call_admin.notify_email = request.POST.get('notify_email') == 'on' - on_call_admin.notify_sms = request.POST.get('notify_sms') == 'on' - on_call_admin.sms_phone = request.POST.get('sms_phone', '') - + on_call_admin.notification_priority = int(request.POST.get("notification_priority", 1)) + on_call_admin.is_active = request.POST.get("is_active") == "on" + on_call_admin.notify_email = request.POST.get("notify_email") == "on" + on_call_admin.notify_sms = request.POST.get("notify_sms") == "on" + on_call_admin.sms_phone = request.POST.get("sms_phone", "") + on_call_admin.save() - + # Log audit AuditService.log_event( - event_type='oncall_admin_updated', + event_type="oncall_admin_updated", description=f"On-call admin updated: {on_call_admin}", user=request.user, content_object=on_call_admin, metadata={ - 'schedule': str(on_call_admin.schedule), - 'admin_user': str(on_call_admin.admin_user), - 'is_active': on_call_admin.is_active - } + "schedule": str(on_call_admin.schedule), + "admin_user": str(on_call_admin.admin_user), + "is_active": on_call_admin.is_active, + }, ) - - messages.success(request, _('On-call admin updated successfully.')) - return redirect('complaints:oncall_schedule_detail', pk=on_call_admin.schedule.id) - + + messages.success(request, _("On-call admin updated successfully.")) + return redirect("complaints:oncall_schedule_detail", pk=on_call_admin.schedule.id) + except Exception as e: logger.error(f"Error updating on-call admin: {str(e)}") - messages.error(request, _('Error updating on-call admin. Please try again.')) - + messages.error(request, _("Error updating on-call admin. Please try again.")) + context = { - 'on_call_admin': on_call_admin, - 'schedule': on_call_admin.schedule, - 'title': _('Edit On-Call Admin'), + "on_call_admin": on_call_admin, + "schedule": on_call_admin.schedule, + "title": _("Edit On-Call Admin"), } - - return render(request, 'complaints/oncall/admin_form.html', context) + + return render(request, "complaints/oncall/admin_form.html", context) @login_required @@ -401,34 +418,31 @@ def oncall_admin_delete(request, pk): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - - on_call_admin = get_object_or_404( - OnCallAdmin.objects.select_related('schedule', 'admin_user'), - pk=pk - ) + + on_call_admin = get_object_or_404(OnCallAdmin.objects.select_related("schedule", "admin_user"), pk=pk) schedule_pk = on_call_admin.schedule.id - + try: # Log before deletion AuditService.log_event( - event_type='oncall_admin_removed', + event_type="oncall_admin_removed", description=f"Admin removed from on-call schedule: {on_call_admin}", user=request.user, metadata={ - 'schedule': str(on_call_admin.schedule), - 'admin_user': str(on_call_admin.admin_user), - 'oncall_admin_id': str(pk) - } + "schedule": str(on_call_admin.schedule), + "admin_user": str(on_call_admin.admin_user), + "oncall_admin_id": str(pk), + }, ) - + on_call_admin.delete() - messages.success(request, _('On-call admin removed successfully.')) - + messages.success(request, _("On-call admin removed successfully.")) + except Exception as e: logger.error(f"Error removing on-call admin: {str(e)}") - messages.error(request, _('Error removing on-call admin.')) - - return redirect('complaints:oncall_schedule_detail', pk=schedule_pk) + messages.error(request, _("Error removing on-call admin.")) + + return redirect("complaints:oncall_schedule_detail", pk=schedule_pk) @login_required @@ -439,41 +453,43 @@ def oncall_dashboard(request): redirect_response = check_px_admin(request) if redirect_response: return redirect_response - + # Get all schedules - schedules = OnCallAdminSchedule.objects.select_related('hospital').all() - + schedules = OnCallAdminSchedule.objects.select_related("hospital").all() + # Get currently active on-call admins now = timezone.now() today = now.date() - - active_on_call_admins = OnCallAdmin.objects.filter( - is_active=True, - schedule__is_active=True - ).select_related('admin_user', 'schedule', 'schedule__hospital').filter( - Q(start_date__isnull=True) | Q(start_date__lte=today), - Q(end_date__isnull=True) | Q(end_date__gte=today) + + active_on_call_admins = ( + OnCallAdmin.objects.filter(is_active=True, schedule__is_active=True) + .select_related("admin_user", "schedule", "schedule__hospital") + .filter( + Q(start_date__isnull=True) | Q(start_date__lte=today), Q(end_date__isnull=True) | Q(end_date__gte=today) + ) ) - + # Check each schedule's current status schedule_statuses = [] for schedule in schedules: is_working = schedule.is_working_time() schedule_oncall = active_on_call_admins.filter(schedule=schedule) - - schedule_statuses.append({ - 'schedule': schedule, - 'is_working_hours': is_working, - 'on_call_count': schedule_oncall.count(), - 'on_call_admins': schedule_oncall - }) - + + schedule_statuses.append( + { + "schedule": schedule, + "is_working_hours": is_working, + "on_call_count": schedule_oncall.count(), + "on_call_admins": schedule_oncall, + } + ) + context = { - 'schedule_statuses': schedule_statuses, - 'total_schedules': schedules.count(), - 'total_active_oncall': active_on_call_admins.count(), - 'current_time': now, - 'title': _('On-Call Dashboard'), + "schedule_statuses": schedule_statuses, + "total_schedules": schedules.count(), + "total_active_oncall": active_on_call_admins.count(), + "current_time": now, + "title": _("On-Call Dashboard"), } - - return render(request, 'complaints/oncall/dashboard.html', context) + + return render(request, "complaints/oncall/dashboard.html", context) diff --git a/apps/core/config_urls.py b/apps/core/config_urls.py index df8fdbc..b7b3731 100644 --- a/apps/core/config_urls.py +++ b/apps/core/config_urls.py @@ -1,14 +1,17 @@ """ Configuration URLs """ + from django.urls import path from . import config_views -app_name = 'config' +app_name = "config" urlpatterns = [ - path('', config_views.config_dashboard, name='dashboard'), - path('sla/', config_views.sla_config_list, name='sla_config_list'), - path('routing/', config_views.routing_rules_list, name='routing_rules_list'), - path('test/',config_views.test, name='test'), + path("", config_views.config_dashboard, name="dashboard"), + path("sla/", config_views.sla_config_list, name="sla_config_list"), + path("routing/", config_views.routing_rules_list, name="routing_rules_list"), + path("users/", config_views.hospital_users_list, name="hospital_users_list"), + path("users//reset-password/", config_views.reset_user_password, name="reset_user_password"), + path("test/", config_views.test, name="test"), ] diff --git a/apps/core/config_views.py b/apps/core/config_views.py index 865b36c..8c2d391 100644 --- a/apps/core/config_views.py +++ b/apps/core/config_views.py @@ -2,15 +2,25 @@ Configuration Console UI views - System configuration management """ +import json + +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator -from django.shortcuts import render +from django.db.models import Q +from django.http import JsonResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.template.loader import render_to_string +from django.conf import settings +from django.utils.translation import gettext_lazy as _ -from apps.organizations.models import Hospital +from apps.organizations.models import Department, Hospital +from apps.organizations.services import StaffService from apps.px_action_center.models import PXActionSLAConfig, RoutingRule from apps.complaints.models import OnCallAdminSchedule from apps.callcenter.models import CallRecord -from apps.core.decorators import px_admin_required +from apps.notifications.services import NotificationService +from apps.core.decorators import px_admin_required, admin_required from apps.accounts.models import User @@ -25,6 +35,7 @@ def config_dashboard(request): oncall_schedules_count = OnCallAdminSchedule.objects.filter(is_active=True).count() call_records_count = CallRecord.objects.count() provisional_users_count = User.objects.filter(is_provisional=True).count() + active_users_count = User.objects.filter(is_active=True, is_superuser=False, is_provisional=False).count() context = { "sla_configs_count": sla_configs_count, @@ -33,6 +44,7 @@ def config_dashboard(request): "oncall_schedules_count": oncall_schedules_count, "call_records_count": call_records_count, "provisional_users_count": provisional_users_count, + "active_users_count": active_users_count, } return render(request, "config/dashboard.html", context) @@ -116,6 +128,136 @@ def routing_rules_list(request): return render(request, "config/routing_rules.html", context) +@admin_required +def hospital_users_list(request): + """Hospital users list view - PX Admin and Hospital Admin""" + + queryset = User.objects.select_related("hospital", "department").filter(is_superuser=False, is_provisional=False) + + if not request.user.is_px_admin(): + if request.tenant_hospital: + queryset = queryset.filter(hospital=request.tenant_hospital) + else: + queryset = queryset.none() + + hospital_filter = request.GET.get("hospital") + if hospital_filter: + queryset = queryset.filter(hospital_id=hospital_filter) + + role_filter = request.GET.get("role") + if role_filter: + queryset = queryset.filter(groups__name=role_filter) + + is_active = request.GET.get("is_active") + if is_active == "true": + queryset = queryset.filter(is_active=True) + elif is_active == "false": + queryset = queryset.filter(is_active=False) + + search_query = request.GET.get("search") + if search_query: + queryset = queryset.filter( + Q(first_name__icontains=search_query) + | Q(last_name__icontains=search_query) + | Q(email__icontains=search_query) + | Q(employee_id__icontains=search_query) + ) + + queryset = queryset.order_by("-date_joined") + + page_size = int(request.GET.get("page_size", 25)) + paginator = Paginator(queryset, page_size) + page_number = request.GET.get("page", 1) + page_obj = paginator.get_page(page_number) + + if request.user.is_px_admin(): + hospitals = Hospital.objects.filter(status="active").order_by("name") + elif request.tenant_hospital: + hospitals = Hospital.objects.filter(id=request.tenant_hospital.id) + else: + hospitals = Hospital.objects.none() + + from django.contrib.auth.models import Group + + roles = Group.objects.all().order_by("name") + + total_users = paginator.count + active_count = queryset.filter(is_active=True).count() + inactive_count = queryset.filter(is_active=False).count() + + context = { + "page_obj": page_obj, + "users": page_obj.object_list, + "hospitals": hospitals, + "roles": roles, + "filters": request.GET, + "total_users": total_users, + "active_count": active_count, + "inactive_count": inactive_count, + } + + return render(request, "config/hospital_users.html", context) + + +@admin_required +def reset_user_password(request, user_id): + """Reset a user's password and send them the new credentials via email.""" + + if request.method != "POST": + return JsonResponse({"error": "Method not allowed"}, status=405) + + target_user = get_object_or_404(User, pk=user_id, is_superuser=False) + + if not request.user.is_px_admin(): + if request.tenant_hospital and target_user.hospital != request.tenant_hospital: + return JsonResponse({"error": "You can only reset passwords for users in your hospital."}, status=403) + + new_password = StaffService.generate_password() + target_user.set_password(new_password) + target_user.save(update_fields=["password"]) + + login_url = f"{request.scheme}://{request.get_host()}/accounts/login/" + + html_message = render_to_string( + "config/emails/reset_password_email.html", + { + "user": target_user, + "password": new_password, + "login_url": login_url, + }, + request=request, + ) + + plain_message = ( + f"Dear {target_user.get_full_name()},\n\n" + f"Your password has been reset by an administrator.\n\n" + f"Your new credentials:\n" + f"Email: {target_user.email}\n" + f"Password: {new_password}\n\n" + f"Please login and change your password immediately.\n" + f"Login URL: {login_url}" + ) + + NotificationService.send_email( + email=target_user.email, + subject=_("Your PX360 Password Has Been Reset"), + message=plain_message, + html_message=html_message, + user=target_user, + notification_type="system", + ) + + return JsonResponse( + { + "success": True, + "message": f"Password has been reset for {target_user.get_full_name()}. A new password has been sent to {target_user.email}.", + "password": new_password, + "user_name": target_user.get_full_name(), + "user_email": target_user.email, + } + ) + + from django.views.decorators.csrf import csrf_exempt from rich import print diff --git a/apps/core/encryption.py b/apps/core/encryption.py new file mode 100644 index 0000000..ca931c7 --- /dev/null +++ b/apps/core/encryption.py @@ -0,0 +1,71 @@ +import base64 +import hashlib + +from cryptography.fernet import Fernet +from django.conf import settings +from django.db import models + + +def _get_fernet() -> Fernet: + key = hashlib.sha256(settings.SECRET_KEY.encode()).digest() + return Fernet(base64.urlsafe_b64encode(key)) + + +def encrypt_value(plaintext: str) -> str: + if not plaintext: + return "" + f = _get_fernet() + return f.encrypt(plaintext.encode()).decode() + + +def decrypt_value(ciphertext: str) -> str: + if not ciphertext: + return "" + f = _get_fernet() + return f.decrypt(ciphertext.encode()).decode() + + +def compute_national_id_hash(value: str) -> str: + if not value: + return "" + salt = settings.SECRET_KEY.encode() + return hashlib.sha256(salt + value.strip().encode()).hexdigest() + + +def mask_national_id(value: str) -> str: + if not value: + return "" + value = value.strip() + if len(value) <= 4: + return "*" * len(value) + return "*" * (len(value) - 4) + value[-4:] + + +class EncryptedCharField(models.CharField): + def get_prep_value(self, value): + if not value: + return value + return encrypt_value(str(value)) + + def from_db_value(self, value, expression, connection): + if not value: + return value + try: + return decrypt_value(value) + except Exception: + return value + + def to_python(self, value): + if not value: + return value + if isinstance(value, str): + try: + return decrypt_value(value) + except Exception: + return value + return value + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + path = "apps.core.encryption.EncryptedCharField" + return name, path, args, kwargs diff --git a/apps/core/management/commands/send_example_emails.py b/apps/core/management/commands/send_example_emails.py index 48d522f..85030c5 100644 --- a/apps/core/management/commands/send_example_emails.py +++ b/apps/core/management/commands/send_example_emails.py @@ -574,35 +574,19 @@ class Command(BaseCommand): def _send_complaint_notification_inline(self): """Example complaint notification (currently inline - no template)""" html_content = f""" - - - - -
-
-

Complaint Notification

-
-
-

Dear Staff Member,

-

A new complaint has been logged and requires your attention:

-
- Reference: #COMP-2026-0050
- Title: Cleanliness Issue in Ward 3B
- Patient: Mrs. Layla Ahmed
- Department: Inpatient Ward 3B
- Status: New -
-

Please review and take appropriate action.

-
-
- - +

Complaint Notification

+

Dear Staff Member,

+

A new complaint has been logged and requires your attention:

+
+

+ Reference: #COMP-2026-0050
+ Title: Cleanliness Issue in Ward 3B
+ Patient: Mrs. Layla Ahmed
+ Department: Inpatient Ward 3B
+ Status: New +

+
+

Please review and take appropriate action.

""" text_content = """ Complaint Notification @@ -624,34 +608,18 @@ class Command(BaseCommand): def _send_complaint_resolution_inline(self): """Example complaint resolution notification (currently inline - no template)""" html_content = f""" - - - - -
-
-

Complaint Resolved

-
-
-

Dear Patient,

-
- Your complaint has been resolved!

- Reference: #COMP-2026-0045
- Title: Appointment Scheduling Issue
- Resolution: We have improved our scheduling system and scheduled your appointment for next week. -
-

We hope you are satisfied with the resolution. If you have any concerns, please contact us.

-

Thank you for your feedback.

-
-
- - +

Complaint Resolved

+

Dear Patient,

+

Your complaint has been resolved!

+
+

+ Reference: #COMP-2026-0045
+ Title: Appointment Scheduling Issue
+ Resolution: We have improved our scheduling system and scheduled your appointment for next week. +

+
+

We hope you are satisfied with the resolution. If you have any concerns, please contact us.

+

Thank you for your feedback.

""" text_content = """ Complaint Resolved @@ -673,21 +641,7 @@ class Command(BaseCommand): def _send_admin_new_complaint_inline(self): """Example admin new complaint notification (currently inline - no template)""" html_content = f""" - - - - -
-
-

🚨 New Complaint Alert

-
-
+

🚨 New Complaint Alert

Dear Administrator,

A new high-priority complaint has been logged:

@@ -700,10 +654,7 @@ class Command(BaseCommand): Date: April 7, 2026

Immediate attention required. Please review and assign to appropriate staff.

-
-
- - + """ text_content = """ 🚨 New Complaint Alert @@ -727,21 +678,7 @@ class Command(BaseCommand): def _send_escalation_inline(self): """Example escalation notification (currently inline - no template)""" html_content = f""" - - - - -
-
-

⚠️ Complaint Escalated

-
-
+

⚠️ Complaint Escalated

Dear Manager,

Complaint has been escalated to your attention:

@@ -752,10 +689,7 @@ class Command(BaseCommand): Date: April 7, 2026

Please review this complaint and provide your intervention to ensure resolution.

-
-
- - + """ text_content = """ ⚠️ Complaint Escalated @@ -935,21 +869,7 @@ class Command(BaseCommand): def _send_survey_invitation(self): """Example survey invitation (currently inline - no HTML template)""" html_content = f""" - - - - -
-
-

Share Your Experience

-
-
+

Share Your Experience

Dear Valued Patient,

Thank you for visiting Al Hammadi Hospital. We would appreciate your feedback to help us improve our services.

Please take a few minutes to complete our satisfaction survey:

@@ -957,10 +877,7 @@ class Command(BaseCommand): Take Survey

Your feedback is important to us!

-
-
- - + """ text_content = """ Share Your Experience @@ -1004,21 +921,7 @@ class Command(BaseCommand): def _send_appreciation_notification(self): """Example appreciation notification (currently inline - no template)""" html_content = f""" - - - - -
-
-

🌟 Staff Appreciation

-
-
+

🌟 Staff Appreciation

Dear Team,

Excellent work recognized!

@@ -1028,10 +931,7 @@ class Command(BaseCommand): From: Patient Family Member

Congratulations on this recognition! Your dedication to patient care is truly appreciated.

-
-
- - + """ text_content = """ 🌟 Staff Appreciation @@ -1086,28 +986,12 @@ class Command(BaseCommand): def _send_explanation_requested(self): """Example explanation requested from settings service (currently inline)""" html_content = f""" - - - - -
-
-

Explanation Requested

-
-
+

Explanation Requested

Dear Staff Member,

An explanation has been requested for complaint #COMP-2026-0060.

Deadline: April 14, 2026

Please submit your explanation through the system.

-
-
- - + """ text_content = """ Explanation Requested @@ -1125,29 +1009,13 @@ class Command(BaseCommand): def _send_complaint_assigned(self): """Example complaint assigned from settings service (currently inline)""" html_content = f""" - - - - -
-
-

Complaint Assigned to You

-
-
+

Complaint Assigned to You

Dear Dr. Khalid,

A complaint has been assigned to you for review and response.

Complaint: #COMP-2026-0062 - Staff Attitude Issue

Department: Outpatient Clinic

Please review and provide your explanation.

-
-
- - + """ text_content = """ Complaint Assigned to You @@ -1166,29 +1034,13 @@ class Command(BaseCommand): def _send_complaint_status_changed(self): """Example complaint status changed from settings service (currently inline)""" html_content = f""" - - - - -
-
-

Complaint Status Updated

-
-
+

Complaint Status Updated

Dear Stakeholder,

The status of complaint #COMP-2026-0058 has been updated.

Previous Status: Under Review

New Status: Resolved

Resolution: Issue has been addressed with staff member. Apology issued to patient.

-
-
- - + """ text_content = """ Complaint Status Updated diff --git a/apps/core/templatetags/national_id_tags.py b/apps/core/templatetags/national_id_tags.py new file mode 100644 index 0000000..98e360e --- /dev/null +++ b/apps/core/templatetags/national_id_tags.py @@ -0,0 +1,10 @@ +from django import template + +from apps.core.encryption import mask_national_id + +register = template.Library() + + +@register.filter +def mask_id(value): + return mask_national_id(value) diff --git a/apps/integrations/services/his_adapter.py b/apps/integrations/services/his_adapter.py index e2c4506..bdb5a5a 100644 --- a/apps/integrations/services/his_adapter.py +++ b/apps/integrations/services/his_adapter.py @@ -231,9 +231,9 @@ class HISAdapter: """Get or create patient from HIS demographic data""" patient_id = patient_data.get("PatientID") mrn = patient_id - national_id = patient_data.get("SSN") - phone = patient_data.get("MobileNo") - email = patient_data.get("Email") + national_id = patient_data.get("SSN") or "" + phone = patient_data.get("MobileNo") or "" + email = patient_data.get("Email") or "" full_name = patient_data.get("PatientName") nationality = patient_data.get("PatientNationality", "") @@ -249,8 +249,8 @@ class HISAdapter: if patient: patient.first_name = first_name patient.last_name = last_name - patient.national_id = national_id - patient.phone = phone + patient.national_id = national_id or "" + patient.phone = phone or "" if email is not None: patient.email = email patient.date_of_birth = date_of_birth @@ -262,7 +262,10 @@ class HISAdapter: mrn_taken = Patient.objects.filter(mrn=mrn).exists() if mrn_taken and national_id: - patient = Patient.objects.filter(national_id=national_id).first() + from apps.core.encryption import compute_national_id_hash + + nid_hash = compute_national_id_hash(national_id) + patient = Patient.objects.filter(national_id_hash=nid_hash).first() if patient: patient.mrn = mrn patient.primary_hospital = hospital @@ -288,8 +291,8 @@ class HISAdapter: first_name=first_name, last_name=last_name, national_id=national_id, - phone=phone, - email=email if email else "", + phone=phone or "", + email=email or "", date_of_birth=date_of_birth, gender=gender, nationality=nationality, diff --git a/apps/integrations/services/his_client.py b/apps/integrations/services/his_client.py index b5b9c97..99915a8 100644 --- a/apps/integrations/services/his_client.py +++ b/apps/integrations/services/his_client.py @@ -32,17 +32,17 @@ class HISClient: Initialize HIS client. Args: - config: IntegrationConfig instance (optional). + config: IntegrationConfig instance (optional). Environment variables (.env) take priority. """ from django.conf import settings - + self.config = config self.session = requests.Session() # Load credentials from Django settings (which reads .env) - self.username = getattr(settings, 'HIS_API_USERNAME', '') - self.password = getattr(settings, 'HIS_API_PASSWORD', '') + self.username = getattr(settings, "HIS_API_USERNAME", "") + self.password = getattr(settings, "HIS_API_PASSWORD", "") def _get_default_config(self) -> Optional[IntegrationConfig]: """Get default active HIS configuration from database.""" @@ -55,14 +55,14 @@ class HISClient: def _get_headers(self) -> Dict[str, str]: """Get request headers with authentication.""" from django.conf import settings - + headers = { "Content-Type": "application/json", "Accept": "application/json", } # Priority 1: API key from Django settings - api_key = getattr(settings, 'HIS_API_KEY', None) + api_key = getattr(settings, "HIS_API_KEY", None) if api_key: headers["X-API-Key"] = api_key headers["Authorization"] = f"Bearer {api_key}" @@ -76,16 +76,16 @@ class HISClient: def _get_api_url(self) -> Optional[str]: """Get API URL from Django settings (priority) or fallback.""" from django.conf import settings - + # Priority 1: Django settings (.env file) - settings_url = getattr(settings, 'HIS_API_URL', None) + settings_url = getattr(settings, "HIS_API_URL", None) if settings_url: return settings_url - + # Priority 2: Database config (if explicitly passed) if self.config and self.config.api_url: return self.config.api_url - + # Priority 3: Hardcoded default return "https://his.alhammadi.med.sa/SSRCE/API/FetchPatientVisitTimeStamps" @@ -159,18 +159,18 @@ class HISClient: def fetch_doctor_ratings(self, from_date: datetime, to_date: datetime) -> Optional[Dict]: """ - Fetch doctor ratings from HIS FetchDoctorRatingMAPI1 endpoint. + Fetch doctor ratings from HIS FetchDoctorRatingMAPI endpoint. Args: from_date: Start date for ratings to_date: End date for ratings Returns: - HIS response dict with FetchDoctorRatingMAPI1List or None on error + HIS response dict with FetchDoctorRatingMAPIList or None on error """ from django.conf import settings - - api_url = getattr(settings, 'HIS_RATINGS_API_URL', None) + + api_url = getattr(settings, "HIS_RATINGS_API_URL", None) if not api_url: logger.error("HIS_RATINGS_API_URL not configured in Django settings") return None @@ -192,11 +192,17 @@ class HISClient: ) response.raise_for_status() + logger.info(f"HIS doctor ratings response status: {response.status_code}") + logger.debug(f"HIS doctor ratings response body: {response.text[:2000]}") + data = response.json() if isinstance(data, dict): + logger.info(f"HIS doctor ratings response keys: {list(data.keys())}") rating_count = len(data.get("FetchDoctorRatingMAPI1List", [])) logger.info(f"Fetched {rating_count} doctor ratings from HIS") + if rating_count == 0 and data: + logger.info(f"Full HIS response (no ratings): {data}") return data else: logger.error(f"Unexpected HIS response type: {type(data)}") @@ -390,12 +396,12 @@ class HISClientFactory: def get_all_active_clients() -> List[HISClient]: """Get all active HIS clients for multi-hospital setups.""" from django.conf import settings - + # Priority 1: If Django settings has HIS API URL, use it - if getattr(settings, 'HIS_API_URL', None): + if getattr(settings, "HIS_API_URL", None): logger.info("Using HIS API URL from Django settings (.env file)") return [HISClient()] - + # Priority 2: Fall back to database configs (for multi-hospital setups) configs = IntegrationConfig.objects.filter(source_system=SourceSystem.HIS, is_active=True) diff --git a/apps/integrations/tasks.py b/apps/integrations/tasks.py index 9edd537..b7b6dab 100644 --- a/apps/integrations/tasks.py +++ b/apps/integrations/tasks.py @@ -1,209 +1,20 @@ """ Integrations Celery tasks -This module contains the core event processing logic that: -1. Processes inbound events from external systems -2. Finds matching journey instances -3. Completes journey stages -4. Triggers survey creation -5. Fetches surveys from HIS systems (every 5 minutes) +This module contains tasks for: +1. Fetching surveys from HIS systems (every 25 minutes) +2. Testing HIS connection """ import logging from datetime import datetime from celery import shared_task -from django.db import transaction from django.utils import timezone logger = logging.getLogger("apps.integrations") -@shared_task(bind=True, max_retries=3) -def process_inbound_event(self, event_id): - """ - Process an inbound integration event. - - This is the core event processing task that: - 1. Finds the journey instance by encounter_id - 2. Finds the matching stage by trigger_event_code - 3. Completes the stage - 4. Creates survey instance if configured - 5. Logs audit events - - Args: - event_id: UUID of the InboundEvent to process - - Returns: - dict: Processing result with status and details - """ - from apps.core.services import create_audit_log - from apps.integrations.models import InboundEvent - from apps.journeys.models import PatientJourneyInstance, PatientJourneyStageInstance, StageStatus - from apps.organizations.models import Department, Staff - - try: - # Get the event - event = InboundEvent.objects.get(id=event_id) - event.mark_processing() - - logger.info(f"Processing event {event.id}: {event.event_code} for encounter {event.encounter_id}") - - # Find journey instance by encounter_id - try: - journey_instance = PatientJourneyInstance.objects.select_related( - "journey_template", "patient", "hospital" - ).get(encounter_id=event.encounter_id) - except PatientJourneyInstance.DoesNotExist: - error_msg = f"No journey instance found for encounter {event.encounter_id}" - logger.warning(error_msg) - event.mark_ignored(error_msg) - return {"status": "ignored", "reason": error_msg} - - # Find matching stage by trigger_event_code - matching_stages = journey_instance.stage_instances.filter( - stage_template__trigger_event_code=event.event_code, - status__in=[StageStatus.PENDING, StageStatus.IN_PROGRESS], - ).select_related("stage_template") - - if not matching_stages.exists(): - error_msg = f"No pending stage found with trigger {event.event_code}" - logger.warning(error_msg) - event.mark_ignored(error_msg) - return {"status": "ignored", "reason": error_msg} - - # Get the first matching stage - stage_instance = matching_stages.first() - - # Extract staff and department from event payload - staff = None - department = None - - if event.physician_license: - try: - staff = Staff.objects.get(license_number=event.physician_license, hospital=journey_instance.hospital) - except Staff.DoesNotExist: - logger.warning(f"Staff member not found with license: {event.physician_license}") - - if event.department_code: - try: - department = Department.objects.get(code=event.department_code, hospital=journey_instance.hospital) - except Department.DoesNotExist: - logger.warning(f"Department not found: {event.department_code}") - - # Complete the stage - with transaction.atomic(): - success = stage_instance.complete( - event=event, staff=staff, department=department, metadata=event.payload_json - ) - - if success: - # Log stage completion - create_audit_log( - event_type="stage_completed", - description=f"Stage {stage_instance.stage_template.name} completed for encounter {event.encounter_id}", - content_object=stage_instance, - metadata={ - "event_code": event.event_code, - "stage_name": stage_instance.stage_template.name, - "journey_type": journey_instance.journey_template.journey_type, - }, - ) - - # Check if this is a discharge event - if event.event_code.upper() == "PATIENT_DISCHARGED": - logger.info(f"Discharge event received for encounter {event.encounter_id}") - - # Mark journey as completed - journey_instance.status = "completed" - journey_instance.completed_at = timezone.now() - journey_instance.save() - - # Check if post-discharge survey is enabled - if journey_instance.journey_template.send_post_discharge_survey: - logger.info( - f"Post-discharge survey enabled for journey {journey_instance.id}. " - f"Will send in {journey_instance.journey_template.post_discharge_survey_delay_hours} hour(s)" - ) - - # Queue post-discharge survey creation task with delay - from apps.surveys.tasks import create_post_discharge_survey - - delay_hours = journey_instance.journey_template.post_discharge_survey_delay_hours - delay_seconds = delay_hours * 3600 - - create_post_discharge_survey.apply_async( - args=[str(journey_instance.id)], countdown=delay_seconds - ) - - logger.info( - f"Queued post-discharge survey for journey {journey_instance.id} (delay: {delay_hours}h)" - ) - else: - logger.info(f"Post-discharge survey disabled for journey {journey_instance.id}") - - # Mark event as processed - event.mark_processed() - - logger.info( - f"Successfully processed event {event.id}: Completed stage {stage_instance.stage_template.name}" - ) - - return { - "status": "processed", - "stage_completed": stage_instance.stage_template.name, - "journey_completion": journey_instance.get_completion_percentage(), - } - else: - error_msg = "Failed to complete stage" - event.mark_failed(error_msg) - return {"status": "failed", "reason": error_msg} - - except InboundEvent.DoesNotExist: - error_msg = f"Event {event_id} not found" - logger.error(error_msg) - return {"status": "error", "reason": error_msg} - - except Exception as e: - error_msg = f"Error processing event: {str(e)}" - logger.error(error_msg, exc_info=True) - - try: - event.mark_failed(error_msg) - except: - pass - - # Retry the task - raise self.retry(exc=e, countdown=60 * (self.request.retries + 1)) - - -@shared_task -def process_pending_events(): - """ - Periodic task to process pending events. - - This task runs every minute (configured in config/celery.py) - and processes all pending events. - """ - from apps.integrations.models import InboundEvent - - pending_events = InboundEvent.objects.filter(status="pending").order_by("received_at")[ - :100 - ] # Process max 100 at a time - - processed_count = 0 - - for event in pending_events: - # Queue individual event for processing - process_inbound_event.delay(str(event.id)) - processed_count += 1 - - if processed_count > 0: - logger.info(f"Queued {processed_count} pending events for processing") - - return {"queued": processed_count} - - # ============================================================================= # HIS Survey Fetching Tasks # ============================================================================= diff --git a/apps/organizations/admin.py b/apps/organizations/admin.py index b9d4896..e9cf803 100644 --- a/apps/organizations/admin.py +++ b/apps/organizations/admin.py @@ -1,141 +1,170 @@ """ Organizations admin """ + from django.contrib import admin -from .models import Department, Hospital, Organization, Patient, Staff,Location,MainSection,SubSection +from .models import Department, Hospital, Organization, Patient, Staff, Location, MainSection, SubSection @admin.register(Organization) class OrganizationAdmin(admin.ModelAdmin): """Organization admin""" - list_display = ['name', 'code', 'city', 'status', 'created_at'] - list_filter = ['status', 'city'] - search_fields = ['name', 'name_ar', 'code', 'license_number'] - ordering = ['name'] + + list_display = ["name", "code", "city", "status", "created_at"] + list_filter = ["status", "city"] + search_fields = ["name", "name_ar", "code"] + ordering = ["name"] fieldsets = ( - (None, {'fields': ('name', 'name_ar', 'code')}), - ('Contact Information', {'fields': ('address', 'city', 'phone', 'email', 'website')}), - ('Details', {'fields': ('license_number', 'status', 'logo')}), - ('Metadata', {'fields': ('created_at', 'updated_at')}), + (None, {"fields": ("name", "name_ar", "code")}), + ("Contact Information", {"fields": ("address", "city", "phone", "email")}), + ("Details", {"fields": ("status", "preferred_language")}), + ("Metadata", {"fields": ("created_at", "updated_at")}), ) - readonly_fields = ['created_at', 'updated_at'] + readonly_fields = ["created_at", "updated_at"] @admin.register(Hospital) class HospitalAdmin(admin.ModelAdmin): """Hospital admin""" - list_display = ['name', 'code', 'city', 'ceo', 'status', 'capacity', 'created_at'] - list_filter = ['status', 'city'] - search_fields = ['name', 'name_ar', 'code', 'license_number'] - ordering = ['name'] + + list_display = ["name", "code", "city", "ceo", "status", "capacity", "created_at"] + list_filter = ["status", "city"] + search_fields = ["name", "name_ar", "code", "license_number"] + ordering = ["name"] fieldsets = ( - (None, {'fields': ('organization', 'name', 'name_ar', 'code')}), - ('Contact Information', {'fields': ('address', 'city', 'phone', 'email')}), - ('Executive Leadership', {'fields': ('ceo', 'medical_director', 'coo', 'cfo')}), - ('Details', {'fields': ('license_number', 'capacity', 'status')}), - ('Metadata', {'fields': ('created_at', 'updated_at')}), + (None, {"fields": ("organization", "name", "name_ar", "code")}), + ("Contact Information", {"fields": ("address", "city", "phone", "email")}), + ("Executive Leadership", {"fields": ("ceo", "medical_director", "coo", "cfo")}), + ("Details", {"fields": ("license_number", "capacity", "status")}), + ("Metadata", {"fields": ("created_at", "updated_at")}), ) - autocomplete_fields = ['organization', 'ceo', 'medical_director', 'coo', 'cfo'] + autocomplete_fields = ["organization", "ceo", "medical_director", "coo", "cfo"] - readonly_fields = ['created_at', 'updated_at'] + readonly_fields = ["created_at", "updated_at"] @admin.register(Department) class DepartmentAdmin(admin.ModelAdmin): """Department admin""" - list_display = ['name', 'hospital', 'code', 'manager', 'status', 'created_at'] - list_filter = ['status', 'hospital'] - search_fields = ['name', 'name_ar', 'code'] - ordering = ['hospital', 'name'] - autocomplete_fields = ['hospital', 'parent', 'manager'] + + list_display = ["name", "hospital", "code", "manager", "status", "created_at"] + list_filter = ["status", "hospital"] + search_fields = ["name", "name_ar", "code"] + ordering = ["hospital", "name"] + autocomplete_fields = ["hospital", "parent", "manager"] fieldsets = ( - (None, {'fields': ('hospital', 'name', 'name_ar', 'code')}), - ('Hierarchy', {'fields': ('parent', 'manager')}), - ('Contact', {'fields': ('phone', 'email', 'location')}), - ('Status', {'fields': ('status',)}), - ('Metadata', {'fields': ('created_at', 'updated_at')}), + (None, {"fields": ("hospital", "name", "name_ar", "code")}), + ("Hierarchy", {"fields": ("parent", "manager")}), + ("Contact", {"fields": ("phone", "email", "location")}), + ("Status", {"fields": ("status",)}), + ("Metadata", {"fields": ("created_at", "updated_at")}), ) - readonly_fields = ['created_at', 'updated_at'] + readonly_fields = ["created_at", "updated_at"] def get_queryset(self, request): qs = super().get_queryset(request) - return qs.select_related('hospital', 'manager', 'parent') + return qs.select_related("hospital", "manager", "parent") @admin.register(Staff) class StaffAdmin(admin.ModelAdmin): """Staff admin""" - list_display = ['__str__', 'staff_type', 'job_title', 'employee_id', 'hospital', 'department', 'email','phone', 'report_to', 'country', 'has_user_account', 'status'] - list_filter = ['status', 'hospital', 'staff_type', 'specialization', 'gender', 'country'] - search_fields = ['name', 'first_name', 'last_name', 'first_name_ar', 'last_name_ar', 'employee_id', 'license_number', 'job_title', 'phone', 'department_name', 'section'] - ordering = ['last_name', 'first_name'] - autocomplete_fields = ['hospital', 'department', 'user', 'report_to'] - actions = ['create_user_accounts', 'send_credentials_emails'] + + list_display = [ + "__str__", + "staff_type", + "job_title", + "employee_id", + "hospital", + "department", + "email", + "phone", + "report_to", + "country", + "has_user_account", + "status", + ] + list_filter = ["status", "hospital", "staff_type", "specialization", "gender", "country"] + search_fields = [ + "name", + "first_name", + "last_name", + "first_name_ar", + "last_name_ar", + "employee_id", + "license_number", + "job_title", + "phone", + "department_name", + "section", + ] + ordering = ["last_name", "first_name"] + autocomplete_fields = ["hospital", "department", "user", "report_to"] + actions = ["create_user_accounts", "send_credentials_emails"] fieldsets = ( - (None, {'fields': ('name', 'first_name', 'last_name', 'first_name_ar', 'last_name_ar')}), - ('Role', {'fields': ('staff_type', 'job_title')}), - ('Professional', {'fields': ('license_number', 'specialization', 'employee_id', 'email', 'phone')}), - ('Organization', {'fields': ('hospital', 'department', 'department_name', 'section', 'subsection', 'location')}), - ('Hierarchy', {'fields': ('report_to',)}), - ('Personal Information', {'fields': ('country', 'gender')}), - ('Account', {'fields': ('user',)}), - ('Status', {'fields': ('status',)}), - ('Metadata', {'fields': ('created_at', 'updated_at')}), + (None, {"fields": ("name", "first_name", "last_name", "first_name_ar", "last_name_ar")}), + ("Role", {"fields": ("staff_type", "job_title")}), + ("Professional", {"fields": ("license_number", "specialization", "employee_id", "email", "phone")}), + ( + "Organization", + {"fields": ("hospital", "department", "department_name", "section", "subsection", "location")}, + ), + ("Hierarchy", {"fields": ("report_to",)}), + ("Personal Information", {"fields": ("country", "gender")}), + ("Account", {"fields": ("user",)}), + ("Status", {"fields": ("status",)}), + ("Metadata", {"fields": ("created_at", "updated_at")}), ) - readonly_fields = ['created_at', 'updated_at'] + readonly_fields = ["created_at", "updated_at"] def get_queryset(self, request): qs = super().get_queryset(request) - return qs.select_related('hospital', 'department', 'user') + return qs.select_related("hospital", "department", "user") def has_user_account(self, obj): """Display user account status""" if obj.user: return '✓ Yes' return '✗ No' - has_user_account.short_description = 'User Account' + + has_user_account.short_description = "User Account" has_user_account.allow_tags = True def create_user_accounts(self, request, queryset): """Admin action to create user accounts for selected staff""" from .services import StaffService - + created = 0 failed = 0 for staff in queryset: if not staff.user and staff.email: try: role = StaffService.get_staff_type_role(staff.staff_type) - user, was_created, password = StaffService.create_user_for_staff( - staff, - role=role, - request=request - ) + user, was_created, password = StaffService.create_user_for_staff(staff, role=role, request=request) if was_created and password: StaffService.send_credentials_email(staff, password, request) created += 1 except Exception as e: failed += 1 - + self.message_user( - request, - f'Created {created} user accounts. Failed: {failed}', - level='success' if failed == 0 else 'warning' + request, f"Created {created} user accounts. Failed: {failed}", level="success" if failed == 0 else "warning" ) - create_user_accounts.short_description = 'Create user accounts for selected staff' + + create_user_accounts.short_description = "Create user accounts for selected staff" def send_credentials_emails(self, request, queryset): """Admin action to send credential emails to selected staff""" from .services import StaffService - + sent = 0 failed = 0 for staff in queryset: @@ -148,42 +177,55 @@ class StaffAdmin(admin.ModelAdmin): sent += 1 except Exception as e: failed += 1 - + self.message_user( - request, - f'Sent {sent} credential emails. Failed: {failed}', - level='success' if failed == 0 else 'warning' + request, f"Sent {sent} credential emails. Failed: {failed}", level="success" if failed == 0 else "warning" ) - send_credentials_emails.short_description = 'Send credential emails to selected staff' + + send_credentials_emails.short_description = "Send credential emails to selected staff" @admin.register(Patient) class PatientAdmin(admin.ModelAdmin): """Patient admin""" - list_display = ['get_full_name', 'mrn', 'national_id', 'phone', 'primary_hospital', 'status'] - list_filter = ['status', 'gender', 'primary_hospital', 'city'] - search_fields = ['mrn', 'national_id', 'first_name', 'last_name', 'first_name_ar', 'last_name_ar', 'phone', 'email'] - ordering = ['last_name', 'first_name'] - autocomplete_fields = ['primary_hospital'] + + list_display = ["get_full_name", "mrn", "get_masked_national_id", "phone", "primary_hospital", "status"] + list_filter = ["status", "gender", "primary_hospital", "city"] + search_fields = [ + "mrn", + "national_id_hash", + "first_name", + "last_name", + "first_name_ar", + "last_name_ar", + "phone", + "email", + ] + ordering = ["last_name", "first_name"] + autocomplete_fields = ["primary_hospital"] fieldsets = ( - (None, {'fields': ('mrn', 'national_id')}), - ('Personal Information', {'fields': ('first_name', 'last_name', 'first_name_ar', 'last_name_ar')}), - ('Demographics', {'fields': ('date_of_birth', 'gender')}), - ('Contact', {'fields': ('phone', 'email', 'address', 'city')}), - ('Hospital', {'fields': ('primary_hospital',)}), - ('Status', {'fields': ('status',)}), - ('Metadata', {'fields': ('created_at', 'updated_at')}), + (None, {"fields": ("mrn", "national_id")}), + ("Personal Information", {"fields": ("first_name", "last_name", "first_name_ar", "last_name_ar")}), + ("Demographics", {"fields": ("date_of_birth", "gender")}), + ("Contact", {"fields": ("phone", "email", "address", "city")}), + ("Hospital", {"fields": ("primary_hospital",)}), + ("Status", {"fields": ("status",)}), + ("Metadata", {"fields": ("created_at", "updated_at")}), ) - readonly_fields = ['created_at', 'updated_at'] + readonly_fields = ["created_at", "updated_at"] + + def get_masked_national_id(self, obj): + return obj.get_masked_national_id() + + get_masked_national_id.short_description = "National ID" def get_queryset(self, request): qs = super().get_queryset(request) - return qs.select_related('primary_hospital') - + return qs.select_related("primary_hospital") admin.site.register(Location) admin.site.register(MainSection) -admin.site.register(SubSection) \ No newline at end of file +admin.site.register(SubSection) diff --git a/apps/organizations/models.py b/apps/organizations/models.py index b80bf03..7f18d6a 100644 --- a/apps/organizations/models.py +++ b/apps/organizations/models.py @@ -5,6 +5,7 @@ Organizations models - Hospital, Department, Physician, Employee, Patient from django.db import models from django.utils.translation import gettext_lazy as _ +from apps.core.encryption import EncryptedCharField, compute_national_id_hash, mask_national_id from apps.core.models import TimeStampedModel, UUIDModel, StatusChoices @@ -412,7 +413,8 @@ class Patient(UUIDModel, TimeStampedModel): # Basic information mrn = models.CharField(max_length=50, unique=True, verbose_name="Medical Record Number") - national_id = models.CharField(max_length=50, blank=True, db_index=True) + national_id = EncryptedCharField(max_length=50, blank=True, default="") + national_id_hash = models.CharField(max_length=64, blank=True, db_index=True, default="") first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) @@ -449,6 +451,17 @@ class Patient(UUIDModel, TimeStampedModel): def get_full_name(self): return f"{self.first_name} {self.last_name}" + def get_masked_national_id(self): + return mask_national_id(self.national_id) + + def save(self, *args, **kwargs): + if self.national_id: + self.national_id_hash = compute_national_id_hash(self.national_id) + else: + self.national_id = self.national_id or "" + self.national_id_hash = "" + super().save(*args, **kwargs) + @staticmethod def generate_mrn(): """ diff --git a/apps/organizations/serializers.py b/apps/organizations/serializers.py index 8d50cfe..581f546 100644 --- a/apps/organizations/serializers.py +++ b/apps/organizations/serializers.py @@ -268,6 +268,8 @@ class PatientSerializer(serializers.ModelSerializer): primary_hospital_name = serializers.CharField(source="primary_hospital.name", read_only=True) full_name = serializers.CharField(source="get_full_name", read_only=True) age = serializers.SerializerMethodField() + national_id = serializers.SerializerMethodField() + national_id_masked = serializers.SerializerMethodField() class Meta: model = Patient @@ -275,6 +277,7 @@ class PatientSerializer(serializers.ModelSerializer): "id", "mrn", "national_id", + "national_id_masked", "first_name", "last_name", "first_name_ar", @@ -295,6 +298,15 @@ class PatientSerializer(serializers.ModelSerializer): ] read_only_fields = ["id", "created_at", "updated_at"] + def get_national_id(self, obj): + request = self.context.get("request") + if request and request.user and request.user.is_superuser: + return obj.national_id + return None + + def get_national_id_masked(self, obj): + return obj.get_masked_national_id() + def get_age(self, obj): """Calculate patient age""" if obj.date_of_birth: @@ -314,10 +326,11 @@ class PatientListSerializer(serializers.ModelSerializer): full_name = serializers.CharField(source="get_full_name", read_only=True) primary_hospital_name = serializers.CharField(source="primary_hospital.name", read_only=True) + national_id_masked = serializers.CharField(source="get_masked_national_id", read_only=True) class Meta: model = Patient - fields = ["id", "mrn", "full_name", "phone", "email", "primary_hospital_name", "status"] + fields = ["id", "mrn", "full_name", "national_id_masked", "phone", "email", "primary_hospital_name", "status"] class LocationSerializer(serializers.ModelSerializer): diff --git a/apps/organizations/ui_views.py b/apps/organizations/ui_views.py index c5589f3..2a0d480 100644 --- a/apps/organizations/ui_views.py +++ b/apps/organizations/ui_views.py @@ -324,11 +324,14 @@ def patient_list(request): # Search search_query = request.GET.get("search") if search_query: + from apps.core.encryption import compute_national_id_hash + + nid_hash = compute_national_id_hash(search_query) queryset = queryset.filter( Q(mrn__icontains=search_query) | Q(first_name__icontains=search_query) | Q(last_name__icontains=search_query) - | Q(national_id__icontains=search_query) + | Q(national_id_hash=nid_hash) | Q(phone__icontains=search_query) ) @@ -375,7 +378,7 @@ def patient_list(request): p.mrn, p.first_name, p.last_name, - p.national_id, + p.get_masked_national_id(), p.get_gender_display(), p.nationality, p.phone, diff --git a/apps/organizations/views.py b/apps/organizations/views.py index 02b679b..f90573d 100644 --- a/apps/organizations/views.py +++ b/apps/organizations/views.py @@ -610,7 +610,7 @@ class PatientViewSet(viewsets.ModelViewSet): queryset = Patient.objects.all() permission_classes = [IsAuthenticated] filterset_fields = ["status", "gender", "primary_hospital", "city", "primary_hospital__organization"] - search_fields = ["mrn", "national_id", "first_name", "last_name", "phone", "email"] + search_fields = ["mrn", "national_id_hash", "first_name", "last_name", "phone", "email"] ordering_fields = ["last_name", "created_at"] ordering = ["last_name", "first_name"] @@ -647,15 +647,27 @@ class PatientViewSet(viewsets.ModelViewSet): q = request.query_params.get("q", "").strip() queryset = self.get_queryset().filter(status="active") if q: + from apps.core.encryption import compute_national_id_hash + + nid_hash = compute_national_id_hash(q) queryset = queryset.filter( models.Q(mrn__icontains=q) | models.Q(first_name__icontains=q) | models.Q(last_name__icontains=q) - | models.Q(national_id__icontains=q) + | models.Q(national_id_hash=nid_hash) | models.Q(phone__icontains=q) ) queryset = queryset[:20] - data = [{"id": p.id, "first_name": p.first_name, "last_name": p.last_name, "mrn": p.mrn} for p in queryset] + data = [ + { + "id": p.id, + "first_name": p.first_name, + "last_name": p.last_name, + "mrn": p.mrn, + "national_id_masked": p.get_masked_national_id(), + } + for p in queryset + ] return Response(data) @@ -706,7 +718,7 @@ class SubSectionViewSet(viewsets.ReadOnlyModelViewSet): @permission_classes([]) def api_location_list(request): """API endpoint for location dropdown (public access)""" - locations = Location.objects.all().order_by("id") + locations = Location.objects.filter(id__in=[48, 49, 82, 110]).order_by("id") serializer = LocationSerializer(locations, many=True) return Response(serializer.data) diff --git a/apps/physicians/adapter.py b/apps/physicians/adapter.py index 429217b..f87c9ac 100644 --- a/apps/physicians/adapter.py +++ b/apps/physicians/adapter.py @@ -81,14 +81,19 @@ class DoctorRatingAdapter: formats = [ "%d-%b-%Y %H:%M:%S", + "%d-%b-%Y %H:%M", "%d-%b-%Y", "%d-%b-%y %H:%M:%S", + "%d-%b-%y %H:%M", "%d-%b-%y", "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M", "%Y-%m-%d", "%d/%m/%Y %H:%M:%S", + "%d/%m/%Y %H:%M", "%d/%m/%Y", "%m/%d/%Y %H:%M:%S", + "%m/%d/%Y %H:%M", "%m/%d/%Y", ] diff --git a/apps/physicians/forms.py b/apps/physicians/forms.py index 79e7e79..8b10735 100644 --- a/apps/physicians/forms.py +++ b/apps/physicians/forms.py @@ -3,6 +3,7 @@ Physicians Forms Forms for doctor rating imports and filtering. """ + from django import forms from apps.organizations.models import Hospital @@ -12,113 +13,146 @@ from apps.core.form_mixins import HospitalFieldMixin class DoctorRatingImportForm(HospitalFieldMixin, forms.Form): """ Form for importing doctor ratings from CSV. - + Hospital field visibility: - PX Admins: See dropdown with all hospitals - Others: Hidden field, auto-set to user's hospital """ + hospital = forms.ModelChoiceField( - queryset=Hospital.objects.filter(status='active'), + queryset=Hospital.objects.filter(status="active"), label="Hospital", - help_text="Select the hospital these ratings belong to" + help_text="Select the hospital these ratings belong to", ) - + csv_file = forms.FileField( label="CSV File", help_text="Upload the Doctor Rating Report CSV file", - widget=forms.FileInput(attrs={'accept': '.csv'}) + widget=forms.FileInput(attrs={"accept": ".csv"}), ) - + skip_header_rows = forms.IntegerField( label="Skip Header Rows", initial=6, min_value=0, max_value=20, - help_text="Number of rows to skip before the column headers (Doctor Rating Report typically has 6 header rows)" + help_text="Number of rows to skip before the column headers (Doctor Rating Report typically has 6 header rows)", ) - + def clean_csv_file(self): - csv_file = self.cleaned_data['csv_file'] - + csv_file = self.cleaned_data["csv_file"] + # Check file extension - if not csv_file.name.endswith('.csv'): + if not csv_file.name.endswith(".csv"): raise forms.ValidationError("File must be a CSV file (.csv extension)") - + # Check file size (max 10MB) if csv_file.size > 10 * 1024 * 1024: raise forms.ValidationError("File size must be less than 10MB") - + return csv_file +class DoctorRatingFetchForm(HospitalFieldMixin, forms.Form): + """ + Form for fetching doctor ratings from HIS API by date range. + + Hospital field visibility: + - PX Admins: See dropdown with all hospitals + - Others: Hidden field, auto-set to user's hospital + """ + + hospital = forms.ModelChoiceField( + queryset=Hospital.objects.filter(status="active"), + label="Hospital", + help_text="Select the hospital for tracking the import job", + ) + + from_date = forms.DateField( + label="From Date", + widget=forms.DateInput( + attrs={ + "type": "date", + "class": "w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy/20 focus:border-navy outline-none transition", + } + ), + help_text="Start date for fetching ratings", + ) + + to_date = forms.DateField( + label="To Date", + widget=forms.DateInput( + attrs={ + "type": "date", + "class": "w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy/20 focus:border-navy outline-none transition", + } + ), + help_text="End date for fetching ratings", + ) + + def clean(self): + cleaned_data = super().clean() + from_date = cleaned_data.get("from_date") + to_date = cleaned_data.get("to_date") + + if from_date and to_date and from_date > to_date: + raise forms.ValidationError("From date must be before or equal to to date.") + + return cleaned_data + + class DoctorRatingFilterForm(forms.Form): """ Form for filtering individual doctor ratings. """ + hospital = forms.ModelChoiceField( - queryset=Hospital.objects.filter(status='active'), - required=False, - label="Hospital" + queryset=Hospital.objects.filter(status="active"), required=False, label="Hospital" ) - + doctor_id = forms.CharField( - required=False, - label="Doctor ID", - widget=forms.TextInput(attrs={'placeholder': 'e.g., 10738'}) + required=False, label="Doctor ID", widget=forms.TextInput(attrs={"placeholder": "e.g., 10738"}) ) - + doctor_name = forms.CharField( - required=False, - label="Doctor Name", - widget=forms.TextInput(attrs={'placeholder': 'Search by doctor name'}) + required=False, label="Doctor Name", widget=forms.TextInput(attrs={"placeholder": "Search by doctor name"}) ) - + rating_min = forms.IntegerField( required=False, min_value=1, max_value=5, label="Min Rating", - widget=forms.NumberInput(attrs={'placeholder': '1-5'}) + widget=forms.NumberInput(attrs={"placeholder": "1-5"}), ) - + rating_max = forms.IntegerField( required=False, min_value=1, max_value=5, label="Max Rating", - widget=forms.NumberInput(attrs={'placeholder': '1-5'}) + widget=forms.NumberInput(attrs={"placeholder": "1-5"}), ) - - date_from = forms.DateField( - required=False, - label="From Date", - widget=forms.DateInput(attrs={'type': 'date'}) - ) - - date_to = forms.DateField( - required=False, - label="To Date", - widget=forms.DateInput(attrs={'type': 'date'}) - ) - + + date_from = forms.DateField(required=False, label="From Date", widget=forms.DateInput(attrs={"type": "date"})) + + date_to = forms.DateField(required=False, label="To Date", widget=forms.DateInput(attrs={"type": "date"})) + source = forms.ChoiceField( required=False, label="Source", - choices=[('', 'All Sources')] + [ - ('his_api', 'HIS API'), - ('csv_import', 'CSV Import'), - ('manual', 'Manual Entry') - ] + choices=[("", "All Sources")] + + [("his_api", "HIS API"), ("csv_import", "CSV Import"), ("manual", "Manual Entry")], ) - + def __init__(self, user, *args, **kwargs): super().__init__(*args, **kwargs) - + # Filter hospital choices based on user role if user.is_px_admin(): - self.fields['hospital'].queryset = Hospital.objects.filter(status='active') + self.fields["hospital"].queryset = Hospital.objects.filter(status="active") elif user.hospital: - self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) - self.fields['hospital'].initial = user.hospital + self.fields["hospital"].queryset = Hospital.objects.filter(id=user.hospital.id) + self.fields["hospital"].initial = user.hospital else: - self.fields['hospital'].queryset = Hospital.objects.none() + self.fields["hospital"].queryset = Hospital.objects.none() diff --git a/apps/physicians/import_views.py b/apps/physicians/import_views.py index 1083152..2b7e291 100644 --- a/apps/physicians/import_views.py +++ b/apps/physicians/import_views.py @@ -21,9 +21,9 @@ from apps.core.services import AuditService from apps.organizations.models import Hospital from .adapter import DoctorRatingAdapter -from .forms import DoctorRatingImportForm +from .forms import DoctorRatingFetchForm, DoctorRatingImportForm from .models import DoctorRatingImportJob, PhysicianIndividualRating -from .tasks import process_doctor_rating_job +from .tasks import _fetch_and_process_his_doctor_ratings, process_doctor_rating_job logger = logging.getLogger(__name__) @@ -226,6 +226,82 @@ def doctor_rating_import(request): return render(request, "physicians/doctor_rating_import.html", context) +@login_required +def doctor_rating_fetch(request): + """ + Fetch doctor ratings from HIS API by date range. + + Runs the fetch synchronously for immediate feedback, then redirects + to the job status page (which will already show completed results). + """ + user = request.user + + if not user.is_px_admin() and not user.is_hospital_admin(): + messages.error(request, "You don't have permission to fetch doctor ratings.") + return redirect("physicians:physician_list") + + if request.method == "POST": + form = DoctorRatingFetchForm(request.POST, request=request) + + if form.is_valid(): + try: + hospital = form.cleaned_data["hospital"] + from_date = form.cleaned_data["from_date"] + to_date = form.cleaned_data["to_date"] + + date_label = f"{from_date.isoformat()} to {to_date.isoformat()}" + + job = DoctorRatingImportJob.objects.create( + name=f"HIS Fetch - {date_label}", + status=DoctorRatingImportJob.JobStatus.PENDING, + source=DoctorRatingImportJob.JobSource.HIS_API, + created_by=user, + hospital=hospital, + ) + + AuditService.log_event( + event_type="doctor_rating_his_fetch", + description=f"Fetching HIS ratings for {date_label}", + user=user, + metadata={ + "job_id": str(job.id), + "hospital": hospital.name, + "from_date": from_date.isoformat(), + "to_date": to_date.isoformat(), + }, + ) + + result = _fetch_and_process_his_doctor_ratings(str(job.id), from_date.isoformat(), to_date.isoformat()) + + if result.get("success"): + total = result.get("total_ratings", 0) + if total == 0: + messages.info(request, f"No ratings found for {date_label}.") + else: + messages.success( + request, + f"Fetched {total} ratings from HIS: " + f"{result.get('success_count', 0)} imported, " + f"{result.get('duplicate_count', 0)} duplicates, " + f"{result.get('failed_count', 0)} failed.", + ) + else: + messages.error(request, f"HIS fetch failed: {result.get('error', 'Unknown error')}") + + return redirect("physicians:doctor_rating_job_status", job_id=job.id) + + except Exception as e: + logger.error(f"Error fetching HIS ratings: {str(e)}", exc_info=True) + messages.error(request, f"Error: {str(e)}") + else: + form = DoctorRatingFetchForm(request=request) + + context = { + "form": form, + } + return render(request, "physicians/doctor_rating_fetch.html", context) + + @login_required def doctor_rating_review(request): """ diff --git a/apps/physicians/management/commands/import_his_doctor_ratings.py b/apps/physicians/management/commands/import_his_doctor_ratings.py index 1a76280..9baaa12 100644 --- a/apps/physicians/management/commands/import_his_doctor_ratings.py +++ b/apps/physicians/management/commands/import_his_doctor_ratings.py @@ -1,7 +1,7 @@ """ Management command to import doctor ratings from HIS API. -This command fetches doctor ratings from the HIS FetchDoctorRatingMAPI1 endpoint +This command fetches doctor ratings from the HIS FetchDoctorRatingMAPI endpoint and imports them into the system. It supports importing for specific months, multiple months, or full historical data. diff --git a/apps/physicians/tasks.py b/apps/physicians/tasks.py index 867ef00..23ae238 100644 --- a/apps/physicians/tasks.py +++ b/apps/physicians/tasks.py @@ -8,6 +8,7 @@ Background tasks for: """ import logging +from typing import Dict from celery import shared_task from django.utils import timezone @@ -249,84 +250,81 @@ def cleanup_old_import_jobs(days: int = 30): return {"cleaned_count": count} -@shared_task(bind=True, max_retries=3, default_retry_delay=300) -def fetch_his_doctor_ratings_monthly(self): +def _fetch_and_process_his_doctor_ratings(job_id: str, from_date_iso: str, to_date_iso: str) -> Dict: """ - Monthly task to fetch doctor ratings from HIS API. + Core logic to fetch and process HIS doctor ratings. - Runs on the 1st of each month to fetch the previous month's ratings. - Example: On March 1st, fetches all ratings from February 1-28/29. - - This task runs at 1:00 AM on the 1st of each month, before the - aggregation task which runs at 2:00 AM. + Can be called synchronously (from a view) or wrapped in a Celery task. """ from datetime import datetime - from calendar import monthrange + + from apps.integrations.services.his_client import HISClient try: - # Calculate previous month - now = timezone.now() - if now.month == 1: - target_year = now.year - 1 - target_month = 12 - else: - target_year = now.year - target_month = now.month - 1 + job = DoctorRatingImportJob.objects.get(id=job_id) + except DoctorRatingImportJob.DoesNotExist: + logger.error(f"Doctor rating import job {job_id} not found") + return {"error": "Job not found"} - month_label = f"{target_year}-{target_month:02d}" - logger.info(f"Starting monthly HIS doctor rating fetch for {month_label}") + try: + from_date = datetime.fromisoformat(from_date_iso) + to_date = datetime.fromisoformat(to_date_iso) + if to_date.hour == 0 and to_date.minute == 0 and to_date.second == 0: + to_date = to_date.replace(hour=23, minute=59, second=59) - # Calculate date range for the month - from_date = datetime(target_year, target_month, 1) - last_day = monthrange(target_year, target_month)[1] - to_date = datetime(target_year, target_month, last_day, 23, 59, 59) + date_label = f"{from_date_iso} to {to_date_iso}" + logger.info(f"Starting HIS doctor rating fetch for {date_label}") - # Initialize HIS client - from apps.integrations.services.his_client import HISClient + job.status = DoctorRatingImportJob.JobStatus.PROCESSING + job.started_at = timezone.now() + job.save() client = HISClient() - - # Fetch ratings from HIS his_data = client.fetch_doctor_ratings(from_date, to_date) if not his_data: + job.status = DoctorRatingImportJob.JobStatus.FAILED + job.error_message = "Failed to fetch data from HIS API" + job.completed_at = timezone.now() + job.save() logger.error("Failed to fetch data from HIS API") - return {"success": False, "error": "Failed to fetch data from HIS API", "month": month_label} + return {"success": False, "error": "Failed to fetch data from HIS API", "date_range": date_label} if his_data.get("Code") != 200: error_msg = his_data.get("Message", "Unknown error") + job.status = DoctorRatingImportJob.JobStatus.FAILED + job.error_message = f"HIS API error: {error_msg}" + job.completed_at = timezone.now() + job.save() logger.error(f"HIS API error: {error_msg}") - return {"success": False, "error": f"HIS API error: {error_msg}", "month": month_label} + return {"success": False, "error": f"HIS API error: {error_msg}", "date_range": date_label} ratings_list = his_data.get("FetchDoctorRatingMAPI1List", []) if not ratings_list: - logger.info(f"No ratings found for {month_label}") + logger.info( + f"HIS returned no ratings. Response keys: {list(his_data.keys())}, Code: {his_data.get('Code')}, Message: {his_data.get('Message')}" + ) + logger.info(f"Full HIS response: {his_data}") + job.status = DoctorRatingImportJob.JobStatus.COMPLETED + job.total_records = 0 + job.processed_count = 0 + job.completed_at = timezone.now() + job.results = {"stats": {"total": 0, "success": 0, "failed": 0, "duplicates": 0, "staff_matched": 0}} + job.save() + logger.info(f"No ratings found for {date_label}") return { "success": True, - "month": month_label, + "date_range": date_label, "total_ratings": 0, "message": "No ratings found for this period", } - logger.info(f"Fetched {len(ratings_list)} ratings from HIS for {month_label}") + logger.info(f"Fetched {len(ratings_list)} ratings from HIS for {date_label}") - # Create import job for tracking - first_hospital = Hospital.objects.first() - if first_hospital: - job = DoctorRatingImportJob.objects.create( - name=f"Monthly HIS Import - {month_label}", - status=DoctorRatingImportJob.JobStatus.PROCESSING, - source=DoctorRatingImportJob.JobSource.HIS_API, - hospital=first_hospital, - total_records=len(ratings_list), - started_at=timezone.now(), - ) - else: - job = None - logger.warning("No hospitals found, creating ratings without import job") + job.total_records = len(ratings_list) + job.save() - # Process ratings stats = { "total": len(ratings_list), "success": 0, @@ -337,7 +335,6 @@ def fetch_his_doctor_ratings_monthly(self): for idx, rating_data in enumerate(ratings_list, 1): try: - # Find hospital by name hospital_name = rating_data.get("HospitalName", "") hospital = Hospital.objects.filter(name__iexact=hospital_name).first() @@ -349,7 +346,6 @@ def fetch_his_doctor_ratings_monthly(self): logger.warning(f"Hospital not found: {hospital_name}") continue - # Process the rating result = DoctorRatingAdapter.process_his_rating_record(rating_data, hospital) if result["is_duplicate"]: @@ -362,8 +358,7 @@ def fetch_his_doctor_ratings_monthly(self): stats["failed"] += 1 logger.warning(f"Failed to process rating: {result.get('message')}") - # Update job progress every 100 records - if job and idx % 100 == 0: + if idx % 100 == 0: job.processed_count = idx job.success_count = stats["success"] job.failed_count = stats["failed"] @@ -374,31 +369,29 @@ def fetch_his_doctor_ratings_monthly(self): stats["failed"] += 1 logger.error(f"Error processing rating {idx}: {e}", exc_info=True) - # Finalize job - if job: - job.processed_count = stats["total"] - job.success_count = stats["success"] - job.failed_count = stats["failed"] - job.completed_at = timezone.now() + job.processed_count = stats["total"] + job.success_count = stats["success"] + job.failed_count = stats["failed"] + job.completed_at = timezone.now() - if stats["failed"] == 0: - job.status = DoctorRatingImportJob.JobStatus.COMPLETED - elif stats["success"] == 0: - job.status = DoctorRatingImportJob.JobStatus.FAILED - else: - job.status = DoctorRatingImportJob.JobStatus.PARTIAL + if stats["failed"] == 0: + job.status = DoctorRatingImportJob.JobStatus.COMPLETED + elif stats["success"] == 0: + job.status = DoctorRatingImportJob.JobStatus.FAILED + else: + job.status = DoctorRatingImportJob.JobStatus.PARTIAL - job.results = {"stats": stats} - job.save() + job.results = {"stats": stats} + job.save() logger.info( - f"Completed monthly HIS doctor rating fetch for {month_label}: " + f"Completed HIS doctor rating fetch for {date_label}: " f"{stats['success']} success, {stats['failed']} failed, {stats['duplicates']} duplicates" ) return { "success": True, - "month": month_label, + "date_range": date_label, "total_ratings": stats["total"], "success_count": stats["success"], "failed_count": stats["failed"], @@ -407,6 +400,117 @@ def fetch_his_doctor_ratings_monthly(self): } except Exception as exc: - logger.error(f"Error in monthly HIS doctor rating fetch: {exc}", exc_info=True) - # Retry the task + logger.error(f"Error in HIS doctor rating fetch: {exc}", exc_info=True) + try: + job.status = DoctorRatingImportJob.JobStatus.FAILED + job.error_message = str(exc) + job.completed_at = timezone.now() + job.save() + except Exception: + pass + raise + + +@shared_task(bind=True, max_retries=3, default_retry_delay=300) +def fetch_his_doctor_ratings(self, job_id: str, from_date_iso: str, to_date_iso: str): + """ + Celery task wrapper for fetching and processing HIS doctor ratings. + + Used by the monthly scheduled task. For manual UI fetches, the view + calls _fetch_and_process_his_doctor_ratings() directly. + """ + try: + return _fetch_and_process_his_doctor_ratings(job_id, from_date_iso, to_date_iso) + except Exception as exc: + raise self.retry(exc=exc) + + +@shared_task(bind=True, max_retries=3, default_retry_delay=300) +def fetch_his_doctor_ratings_monthly(self): + """ + Monthly task to fetch doctor ratings from HIS API. + + Runs on the 1st of each month to fetch the previous month's ratings. + Example: On March 1st, fetches all ratings from February 1-28/29. + + This task runs at 1:00 AM on the 1st of each month, before the + aggregation task which runs at 2:00 AM. + """ + from calendar import monthrange + from datetime import datetime + + try: + now = timezone.now() + if now.month == 1: + target_year = now.year - 1 + target_month = 12 + else: + target_year = now.year + target_month = now.month - 1 + + month_label = f"{target_year}-{target_month:02d}" + logger.info(f"Starting monthly HIS doctor rating fetch for {month_label}") + + from_date = datetime(target_year, target_month, 1) + last_day = monthrange(target_year, target_month)[1] + to_date = datetime(target_year, target_month, last_day) + + first_hospital = Hospital.objects.first() + if not first_hospital: + logger.error("No hospitals found") + return {"success": False, "error": "No hospitals found"} + + job = DoctorRatingImportJob.objects.create( + name=f"Monthly HIS Import - {month_label}", + status=DoctorRatingImportJob.JobStatus.PENDING, + source=DoctorRatingImportJob.JobSource.HIS_API, + hospital=first_hospital, + ) + + fetch_his_doctor_ratings.delay(str(job.id), from_date.date().isoformat(), to_date.date().isoformat()) + + return {"success": True, "job_id": str(job.id), "month": month_label} + + except Exception as exc: + logger.error(f"Error in monthly HIS doctor rating fetch: {exc}", exc_info=True) + raise self.retry(exc=exc) + + +@shared_task(bind=True, max_retries=3, default_retry_delay=300) +def fetch_his_doctor_ratings_daily(self): + """ + Daily task to fetch doctor ratings from HIS API for yesterday. + + Query window: FromDate=yesterday 00:00:00, ToDate=yesterday 23:59:59 + e.g. FromDate=08-Apr-2026 00:00:00&ToDate=08-Apr-2026 23:59:59 + """ + from datetime import datetime, timedelta + + try: + yesterday = timezone.now().date() - timedelta(days=1) + + from_date = datetime.combine(yesterday, datetime.min.time()) + to_date = datetime(yesterday.year, yesterday.month, yesterday.day, 23, 59, 59) + + date_label = f"{from_date.date().isoformat()} to {to_date.date().isoformat()}" + logger.info(f"Starting daily HIS doctor rating fetch for {date_label}") + + first_hospital = Hospital.objects.first() + if not first_hospital: + logger.error("No hospitals found") + return {"success": False, "error": "No hospitals found"} + + job = DoctorRatingImportJob.objects.create( + name=f"Daily HIS Import - {yesterday.isoformat()}", + status=DoctorRatingImportJob.JobStatus.PENDING, + source=DoctorRatingImportJob.JobSource.HIS_API, + hospital=first_hospital, + ) + + fetch_his_doctor_ratings.delay(str(job.id), from_date.isoformat(), to_date.isoformat()) + + return {"success": True, "job_id": str(job.id), "date": yesterday.isoformat()} + + except Exception as exc: + logger.error(f"Error in daily HIS doctor rating fetch: {exc}", exc_info=True) raise self.retry(exc=exc) diff --git a/apps/physicians/urls.py b/apps/physicians/urls.py index 313d027..cdba4ea 100644 --- a/apps/physicians/urls.py +++ b/apps/physicians/urls.py @@ -34,6 +34,8 @@ urlpatterns = [ path("individual-ratings/", import_views.individual_ratings_list, name="individual_ratings_list"), # Doctor Rating Import (CSV Upload) path("import/", import_views.doctor_rating_import, name="doctor_rating_import"), + # Doctor Rating Fetch (HIS API) + path("fetch/", import_views.doctor_rating_fetch, name="doctor_rating_fetch"), path("import/review/", import_views.doctor_rating_review, name="doctor_rating_review"), path("import/jobs/", import_views.doctor_rating_job_list, name="doctor_rating_job_list"), path("import/jobs//", import_views.doctor_rating_job_status, name="doctor_rating_job_status"), diff --git a/apps/px_sources/ui_views.py b/apps/px_sources/ui_views.py index 1474b72..bf36ca6 100644 --- a/apps/px_sources/ui_views.py +++ b/apps/px_sources/ui_views.py @@ -99,17 +99,31 @@ def source_detail(request, pk): from datetime import timedelta cutoff = timezone.now() - timedelta(days=30) + recent_usage = usage_stats_queryset.filter(created_at__gte=cutoff) + from django.contrib.contenttypes.models import ContentType + + complaint_ct = ContentType.objects.get(app_label="complaints", model="complaint") + inquiry_ct = ContentType.objects.get(app_label="complaints", model="inquiry") usage_stats = { "total": usage_stats_queryset.count(), - "recent": usage_stats_queryset.filter(created_at__gte=cutoff).count(), + "recent": recent_usage.count(), + "complaints": recent_usage.filter(content_type=complaint_ct).count(), + "inquiries": recent_usage.filter(content_type=inquiry_ct).count(), } + source_complaints = source.complaints.select_related("hospital", "department").order_by("-created_at")[:50] + source_inquiries = source.inquiries.select_related("hospital", "department").order_by("-created_at")[:50] + context = { "source": source, "usage_records": usage_records, "source_users": source_users, "available_users": available_users, "usage_stats": usage_stats, + "source_complaints": source_complaints, + "source_inquiries": source_inquiries, + "complaints_count": source.complaints.count(), + "inquiries_count": source.inquiries.count(), } return render(request, "px_sources/source_detail.html", context) diff --git a/apps/surveys/ui_views.py b/apps/surveys/ui_views.py index f301ade..b79828c 100644 --- a/apps/surveys/ui_views.py +++ b/apps/surveys/ui_views.py @@ -214,7 +214,11 @@ def survey_instance_detail(request, pk): @login_required def survey_template_list(request): """Survey templates list view""" - queryset = SurveyTemplate.objects.select_related("hospital").prefetch_related("questions") + queryset = ( + SurveyTemplate.objects.select_related("hospital") + .prefetch_related("questions") + .annotate(questions_count=Count("questions")) + ) user = request.user if user.is_px_admin(): diff --git a/config/celery.py b/config/celery.py index c9c19f0..cc53d3a 100644 --- a/config/celery.py +++ b/config/celery.py @@ -29,17 +29,12 @@ app.autodiscover_tasks() # Celery Beat schedule for periodic tasks app.conf.beat_schedule = { - # Process unprocessed integration events every 1 minute - "process-integration-events": { - "task": "apps.integrations.tasks.process_pending_events", - "schedule": crontab(minute="*/1"), - }, - # Fetch patient data from HIS every 5 minutes + # Fetch patient data from HIS every 25 minutes "fetch-his-surveys": { "task": "apps.integrations.tasks.fetch_his_surveys", - "schedule": crontab(minute="*/5"), + "schedule": crontab(minute="*/25"), "options": { - "expires": 300, + "expires": 1500, }, }, # TEST TASK - Fetch from JSON file (uncomment for testing, remove when done) @@ -117,6 +112,11 @@ app.conf.beat_schedule = { "task": "apps.physicians.tasks.fetch_his_doctor_ratings_monthly", "schedule": crontab(hour=1, minute=0, day_of_month=1), }, + # Fetch doctor ratings from HIS daily at 1:30 AM (yesterday's ratings) + "fetch-his-doctor-ratings-daily": { + "task": "apps.physicians.tasks.fetch_his_doctor_ratings_daily", + "schedule": crontab(hour=1, minute=30), + }, # Calculate physician monthly ratings on the 1st of each month at 2 AM "calculate-physician-ratings": { "task": "apps.physicians.tasks.calculate_monthly_ratings", @@ -183,10 +183,10 @@ app.conf.beat_schedule = { "task": "apps.surveys.tasks.process_survey_text_analysis", "schedule": crontab(minute="*/30"), }, - # Pre-compute analytics dashboard cache every 5 minutes + # Pre-compute analytics dashboard cache daily at 3 AM "precompute-analytics-cache": { "task": "apps.analytics.tasks.precompute_dashboard_cache_task", - "schedule": crontab(minute="*/5"), + "schedule": crontab(hour=3, minute=0), }, # Generate AI executive summary daily at 6 AM "generate-daily-executive-summary": { diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo index abea623ad6c55aef507be6206e38264e376ff655..ee005c5978b678c9aeb2aab3a9925c45ec022512 100644 GIT binary patch delta 96667 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Ⓢ}89@E?kn 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 delta 98090 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 vC9di=|IVPpQH7$nNBkQ%m(AM5yk#b=P+ diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po index 5728278..efffb51 100644 --- a/locale/ar/LC_MESSAGES/django.po +++ b/locale/ar/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PX360 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-08 13:21+0300\n" +"POT-Creation-Date: 2026-04-09 13:06+0300\n" "PO-Revision-Date: 2025-12-15 12:30+0300\n" "Last-Translator: PX360 Team\n" "Language-Team: Arabic\n" @@ -34,9 +34,8 @@ msgstr "اسم الفئة" #: templates/complaints/complaint_pdf.html:564 #: templates/complaints/complaint_threshold_form.html:361 #: templates/complaints/escalation_rule_form.html:390 -#: templates/core/public_submit.html:515 +#: templates/core/public_submit.html:510 #: templates/emails/new_complaint_admin_notification.html:98 -#: templates/emails/new_observation_notification.html:106 #: templates/emails/observation_assigned.html:110 #: templates/journeys/template_form.html:239 #: templates/observations/category_form.html:50 @@ -44,7 +43,7 @@ msgstr "اسم الفئة" #: templates/observations/convert_to_action.html:65 #: templates/observations/observation_create.html:186 #: templates/observations/observation_detail.html:114 -#: templates/observations/observation_list.html:297 +#: templates/observations/observation_list.html:298 #: templates/observations/public_new.html:138 #: templates/projects/convert_action.html:146 #: templates/projects/project_form.html:188 @@ -55,8 +54,9 @@ msgstr "اسم الفئة" #: templates/projects/template_form.html:291 #: templates/px_sources/source_confirm_delete.html:69 #: templates/px_sources/source_detail.html:157 +#: templates/px_sources/source_detail.html:249 #: templates/px_sources/source_form.html:232 templates/rca/rca_detail.html:167 -#: templates/rca/rca_detail.html:432 templates/rca/rca_detail.html:477 +#: templates/rca/rca_detail.html:426 templates/rca/rca_detail.html:467 #: templates/rca/rca_form.html:55 templates/references/document_view.html:364 #: templates/references/document_view.html:488 #: templates/reports/report_builder.html:190 @@ -131,8 +131,8 @@ msgstr "البحث في رسائل التقدير..." #: appreciation/forms.py:267 templates/complaints/adverse_action_list.html:220 #: templates/journeys/instance_list.html:184 -#: templates/observations/observation_list.html:180 -#: templates/organizations/patient_list.html:213 +#: templates/observations/observation_list.html:181 +#: templates/organizations/patient_list.html:214 #: templates/px_sources/source_user_complaint_list.html:115 #: templates/px_sources/source_user_inquiry_list.html:115 #: templates/reports/report_builder.html:92 @@ -157,7 +157,7 @@ msgstr "جميع الأنواع" #: templates/complaints/complaint_threshold_form.html:301 #: templates/complaints/complaint_threshold_list.html:283 #: templates/complaints/inquiry_list.html:196 -#: templates/observations/observation_list.html:205 +#: templates/observations/observation_list.html:206 #: templates/px_sources/source_user_complaint_list.html:138 #: templates/px_sources/source_user_inquiry_list.html:127 #: templates/standards/search.html:118 @@ -182,7 +182,7 @@ msgid "Organization" msgstr "المنظمة" #: apps/accounts/admin.py:41 templates/accounts/settings.html:24 -#: templates/layouts/partials/sidebar.html:616 +#: templates/layouts/partials/sidebar.html:621 #: templates/layouts/partials/topbar.html:116 msgid "Profile" msgstr "الملف الشخصي" @@ -246,7 +246,7 @@ msgstr "مدير القسم" msgid "PX Coordinator" msgstr "منسق تجربة المرضى" -#: apps/accounts/models.py:455 apps/organizations/models.py:176 +#: apps/accounts/models.py:455 apps/organizations/models.py:177 #: templates/analytics/command_center.html:388 #: templates/appreciation/appreciation_send_form.html:122 #: templates/callcenter/complaint_form.html:148 @@ -262,13 +262,13 @@ msgstr "منسق تجربة المرضى" msgid "Physician" msgstr "طبيب" -#: apps/accounts/models.py:456 apps/organizations/models.py:177 +#: apps/accounts/models.py:456 apps/organizations/models.py:178 #: templates/organizations/staff_list.html:208 msgid "Nurse" msgstr "ممرض/ممرضة" #: apps/accounts/models.py:457 apps/complaints/forms.py:462 -#: apps/complaints/models.py:72 apps/surveys/forms.py:195 +#: apps/complaints/models.py:73 apps/surveys/forms.py:195 #: templates/accounts/settings.html:428 #: templates/complaints/complaint_detail.html:130 #: templates/dashboard/complaint_request_list.html:41 @@ -388,20 +388,21 @@ msgstr "يجب أن تتكون كلمة المرور من 8 أحرف على ال msgid "Password changed successfully. Please login again." msgstr "تم تغيير كلمة المرور بنجاح. يرجى تسجيل الدخول مرة أخرى." -#: apps/accounts/views.py:358 apps/complaints/models.py:1847 -#: apps/complaints/models.py:2614 apps/surveys/forms.py:199 +#: apps/accounts/views.py:358 apps/complaints/models.py:1896 +#: apps/complaints/models.py:2662 apps/surveys/forms.py:199 #: apps/surveys/forms.py:408 templates/accounts/onboarding/dashboard.html:222 #: templates/accounts/onboarding/provisional_list.html:133 #: templates/accounts/onboarding/provisional_list.html:265 #: templates/accounts/settings.html:93 templates/accounts/settings.html:383 #: templates/complaints/inquiry_detail.html:470 #: templates/complaints/oncall/schedule_detail.html:137 -#: templates/core/public_submit.html:551 +#: templates/config/hospital_users.html:232 +#: templates/core/public_submit.html:546 #: templates/feedback/feedback_form.html:202 #: templates/notifications/settings.html:401 #: templates/observations/observation_detail.html:198 #: templates/observations/public_new.html:205 -#: templates/organizations/patient_detail.html:246 +#: templates/organizations/patient_detail.html:253 #: templates/organizations/staff_detail.html:132 #: templates/organizations/staff_form.html:229 #: templates/physicians/physician_detail.html:382 @@ -412,7 +413,7 @@ msgstr "تم تغيير كلمة المرور بنجاح. يرجى تسجيل ا msgid "Email" msgstr "البريد الإلكتروني" -#: apps/accounts/views.py:358 apps/complaints/models.py:2615 +#: apps/accounts/views.py:358 apps/complaints/models.py:2663 #: apps/surveys/forms.py:200 apps/surveys/forms.py:407 #: templates/complaints/oncall/schedule_detail.html:140 #: templates/notifications/settings.html:402 @@ -426,13 +427,13 @@ msgstr "رسائل نصية" msgid "Both" msgstr "كلاهما" -#: apps/accounts/views.py:359 apps/organizations/models.py:27 +#: apps/accounts/views.py:359 apps/organizations/models.py:28 #: templates/complaints/partials/resolution_panel.html:82 #: templates/surveys/instance_detail.html:190 msgid "English" msgstr "الإنجليزية" -#: apps/accounts/views.py:359 apps/organizations/models.py:27 +#: apps/accounts/views.py:359 apps/organizations/models.py:28 #: templates/complaints/partials/resolution_panel.html:91 #: templates/surveys/instance_detail.html:190 msgid "Arabic" @@ -489,7 +490,7 @@ msgstr "معدل الشكاوى غير المكتملة (KPI-7)" #: templates/feedback/action_plan_list.html:78 #: templates/feedback/comment_import_list.html:41 #: templates/journeys/instance_detail.html:258 -#: templates/physicians/doctor_rating_job_list.html:133 +#: templates/physicians/doctor_rating_job_list.html:140 #: templates/physicians/doctor_rating_job_status.html:105 #: templates/projects/project_list.html:124 #: templates/projects/project_list.html:148 @@ -525,7 +526,7 @@ msgstr "جاري التوليد" #: templates/journeys/instance_list.html:138 #: templates/journeys/instance_list.html:186 #: templates/journeys/template_detail.html:50 -#: templates/physicians/doctor_rating_job_list.html:143 +#: templates/physicians/doctor_rating_job_list.html:150 #: templates/physicians/doctor_rating_job_status.html:115 #: templates/physicians/doctor_rating_job_status.html:184 #: templates/projects/project_list.html:115 @@ -540,9 +541,9 @@ msgstr "مكتملة" #: apps/analytics/kpi_models.py:38 apps/feedback/models.py:397 #: templates/analytics/kpi_report_list.html:113 #: templates/analytics/kpi_report_list.html:144 -#: templates/core/public_submit.html:732 templates/core/public_submit.html:735 +#: templates/core/public_submit.html:727 templates/core/public_submit.html:730 #: templates/feedback/comment_import_list.html:37 -#: templates/physicians/doctor_rating_job_list.html:148 +#: templates/physicians/doctor_rating_job_list.html:155 #: templates/physicians/doctor_rating_job_status.html:120 #: templates/physicians/doctor_rating_job_status.html:218 #: templates/simulator/log_list.html:128 templates/simulator/log_list.html:230 @@ -751,38 +752,38 @@ msgstr "تم إعادة إنشاء تقرير مؤشرات الأداء الرئ msgid "Complainant Name" msgstr "اسم مقدم الشكوى" -#: apps/complaints/forms.py:73 apps/complaints/forms.py:926 +#: apps/complaints/forms.py:73 apps/complaints/forms.py:896 #: templates/accounts/onboarding/step_checklist.html:111 #: templates/complaints/public_complaint_form.html:183 #: templates/complaints/public_inquiry_form.html:25 -#: templates/core/public_submit.html:650 +#: templates/core/public_submit.html:645 msgid "Your full name" msgstr "اسمك الكامل" #: apps/complaints/forms.py:77 apps/complaints/forms.py:418 -#: apps/complaints/models.py:208 +#: apps/complaints/models.py:209 #: templates/complaints/public_complaint_form.html:189 msgid "Relation to Patient" msgstr "صلة القرابة بالمريض" -#: apps/complaints/forms.py:87 apps/complaints/forms.py:937 +#: apps/complaints/forms.py:87 apps/complaints/forms.py:907 #: templates/accounts/login.html:72 #: templates/accounts/onboarding/step_activation.html:58 #: templates/accounts/password_reset.html:68 #: templates/complaints/public_complaint_form.html:202 #: templates/complaints/public_inquiry_form.html:29 -#: templates/core/public_submit.html:654 +#: templates/core/public_submit.html:649 #: templates/px_sources/source_user_form.html:205 msgid "Email Address" msgstr "البريد الإلكتروني" -#: apps/complaints/forms.py:89 apps/complaints/forms.py:939 +#: apps/complaints/forms.py:89 apps/complaints/forms.py:909 msgid "your@email.com" msgstr "your@email.com" #: apps/complaints/forms.py:93 #: templates/complaints/public_complaint_form.html:207 -#: templates/organizations/patient_list.html:457 +#: templates/organizations/patient_list.html:463 msgid "Mobile Number" msgstr "رقم الجوال" @@ -819,8 +820,8 @@ msgid "Incident Date" msgstr "تاريخ الواقعة" #: apps/complaints/forms.py:120 apps/complaints/forms.py:446 -#: apps/complaints/forms.py:642 apps/complaints/forms.py:944 -#: apps/complaints/models.py:49 apps/feedback/models.py:321 +#: apps/complaints/forms.py:612 apps/complaints/forms.py:914 +#: apps/complaints/models.py:50 apps/feedback/models.py:321 #: apps/surveys/forms.py:375 #: templates/accounts/onboarding/provisional_list.html:305 #: templates/accounts/settings.html:397 @@ -842,12 +843,13 @@ msgstr "تاريخ الواقعة" #: templates/complaints/partials/resolution_panel.html:112 #: templates/complaints/public_complaint_form.html:253 #: templates/complaints/public_inquiry_form.html:48 +#: templates/config/hospital_users.html:176 +#: templates/config/hospital_users.html:233 #: templates/config/routing_rules.html:44 templates/config/sla_config.html:41 -#: templates/core/public_submit.html:667 +#: templates/core/public_submit.html:662 #: templates/dashboard/admin_evaluation.html:450 #: templates/dashboard/admin_evaluation.html:527 #: templates/emails/new_complaint_admin_notification.html:76 -#: templates/emails/new_observation_notification.html:76 #: templates/emails/observation_assigned.html:76 #: templates/feedback/comment_import_list.html:16 #: templates/feedback/feedback_form.html:313 @@ -856,17 +858,16 @@ msgstr "تاريخ الواقعة" #: templates/journeys/instance_list.html:240 #: templates/journeys/template_form.html:199 #: templates/journeys/template_list.html:80 -#: templates/layouts/partials/sidebar.html:553 +#: templates/layouts/partials/sidebar.html:558 #: templates/organizations/department_list.html:163 -#: templates/organizations/patient_detail.html:580 -#: templates/organizations/patient_list.html:265 -#: templates/organizations/patient_list.html:500 -#: templates/organizations/patient_visit_journey.html:130 +#: templates/organizations/patient_detail.html:587 +#: templates/organizations/patient_list.html:506 +#: templates/organizations/patient_visit_journey.html:131 #: templates/organizations/physician_list.html:80 #: templates/organizations/section_list.html:125 #: templates/organizations/staff_detail.html:102 -#: templates/physicians/doctor_rating_import.html:115 -#: templates/physicians/doctor_rating_job_list.html:98 +#: templates/physicians/doctor_rating_import.html:120 +#: templates/physicians/doctor_rating_job_list.html:105 #: templates/physicians/doctor_rating_job_status.html:158 #: templates/physicians/doctor_rating_review.html:102 #: templates/physicians/physician_detail.html:371 @@ -879,7 +880,9 @@ msgstr "تاريخ الواقعة" #: templates/projects/project_form.html:202 #: templates/projects/project_list.html:166 #: templates/projects/project_save_as_template.html:108 -#: templates/px_sources/source_detail.html:235 +#: templates/px_sources/source_detail.html:252 +#: templates/px_sources/source_detail.html:320 +#: templates/px_sources/source_detail.html:391 #: templates/rca/rca_detail.html:148 templates/rca/rca_list.html:231 #: templates/simulator/log_detail.html:238 #: templates/surveys/bulk_job_list.html:85 @@ -890,7 +893,7 @@ msgid "Hospital" msgstr "المستشفى" #: apps/complaints/forms.py:122 apps/complaints/forms.py:448 -#: apps/complaints/forms.py:644 apps/complaints/forms.py:946 +#: apps/complaints/forms.py:614 apps/complaints/forms.py:916 #: templates/accounts/onboarding/bulk_invite.html:83 #: templates/accounts/onboarding/provisional_list.html:309 #: templates/complaints/complaint_threshold_form.html:173 @@ -898,18 +901,18 @@ msgstr "المستشفى" #: templates/complaints/patient_complaint_portal.html:60 #: templates/complaints/public_complaint_form.html:256 #: templates/complaints/public_inquiry_form.html:52 -#: templates/core/public_submit.html:633 templates/core/select_hospital.html:7 +#: templates/core/public_submit.html:628 templates/core/select_hospital.html:7 #: templates/core/select_hospital.html:48 #: templates/integrations/survey_mapping_settings.html:127 msgid "Select Hospital" msgstr "اختر المستشفى" -#: apps/complaints/forms.py:130 apps/complaints/forms.py:650 +#: apps/complaints/forms.py:130 apps/complaints/forms.py:620 msgid "Department (Optional)" msgstr "القسم (اختياري)" #: apps/complaints/forms.py:132 apps/complaints/forms.py:456 -#: apps/complaints/forms.py:652 +#: apps/complaints/forms.py:622 #: templates/accounts/onboarding/provisional_list.html:322 #: templates/appreciation/appreciation_send_form.html:147 #: templates/complaints/complaint_form.html:776 @@ -927,9 +930,8 @@ msgstr "اختر القسم" #: templates/complaints/complaint_detail.html:215 #: templates/complaints/complaint_pdf.html:527 #: templates/complaints/public_complaint_form.html:276 -#: templates/core/public_submit.html:522 +#: templates/core/public_submit.html:517 #: templates/dashboard/partials/observations_table.html:26 -#: templates/emails/new_observation_notification.html:67 #: templates/emails/observation_assigned.html:67 #: templates/observations/observation_create.html:245 #: templates/observations/observation_detail.html:136 @@ -940,7 +942,7 @@ msgstr "الموقع" #: apps/complaints/forms.py:141 apps/complaints/forms.py:479 #: templates/complaints/public_complaint_form.html:279 #: templates/complaints/public_complaint_form.html:382 -#: templates/core/public_submit.html:794 +#: templates/core/public_submit.html:790 msgid "Select Location" msgstr "اختر الموقع" @@ -955,6 +957,8 @@ msgid "Section" msgstr "القسم" #: apps/complaints/forms.py:149 apps/complaints/forms.py:487 +#: templates/complaints/complaint_form.html:807 +#: templates/complaints/complaint_form.html:819 #: templates/organizations/subsection_form.html:107 msgid "Select Section" msgstr "اختر القسم" @@ -966,10 +970,14 @@ msgid "Subsection" msgstr "الوحدة" #: apps/complaints/forms.py:159 apps/complaints/forms.py:497 +#: templates/complaints/complaint_form.html:808 +#: templates/complaints/complaint_form.html:828 +#: templates/complaints/complaint_form.html:843 +#: templates/complaints/complaint_form.html:854 #: templates/complaints/public_complaint_form.html:299 #: templates/complaints/public_complaint_form.html:407 #: templates/complaints/public_complaint_form.html:441 -#: templates/core/public_submit.html:818 templates/core/public_submit.html:848 +#: templates/core/public_submit.html:814 templates/core/public_submit.html:844 msgid "Select Subsection" msgstr "اختر القسم الفرعي" @@ -990,7 +998,6 @@ msgstr "اسم الموظف المعني (إن كان معروفًا)" #: templates/complaints/patient_complaint_visit_form.html:130 #: templates/complaints/public_complaint_form.html:248 #: templates/emails/explanation_reminder.html:30 -#: templates/emails/explanation_request.html:48 #: templates/emails/explanation_second_reminder.html:33 #: templates/emails/sla_reminder.html:42 #: templates/emails/sla_second_reminder.html:45 @@ -1026,17 +1033,16 @@ msgstr "ما النتيجة التي تتوقعها لحل هذه الشكوى؟ #: templates/complaints/escalation_rule_list.html:291 #: templates/complaints/sla_management.html:370 #: templates/config/routing_rules.html:43 -#: templates/core/public_submit.html:497 +#: templates/core/public_submit.html:492 #: templates/emails/new_complaint_admin_notification.html:59 -#: templates/emails/new_observation_notification.html:57 #: templates/emails/observation_assigned.html:57 #: templates/observations/convert_to_action.html:38 #: templates/observations/observation_create.html:200 -#: templates/observations/observation_list.html:191 -#: templates/observations/observation_list.html:298 +#: templates/observations/observation_list.html:192 +#: templates/observations/observation_list.html:299 #: templates/observations/public_new.html:101 #: templates/observations/public_success.html:130 -#: templates/observations/public_track.html:128 +#: templates/observations/public_track.html:204 #: templates/rca/rca_detail.html:122 templates/rca/rca_form.html:91 #: templates/rca/rca_list.html:182 templates/rca/rca_list.html:229 msgid "Severity" @@ -1052,7 +1058,7 @@ msgstr "الخطورة" #: templates/complaints/escalation_rule_list.html:292 #: templates/complaints/inquiry_detail.html:501 #: templates/complaints/inquiry_list.html:233 -#: templates/complaints/oncall/admin_form.html:225 +#: templates/complaints/oncall/admin_form.html:248 #: templates/complaints/oncall/schedule_detail.html:94 #: templates/complaints/sla_management.html:371 #: templates/config/routing_rules.html:40 @@ -1064,6 +1070,8 @@ msgstr "الخطورة" #: templates/feedback/feedback_form.html:295 #: templates/observations/convert_to_action.html:75 #: templates/projects/convert_action.html:125 +#: templates/px_sources/source_detail.html:251 +#: templates/px_sources/source_detail.html:319 #: templates/px_sources/source_user_complaint_list.html:125 #: templates/px_sources/source_user_complaint_list.html:187 #: templates/px_sources/source_user_dashboard.html:164 @@ -1147,7 +1155,8 @@ msgstr "رقم الهوية الوطنية/الإقامة" #: templates/complaints/complaint_pdf.html:523 #: templates/complaints/inquiry_detail.html:491 #: templates/complaints/involved_department_form.html:129 -#: templates/complaints/public_complaint_track.html:196 +#: templates/complaints/public_complaint_track.html:192 +#: templates/config/hospital_users.html:234 #: templates/dashboard/admin_evaluation.html:276 #: templates/dashboard/admin_evaluation.html:451 #: templates/dashboard/admin_evaluation.html:528 @@ -1164,8 +1173,8 @@ msgstr "رقم الهوية الوطنية/الإقامة" #: templates/observations/observation_create.html:285 #: templates/observations/observation_detail.html:295 #: templates/observations/observation_detail.html:359 -#: templates/observations/observation_list.html:216 -#: templates/observations/observation_list.html:301 +#: templates/observations/observation_list.html:217 +#: templates/observations/observation_list.html:302 #: templates/organizations/physician_list.html:81 #: templates/organizations/section_confirm_delete.html:30 #: templates/organizations/section_form.html:105 @@ -1230,71 +1239,73 @@ msgstr "معرّف الزيارة (اختياري)" msgid "Detailed description of complaint..." msgstr "وصف مفصل للشكوى..." -#: apps/complaints/forms.py:634 +#: apps/complaints/forms.py:604 msgid "Patient (Optional)" msgstr "المريض (اختياري)" -#: apps/complaints/forms.py:636 +#: apps/complaints/forms.py:606 msgid "Select Patient" msgstr "اختر المريض" -#: apps/complaints/forms.py:658 apps/complaints/forms.py:952 +#: apps/complaints/forms.py:628 apps/complaints/forms.py:922 msgid "Inquiry Type" msgstr "نوع الاستفسار" -#: apps/complaints/forms.py:674 apps/complaints/forms.py:965 +#: apps/complaints/forms.py:644 apps/complaints/forms.py:935 #: templates/callcenter/inquiry_form.html:170 #: templates/callcenter/inquiry_list.html:174 #: templates/callcenter/interaction_list.html:108 #: templates/complaints/inquiry_list.html:229 #: templates/complaints/public_inquiry_form.html:80 -#: templates/core/public_submit.html:688 +#: templates/core/public_submit.html:683 #: templates/dashboard/partials/complaints_table.html:25 #: templates/dashboard/partials/inquiries_table.html:25 +#: templates/px_sources/source_detail.html:316 #: templates/px_sources/source_user_dashboard.html:254 #: templates/px_sources/source_user_inquiry_list.html:171 #: templates/simulator/log_detail.html:204 msgid "Subject" msgstr "الموضوع" -#: apps/complaints/forms.py:677 apps/complaints/forms.py:968 +#: apps/complaints/forms.py:647 apps/complaints/forms.py:938 msgid "Brief subject" msgstr "موضوع مختصر" -#: apps/complaints/forms.py:681 apps/complaints/forms.py:972 +#: apps/complaints/forms.py:651 apps/complaints/forms.py:942 #: templates/callcenter/inquiry_form.html:176 #: templates/complaints/public_inquiry_form.html:90 -#: templates/core/public_submit.html:694 +#: templates/core/public_submit.html:689 #: templates/feedback/feedback_form.html:246 #: templates/notifications/send_sms_direct.html:44 msgid "Message" msgstr "الرسالة" -#: apps/complaints/forms.py:683 apps/complaints/forms.py:974 +#: apps/complaints/forms.py:653 apps/complaints/forms.py:944 msgid "Describe your inquiry" msgstr "اشرح استفسارك" -#: apps/complaints/forms.py:688 templates/callcenter/inquiry_form.html:99 +#: apps/complaints/forms.py:658 templates/callcenter/inquiry_form.html:99 #: templates/feedback/feedback_form.html:193 msgid "Contact Name" msgstr "اسم جهة الاتصال" -#: apps/complaints/forms.py:691 templates/callcenter/inquiry_form.html:105 +#: apps/complaints/forms.py:661 templates/callcenter/inquiry_form.html:105 #: templates/px_sources/source_form.html:251 msgid "Contact Phone" msgstr "هاتف جهة الاتصال" -#: apps/complaints/forms.py:694 templates/callcenter/inquiry_form.html:111 +#: apps/complaints/forms.py:664 templates/callcenter/inquiry_form.html:111 #: templates/px_sources/source_form.html:242 msgid "Contact Email" msgstr "البريد الإلكتروني لجهة الاتصال" -#: apps/complaints/forms.py:923 templates/analytics/kpi_list.html:76 +#: apps/complaints/forms.py:893 templates/analytics/kpi_list.html:76 #: templates/complaints/complaint_detail.html:295 #: templates/complaints/inquiry_detail.html:462 #: templates/complaints/public_inquiry_form.html:21 +#: templates/config/hospital_users.html:231 #: templates/config/routing_rules.html:41 -#: templates/core/public_submit.html:549 templates/core/public_submit.html:649 +#: templates/core/public_submit.html:544 templates/core/public_submit.html:644 #: templates/journeys/template_list.html:78 #: templates/observations/observation_detail.html:186 #: templates/observations/public_new.html:197 @@ -1320,10 +1331,10 @@ msgstr "البريد الإلكتروني لجهة الاتصال" msgid "Name" msgstr "الاسم" -#: apps/complaints/forms.py:930 apps/surveys/forms.py:288 +#: apps/complaints/forms.py:900 apps/surveys/forms.py:288 #: templates/accounts/settings.html:101 #: templates/complaints/public_inquiry_form.html:40 -#: templates/core/public_submit.html:662 +#: templates/core/public_submit.html:657 #: templates/notifications/send_sms_direct.html:25 #: templates/notifications/settings.html:406 #: templates/px_sources/source_user_form.html:238 @@ -1331,29 +1342,29 @@ msgstr "الاسم" msgid "Phone Number" msgstr "رقم الهاتف" -#: apps/complaints/forms.py:933 +#: apps/complaints/forms.py:903 #: templates/complaints/public_inquiry_form.html:44 -#: templates/core/public_submit.html:663 +#: templates/core/public_submit.html:658 msgid "Your phone number" msgstr "رقم هاتفك" -#: apps/complaints/forms.py:1030 +#: apps/complaints/forms.py:1000 msgid "This department is already involved in this complaint." msgstr "هذا القسم مشارك بالفعل في هذه الشكوى." -#: apps/complaints/forms.py:1081 +#: apps/complaints/forms.py:1051 msgid "This staff member is already involved in this complaint." msgstr "هذا الموظف مشارك بالفعل في هذه الشكوى." -#: apps/complaints/forms.py:1106 +#: apps/complaints/forms.py:1076 msgid "Enter department response and findings..." msgstr "أدخل استجابة القسم ونتائج التحقيق..." -#: apps/complaints/forms.py:1125 +#: apps/complaints/forms.py:1095 msgid "Enter your explanation regarding this complaint..." msgstr "أدخل تفسيرك بخصوص هذه الشكوى..." -#: apps/complaints/models.py:25 apps/complaints/models.py:1348 +#: apps/complaints/models.py:26 apps/complaints/models.py:1397 #: templates/callcenter/complaint_list.html:93 #: templates/callcenter/complaint_list.html:139 #: templates/callcenter/inquiry_list.html:89 @@ -1376,7 +1387,11 @@ msgstr "أدخل تفسيرك بخصوص هذه الشكوى..." msgid "Open" msgstr "مفتوح" -#: apps/complaints/models.py:26 apps/complaints/models.py:1349 +#: apps/complaints/models.py:27 apps/complaints/models.py:673 +#: apps/complaints/models.py:675 apps/complaints/models.py:683 +#: apps/complaints/models.py:685 apps/complaints/models.py:1398 +#: apps/complaints/ui_views.py:2130 apps/complaints/ui_views.py:2131 +#: apps/complaints/ui_views.py:2132 apps/complaints/ui_views.py:2133 #: templates/accounts/onboarding/dashboard.html:59 #: templates/accounts/onboarding/progress_detail.html:153 #: templates/accounts/onboarding/provisional_list.html:110 @@ -1394,9 +1409,8 @@ msgstr "مفتوح" #: templates/dashboard/my_dashboard.html:62 #: templates/dashboard/my_dashboard.html:142 #: templates/dashboard/my_dashboard.html:261 -#: templates/observations/observation_list.html:131 -#: templates/observations/public_track.html:174 -#: templates/organizations/patient_visit_journey.html:126 +#: templates/observations/observation_list.html:132 +#: templates/organizations/patient_visit_journey.html:127 #: templates/px_sources/source_user_complaint_list.html:117 #: templates/px_sources/source_user_complaint_list.html:212 #: templates/px_sources/source_user_dashboard.html:188 @@ -1407,12 +1421,14 @@ msgstr "مفتوح" msgid "In Progress" msgstr "قيد التنفيذ" -#: apps/complaints/models.py:27 +#: apps/complaints/models.py:28 msgid "Partially Resolved" msgstr "محلول جزئياً" -#: apps/complaints/models.py:28 apps/complaints/models.py:1350 -#: apps/complaints/models.py:2338 templates/callcenter/complaint_list.html:111 +#: apps/complaints/models.py:29 apps/complaints/models.py:680 +#: apps/complaints/models.py:1399 apps/complaints/models.py:2386 +#: apps/complaints/ui_views.py:2134 +#: templates/callcenter/complaint_list.html:111 #: templates/callcenter/complaint_list.html:141 #: templates/callcenter/inquiry_list.html:107 #: templates/callcenter/inquiry_list.html:137 @@ -1428,7 +1444,6 @@ msgstr "محلول جزئياً" #: templates/dashboard/my_dashboard.html:75 #: templates/dashboard/my_dashboard.html:143 #: templates/dashboard/my_dashboard.html:262 -#: templates/observations/public_track.html:181 #: templates/px_sources/source_user_complaint_list.html:118 #: templates/px_sources/source_user_complaint_list.html:217 #: templates/px_sources/source_user_dashboard.html:193 @@ -1438,21 +1453,22 @@ msgstr "محلول جزئياً" msgid "Resolved" msgstr "تم الحل" -#: apps/complaints/models.py:29 apps/complaints/models.py:1351 +#: apps/complaints/models.py:30 apps/complaints/models.py:681 +#: apps/complaints/models.py:1400 apps/complaints/ui_views.py:2135 #: apps/feedback/models.py:35 templates/callcenter/complaint_list.html:142 #: templates/callcenter/inquiry_list.html:138 #: templates/complaints/inquiry_list.html:190 #: templates/dashboard/my_dashboard.html:88 #: templates/dashboard/my_dashboard.html:144 #: templates/dashboard/my_dashboard.html:263 -#: templates/observations/public_track.html:188 #: templates/px_sources/source_user_complaint_list.html:119 #: templates/px_sources/source_user_inquiry_list.html:119 #: templates/rca/rca_list.html:166 msgid "Closed" msgstr "مغلقة" -#: apps/complaints/models.py:30 apps/core/models.py:133 +#: apps/complaints/models.py:31 apps/complaints/models.py:682 +#: apps/complaints/ui_views.py:2136 apps/core/models.py:133 #: templates/actions/action_create.html:88 #: templates/actions/action_list.html:104 #: templates/actions/action_list.html:163 @@ -1460,36 +1476,36 @@ msgstr "مغلقة" msgid "Cancelled" msgstr "ملغى" -#: apps/complaints/models.py:31 apps/complaints/models.py:1352 +#: apps/complaints/models.py:32 apps/complaints/models.py:1401 msgid "Contacted" msgstr "تم التواصل" -#: apps/complaints/models.py:32 apps/complaints/models.py:1353 +#: apps/complaints/models.py:33 apps/complaints/models.py:1402 msgid "Contacted, No Response" msgstr "تم التواصل، لا رد" -#: apps/complaints/models.py:38 +#: apps/complaints/models.py:39 msgid "Full Action Taken" msgstr "تم اتخاذ الإجراء الكامل" -#: apps/complaints/models.py:39 +#: apps/complaints/models.py:40 msgid "Partial Action Taken" msgstr "تم اتخاذ الإجراء الجزئي" -#: apps/complaints/models.py:40 +#: apps/complaints/models.py:41 msgid "No Action Needed" msgstr "لا حاجة لاتخاذ إجراء" -#: apps/complaints/models.py:41 +#: apps/complaints/models.py:42 msgid "Cannot Resolve" msgstr "غير قادر على الحل" -#: apps/complaints/models.py:42 +#: apps/complaints/models.py:43 msgid "Patient Withdrawn" msgstr "المريض منسحب" -#: apps/complaints/models.py:48 apps/complaints/models.py:70 -#: apps/complaints/models.py:204 apps/surveys/forms.py:194 +#: apps/complaints/models.py:49 apps/complaints/models.py:71 +#: apps/complaints/models.py:205 apps/surveys/forms.py:194 #: templates/analytics/command_center.html:360 #: templates/callcenter/complaint_form.html:117 #: templates/callcenter/complaint_list.html:178 @@ -1504,9 +1520,9 @@ msgstr "المريض منسحب" #: templates/emails/new_complaint_admin_notification.html:69 #: templates/feedback/feedback_form.html:180 #: templates/journeys/instance_list.html:238 -#: templates/organizations/patient_list.html:260 -#: templates/organizations/patient_list.html:496 -#: templates/organizations/patient_visit_journey.html:216 +#: templates/organizations/patient_list.html:261 +#: templates/organizations/patient_list.html:502 +#: templates/organizations/patient_visit_journey.html:217 #: templates/physicians/doctor_rating_review.html:175 #: templates/px_sources/source_user_complaint_list.html:184 #: templates/px_sources/source_user_dashboard.html:162 @@ -1518,20 +1534,20 @@ msgstr "المريض منسحب" msgid "Patient" msgstr "المريض" -#: apps/complaints/models.py:50 +#: apps/complaints/models.py:51 #: templates/complaints/partials/resolution_panel.html:113 msgid "Other — please specify" msgstr "آخر — يرجى التحديد" -#: apps/complaints/models.py:56 templates/analytics/command_center.html:589 +#: apps/complaints/models.py:57 templates/analytics/command_center.html:589 #: templates/analytics/dashboard.html:725 #: templates/complaints/adverse_action_form.html:101 #: templates/complaints/adverse_action_list.html:274 -#: templates/core/public_submit.html:309 +#: templates/core/public_submit.html:303 msgid "Complaint" msgstr "شكوى" -#: apps/complaints/models.py:57 +#: apps/complaints/models.py:58 #: templates/appreciation/appreciation_detail.html:11 #: templates/appreciation/appreciation_list.html:4 #: templates/appreciation/appreciation_list.html:57 @@ -1545,23 +1561,23 @@ msgstr "شكوى" msgid "Appreciation" msgstr "التقدير" -#: apps/complaints/models.py:63 apps/px_sources/models.py:37 +#: apps/complaints/models.py:64 apps/px_sources/models.py:37 #: templates/dashboard/admin_evaluation.html:453 msgid "Internal" msgstr "داخلي" -#: apps/complaints/models.py:64 apps/px_sources/models.py:38 +#: apps/complaints/models.py:65 apps/px_sources/models.py:38 #: templates/dashboard/admin_evaluation.html:454 msgid "External" msgstr "خارجي" -#: apps/complaints/models.py:71 templates/callcenter/complaint_form.html:118 +#: apps/complaints/models.py:72 templates/callcenter/complaint_form.html:118 #: templates/callcenter/inquiry_form.html:122 msgid "Family Member" msgstr "أحد أفراد العائلة" -#: apps/complaints/models.py:73 templates/journeys/template_form.html:266 -#: templates/organizations/patient_visit_journey.html:356 +#: apps/complaints/models.py:74 templates/journeys/template_form.html:266 +#: templates/organizations/patient_visit_journey.html:357 #: templates/simulator/log_detail.html:161 #: templates/surveys/comment_list.html:263 #: templates/surveys/instance_detail.html:6 @@ -1569,13 +1585,13 @@ msgstr "أحد أفراد العائلة" msgid "Survey" msgstr "الاستبيان" -#: apps/complaints/models.py:74 templates/layouts/partials/sidebar.html:331 +#: apps/complaints/models.py:75 templates/layouts/partials/sidebar.html:331 #: templates/social/social_comment_detail.html:14 #: templates/social/social_platform.html:96 msgid "Social Media" msgstr "وسائل التواصل الاجتماعي" -#: apps/complaints/models.py:75 templates/callcenter/complaint_form.html:5 +#: apps/complaints/models.py:76 templates/callcenter/complaint_form.html:5 #: templates/callcenter/complaint_list.html:5 #: templates/callcenter/complaint_success.html:5 #: templates/callcenter/inquiry_form.html:5 @@ -1585,21 +1601,21 @@ msgstr "وسائل التواصل الاجتماعي" msgid "Call Center" msgstr "مركز الاتصال" -#: apps/complaints/models.py:76 +#: apps/complaints/models.py:77 #: templates/analytics/kpi_report_generate.html:161 msgid "Ministry of Health" msgstr "وزارة الصحة" -#: apps/complaints/models.py:77 +#: apps/complaints/models.py:78 msgid "Council of Health Insurance" msgstr "مجلس التأمين الصحي" -#: apps/complaints/models.py:78 apps/complaints/models.py:1314 -#: apps/complaints/models.py:1848 apps/complaints/models.py:1911 -#: apps/complaints/models.py:2321 apps/complaints/models.py:2618 +#: apps/complaints/models.py:79 apps/complaints/models.py:1363 +#: apps/complaints/models.py:1897 apps/complaints/models.py:1960 +#: apps/complaints/models.py:2369 apps/complaints/models.py:2666 #: apps/feedback/models.py:50 apps/feedback/models.py:349 -#: apps/organizations/models.py:179 apps/organizations/models.py:218 -#: apps/organizations/models.py:425 apps/px_sources/models.py:41 +#: apps/organizations/models.py:180 apps/organizations/models.py:219 +#: apps/organizations/models.py:427 apps/px_sources/models.py:41 #: templates/callcenter/complaint_form.html:119 #: templates/callcenter/complaint_form.html:191 #: templates/callcenter/inquiry_form.html:123 @@ -1608,7 +1624,7 @@ msgstr "مجلس التأمين الصحي" #: templates/complaints/partials/resolution_panel.html:29 #: templates/complaints/public_complaint_form.html:196 #: templates/complaints/public_inquiry_form.html:73 -#: templates/core/public_submit.html:683 +#: templates/core/public_submit.html:678 #: templates/dashboard/employee_evaluation.html:719 #: templates/organizations/staff_list.html:210 #: templates/px_sources/source_user_complaint_list.html:144 @@ -1616,11 +1632,11 @@ msgstr "مجلس التأمين الصحي" msgid "Other" msgstr "أخرى" -#: apps/complaints/models.py:95 templates/complaints/complaint_detail.html:261 +#: apps/complaints/models.py:96 templates/complaints/complaint_detail.html:261 msgid "Domain" msgstr "المجال" -#: apps/complaints/models.py:96 +#: apps/complaints/models.py:97 #: templates/accounts/acknowledgements/category_list.html:95 #: templates/accounts/acknowledgements/checklist_form.html:124 #: templates/accounts/acknowledgements/checklist_list.html:86 @@ -1644,11 +1660,10 @@ msgstr "المجال" #: templates/complaints/inquiry_list.html:194 #: templates/complaints/inquiry_list.html:231 #: templates/complaints/public_inquiry_form.html:63 -#: templates/core/public_submit.html:491 templates/core/public_submit.html:675 +#: templates/core/public_submit.html:486 templates/core/public_submit.html:670 #: templates/dashboard/partials/complaints_table.html:26 #: templates/dashboard/partials/feedback_table.html:27 #: templates/dashboard/partials/observations_table.html:25 -#: templates/emails/new_observation_notification.html:49 #: templates/emails/observation_assigned.html:49 #: templates/emails/observation_monthly_followup.html:49 #: templates/emails/observation_resolved.html:49 @@ -1658,14 +1673,15 @@ msgstr "المجال" #: templates/observations/convert_to_action.html:71 #: templates/observations/observation_create.html:160 #: templates/observations/observation_detail.html:132 -#: templates/observations/observation_list.html:203 -#: templates/observations/observation_list.html:296 +#: templates/observations/observation_list.html:204 +#: templates/observations/observation_list.html:297 #: templates/observations/public_new.html:91 #: templates/observations/public_success.html:126 -#: templates/observations/public_track.html:124 +#: templates/observations/public_track.html:196 +#: templates/px_sources/source_detail.html:317 #: templates/px_sources/source_user_complaint_list.html:136 #: templates/px_sources/source_user_inquiry_list.html:125 -#: templates/rca/rca_detail.html:418 +#: templates/rca/rca_detail.html:412 #: templates/standards/compliance_form.html:153 #: templates/standards/search.html:114 templates/standards/search.html:167 #: templates/standards/standard_confirm_delete.html:86 @@ -1673,41 +1689,41 @@ msgstr "المجال" msgid "Category" msgstr "الفئة" -#: apps/complaints/models.py:97 templates/callcenter/complaint_form.html:196 +#: apps/complaints/models.py:98 templates/callcenter/complaint_form.html:196 #: templates/complaints/complaint_detail.html:273 #: templates/feedback/feedback_form.html:264 msgid "Subcategory" msgstr "الفئة الفرعية" -#: apps/complaints/models.py:98 templates/callcenter/complaint_form.html:209 +#: apps/complaints/models.py:99 templates/callcenter/complaint_form.html:209 #: templates/complaints/complaint_detail.html:256 #: templates/complaints/complaint_detail.html:279 #: templates/feedback/comment_list.html:29 msgid "Classification" msgstr "التصنيف" -#: apps/complaints/models.py:101 +#: apps/complaints/models.py:102 msgid "Clinical" msgstr "سريري" -#: apps/complaints/models.py:102 +#: apps/complaints/models.py:103 msgid "Management" msgstr "إدارة" -#: apps/complaints/models.py:103 +#: apps/complaints/models.py:104 msgid "Relationships" msgstr "العلاقات" -#: apps/complaints/models.py:205 +#: apps/complaints/models.py:206 #: templates/complaints/public_complaint_form.html:194 msgid "Relative" msgstr "قريب" -#: apps/complaints/models.py:451 +#: apps/complaints/models.py:453 msgid "Satisfied" msgstr "راضٍ" -#: apps/complaints/models.py:452 apps/feedback/models.py:57 +#: apps/complaints/models.py:454 apps/feedback/models.py:57 #: templates/ai_engine/analyze_text.html:105 #: templates/ai_engine/sentiment_dashboard.html:48 #: templates/ai_engine/sentiment_dashboard.html:90 @@ -1732,29 +1748,36 @@ msgstr "راضٍ" msgid "Neutral" msgstr "محايد" -#: apps/complaints/models.py:453 +#: apps/complaints/models.py:455 msgid "Dissatisfied" msgstr "غير راضٍ" -#: apps/complaints/models.py:454 +#: apps/complaints/models.py:456 msgid "No Response" msgstr "لا رد" -#: apps/complaints/models.py:455 templates/actions/action_detail.html:496 +#: apps/complaints/models.py:457 templates/actions/action_detail.html:496 #: templates/complaints/adverse_action_list.html:279 #: templates/complaints/partials/adverse_actions_panel.html:47 #: templates/dashboard/command_center.html:524 msgid "Escalated" msgstr "تم التصعيد" -#: apps/complaints/models.py:1310 apps/feedback/models.py:313 +#: apps/complaints/models.py:672 apps/complaints/models.py:697 +#: apps/complaints/ui_views.py:2129 +#: templates/appreciation/appreciation_list.html:75 +#: templates/appreciation/leaderboard.html:87 +msgid "Received" +msgstr "المستلمة" + +#: apps/complaints/models.py:1359 apps/feedback/models.py:313 #: templates/callcenter/inquiry_form.html:161 #: templates/callcenter/inquiry_list.html:145 #: templates/integrations/survey_mapping_settings.html:160 msgid "Appointment" msgstr "موعد" -#: apps/complaints/models.py:1311 apps/feedback/models.py:344 +#: apps/complaints/models.py:1360 apps/feedback/models.py:344 #: templates/callcenter/complaint_form.html:189 #: templates/callcenter/inquiry_form.html:162 #: templates/callcenter/inquiry_list.html:146 @@ -1764,19 +1787,19 @@ msgstr "موعد" msgid "Billing" msgstr "الفوترة" -#: apps/complaints/models.py:1312 templates/callcenter/inquiry_form.html:163 +#: apps/complaints/models.py:1361 templates/callcenter/inquiry_form.html:163 #: templates/callcenter/inquiry_list.html:147 #: templates/complaints/inquiry_list.html:201 #: templates/complaints/public_inquiry_form.html:72 -#: templates/core/public_submit.html:682 +#: templates/core/public_submit.html:677 msgid "Medical Records" msgstr "السجلات الطبية" -#: apps/complaints/models.py:1313 templates/callcenter/inquiry_form.html:164 +#: apps/complaints/models.py:1362 templates/callcenter/inquiry_form.html:164 msgid "General Information" msgstr "معلومات عامة" -#: apps/complaints/models.py:1322 apps/core/models.py:137 +#: apps/complaints/models.py:1371 apps/core/models.py:137 #: apps/core/models.py:144 templates/actions/action_create.html:31 #: templates/actions/action_list.html:113 #: templates/actions/action_list.html:155 @@ -1787,11 +1810,11 @@ msgstr "معلومات عامة" #: templates/complaints/complaint_list.html:187 #: templates/complaints/partials/priority_badge.html:16 #: templates/complaints/partials/severity_badge.html:16 -#: templates/core/public_submit.html:499 +#: templates/core/public_submit.html:494 #: templates/dashboard/command_center.html:862 #: templates/dashboard/my_dashboard.html:151 #: templates/observations/observation_create.html:205 -#: templates/observations/observation_list.html:194 +#: templates/observations/observation_list.html:195 #: templates/observations/public_new.html:106 #: templates/px_sources/source_user_complaint_list.html:128 #: templates/px_sources/source_user_complaint_list.html:235 @@ -1800,7 +1823,7 @@ msgstr "معلومات عامة" msgid "Low" msgstr "منخفض" -#: apps/complaints/models.py:1323 apps/core/models.py:138 +#: apps/complaints/models.py:1372 apps/core/models.py:138 #: apps/core/models.py:145 templates/actions/action_create.html:32 #: templates/actions/action_list.html:112 #: templates/actions/action_list.html:153 @@ -1811,11 +1834,11 @@ msgstr "منخفض" #: templates/complaints/complaint_list.html:188 #: templates/complaints/partials/priority_badge.html:12 #: templates/complaints/partials/severity_badge.html:12 -#: templates/core/public_submit.html:500 +#: templates/core/public_submit.html:495 #: templates/dashboard/command_center.html:862 #: templates/dashboard/my_dashboard.html:152 #: templates/observations/observation_create.html:209 -#: templates/observations/observation_list.html:195 +#: templates/observations/observation_list.html:196 #: templates/observations/public_new.html:110 #: templates/px_sources/source_user_complaint_list.html:129 #: templates/px_sources/source_user_complaint_list.html:231 @@ -1824,7 +1847,7 @@ msgstr "منخفض" msgid "Medium" msgstr "متوسط" -#: apps/complaints/models.py:1324 apps/core/models.py:139 +#: apps/complaints/models.py:1373 apps/core/models.py:139 #: apps/core/models.py:146 templates/actions/action_create.html:33 #: templates/actions/action_list.html:111 #: templates/actions/action_list.html:151 @@ -1835,11 +1858,11 @@ msgstr "متوسط" #: templates/complaints/complaint_list.html:189 #: templates/complaints/partials/priority_badge.html:8 #: templates/complaints/partials/severity_badge.html:8 -#: templates/core/public_submit.html:501 +#: templates/core/public_submit.html:496 #: templates/dashboard/command_center.html:862 #: templates/dashboard/my_dashboard.html:153 #: templates/observations/observation_create.html:213 -#: templates/observations/observation_list.html:196 +#: templates/observations/observation_list.html:197 #: templates/observations/public_new.html:114 #: templates/px_sources/source_user_complaint_list.html:130 #: templates/px_sources/source_user_complaint_list.html:227 @@ -1848,68 +1871,68 @@ msgstr "متوسط" msgid "High" msgstr "مرتفع" -#: apps/complaints/models.py:1619 +#: apps/complaints/models.py:1668 msgid "Status Change" msgstr "تغيير الحالة" -#: apps/complaints/models.py:1620 +#: apps/complaints/models.py:1669 #: templates/observations/observation_create.html:275 #: templates/observations/observation_detail.html:290 msgid "Assignment" msgstr "التعيين" -#: apps/complaints/models.py:1621 +#: apps/complaints/models.py:1670 #: templates/complaints/complaint_detail.html:612 #: templates/complaints/inquiry_detail.html:436 #: templates/observations/observation_detail.html:247 #: templates/observations/observation_detail.html:371 -#: templates/rca/rca_detail.html:542 +#: templates/rca/rca_detail.html:528 msgid "Note" msgstr "ملاحظة" -#: apps/complaints/models.py:1622 templates/complaints/inquiry_detail.html:364 +#: apps/complaints/models.py:1671 templates/complaints/inquiry_detail.html:364 #: templates/complaints/inquiry_detail.html:434 msgid "Response" msgstr "الرد" -#: apps/complaints/models.py:1623 apps/feedback/models.py:44 +#: apps/complaints/models.py:1672 apps/feedback/models.py:44 #: templates/callcenter/complaint_form.html:190 #: templates/complaints/inquiry_detail.html:435 msgid "Communication" msgstr "التواصل" -#: apps/complaints/models.py:1706 +#: apps/complaints/models.py:1755 msgid "Email Link" msgstr "رابط البريد الإلكتروني" -#: apps/complaints/models.py:1707 templates/complaints/complaint_pdf.html:613 +#: apps/complaints/models.py:1756 templates/complaints/complaint_pdf.html:613 msgid "Direct Entry" msgstr "إدخال مباشر" -#: apps/complaints/models.py:1757 templates/complaints/complaint_pdf.html:623 +#: apps/complaints/models.py:1806 templates/complaints/complaint_pdf.html:623 #: templates/feedback/feedback_list.html:202 msgid "Pending Review" msgstr "بانتظار المراجعة" -#: apps/complaints/models.py:1758 templates/complaints/complaint_pdf.html:619 +#: apps/complaints/models.py:1807 templates/complaints/complaint_pdf.html:619 #: templates/complaints/partials/explanation_panel.html:109 msgid "Acceptable" msgstr "مقبول" -#: apps/complaints/models.py:1759 templates/complaints/complaint_pdf.html:621 +#: apps/complaints/models.py:1808 templates/complaints/complaint_pdf.html:621 #: templates/complaints/partials/explanation_panel.html:112 msgid "Not Acceptable" msgstr "غير مقبول" -#: apps/complaints/models.py:1845 +#: apps/complaints/models.py:1894 #: templates/complaints/complaint_detail.html:303 #: templates/complaints/inquiry_detail.html:466 -#: templates/core/public_submit.html:550 +#: templates/core/public_submit.html:545 #: templates/feedback/feedback_form.html:209 #: templates/observations/observation_detail.html:192 #: templates/observations/public_new.html:201 #: templates/organizations/hospital_list.html:164 -#: templates/organizations/patient_detail.html:242 +#: templates/organizations/patient_detail.html:249 #: templates/organizations/staff_detail.html:142 #: templates/organizations/staff_form.html:276 #: templates/physicians/physician_detail.html:388 @@ -1918,366 +1941,366 @@ msgstr "غير مقبول" msgid "Phone" msgstr "رقم الهاتف" -#: apps/complaints/models.py:1846 +#: apps/complaints/models.py:1895 msgid "In Person" msgstr "شخصياً" -#: apps/complaints/models.py:1908 +#: apps/complaints/models.py:1957 msgid "Management Intervention" msgstr "التدخل الإداري" -#: apps/complaints/models.py:1909 +#: apps/complaints/models.py:1958 msgid "PR Follow-up" msgstr "متابعة العلاقات العامة" -#: apps/complaints/models.py:1910 +#: apps/complaints/models.py:1959 msgid "Department Review" msgstr "مراجعة القسم" -#: apps/complaints/models.py:1952 +#: apps/complaints/models.py:2001 msgid "Primary Department" msgstr "القسم الرئيسي" -#: apps/complaints/models.py:1953 +#: apps/complaints/models.py:2002 msgid "Secondary/Supporting" msgstr "ثانوي/مساند" -#: apps/complaints/models.py:1954 +#: apps/complaints/models.py:2003 msgid "Coordination Only" msgstr "التنسيق فقط" -#: apps/complaints/models.py:1955 +#: apps/complaints/models.py:2004 msgid "Investigating" msgstr "جاري التحقيق" -#: apps/complaints/models.py:2045 +#: apps/complaints/models.py:2094 msgid "Accused/Involved" msgstr "متهم/متورط" -#: apps/complaints/models.py:2046 +#: apps/complaints/models.py:2095 msgid "Witness" msgstr "شاهد" -#: apps/complaints/models.py:2047 +#: apps/complaints/models.py:2096 msgid "Responsible for Resolution" msgstr "مسؤول عن الحل" -#: apps/complaints/models.py:2048 +#: apps/complaints/models.py:2097 msgid "Investigator" msgstr "المحقق" -#: apps/complaints/models.py:2049 +#: apps/complaints/models.py:2098 msgid "Support Staff" msgstr "طاقم الدعم" -#: apps/complaints/models.py:2050 +#: apps/complaints/models.py:2099 msgid "Coordinator" msgstr "المنسق" -#: apps/complaints/models.py:2312 +#: apps/complaints/models.py:2360 msgid "Refused Service" msgstr "رفض الخدمة" -#: apps/complaints/models.py:2313 +#: apps/complaints/models.py:2361 msgid "Delayed Treatment" msgstr "تأخير العلاج" -#: apps/complaints/models.py:2314 +#: apps/complaints/models.py:2362 msgid "Verbal Abuse / Hostility" msgstr "إساءة لفظية / عدائية" -#: apps/complaints/models.py:2315 +#: apps/complaints/models.py:2363 msgid "Increased Wait Time" msgstr "زيادة وقت الانتظار" -#: apps/complaints/models.py:2316 +#: apps/complaints/models.py:2364 msgid "Unnecessary Procedure" msgstr "إجراء غير ضروري" -#: apps/complaints/models.py:2317 +#: apps/complaints/models.py:2365 msgid "Dismissed from Care" msgstr "فصل من الرعاية" -#: apps/complaints/models.py:2318 +#: apps/complaints/models.py:2366 msgid "Poor Treatment Quality" msgstr "جودة العلاج رديئة" -#: apps/complaints/models.py:2319 +#: apps/complaints/models.py:2367 msgid "Discrimination" msgstr "التمييز" -#: apps/complaints/models.py:2320 +#: apps/complaints/models.py:2368 msgid "Retaliation" msgstr "الانتقام" -#: apps/complaints/models.py:2326 +#: apps/complaints/models.py:2374 msgid "Low - Minor inconvenience" msgstr "منخفض - إزعاج طفيف" -#: apps/complaints/models.py:2327 +#: apps/complaints/models.py:2375 msgid "Medium - Moderate impact" msgstr "متوسط - تأثير متوسط" -#: apps/complaints/models.py:2328 +#: apps/complaints/models.py:2376 msgid "High - Significant harm" msgstr "عالٍ - ضرر كبير" -#: apps/complaints/models.py:2329 +#: apps/complaints/models.py:2377 msgid "Critical - Severe harm / Life-threatening" msgstr "حرج - ضرر شديد / مهدد للحياة" -#: apps/complaints/models.py:2334 +#: apps/complaints/models.py:2382 msgid "Reported - Awaiting Review" msgstr "تم الإبلاغ - بانتظار المراجعة" -#: apps/complaints/models.py:2335 +#: apps/complaints/models.py:2383 msgid "Under Investigation" msgstr "قيد التحقيق" -#: apps/complaints/models.py:2336 +#: apps/complaints/models.py:2384 msgid "Verified" msgstr "تم التحقق" -#: apps/complaints/models.py:2337 +#: apps/complaints/models.py:2385 msgid "Unfounded" msgstr "غير مستند" -#: apps/complaints/models.py:2345 +#: apps/complaints/models.py:2393 msgid "The complaint this adverse action is related to" msgstr "الشـكوى التي تتعلق بهذا الإجراء السلبي" -#: apps/complaints/models.py:2350 +#: apps/complaints/models.py:2398 msgid "Type of adverse action" msgstr "نوع الإجراء السلبي" -#: apps/complaints/models.py:2357 +#: apps/complaints/models.py:2405 msgid "Severity level of the adverse action" msgstr "مستوى شدة الإجراء السلبي" -#: apps/complaints/models.py:2360 +#: apps/complaints/models.py:2408 msgid "Detailed description of what happened to the patient" msgstr "وصف مفصل لما حدث للمريض" -#: apps/complaints/models.py:2363 +#: apps/complaints/models.py:2411 msgid "Date and time when the adverse action occurred" msgstr "تاريخ ووقت حدوث الحادث الضائر" -#: apps/complaints/models.py:2367 +#: apps/complaints/models.py:2415 msgid "Location where the incident occurred (e.g., Emergency Room, Clinic B)" msgstr "الموقع الذي وقع فيه الحادث (مثلاً، غرفة الطوارئ، عيادة ب)" -#: apps/complaints/models.py:2375 +#: apps/complaints/models.py:2423 msgid "Staff members involved in the adverse action" msgstr "العاملون المتورطون في الإجراء السلبي" -#: apps/complaints/models.py:2380 +#: apps/complaints/models.py:2428 msgid "" "Description of the impact on the patient (physical, emotional, financial)" msgstr "وصف تأثير الإجراء على المريض (الجسدي، العاطفي، المالي)" -#: apps/complaints/models.py:2388 +#: apps/complaints/models.py:2436 msgid "Current status of the adverse action report" msgstr "الحالة الحالية لتقرير الإجراء السلبي" -#: apps/complaints/models.py:2397 +#: apps/complaints/models.py:2445 msgid "User who reported this adverse action" msgstr "المستخدم الذي أبلغ عن هذا الإجراء السلبي" -#: apps/complaints/models.py:2401 +#: apps/complaints/models.py:2449 msgid "Notes from the investigation" msgstr "ملاحظات التحقيق" -#: apps/complaints/models.py:2409 +#: apps/complaints/models.py:2457 msgid "User who investigated this adverse action" msgstr "المستخدم الذي تحققمن هذا الإجراء السلبي" -#: apps/complaints/models.py:2412 +#: apps/complaints/models.py:2460 msgid "When the investigation was completed" msgstr "متى تم الانتهاء من التحقيق" -#: apps/complaints/models.py:2415 +#: apps/complaints/models.py:2463 msgid "How the adverse action was resolved" msgstr "كيف تم حل هذا الإجراء السلبي" -#: apps/complaints/models.py:2423 +#: apps/complaints/models.py:2471 msgid "User who resolved this adverse action" msgstr "المستخدم الذي حل هذا الإجراء السلبي" -#: apps/complaints/models.py:2426 +#: apps/complaints/models.py:2474 msgid "When the adverse action was resolved" msgstr "متى تم حل هذا الإجراء السلبي" -#: apps/complaints/models.py:2430 +#: apps/complaints/models.py:2478 msgid "Whether this adverse action has been escalated to management" msgstr "هل تم رفع هذا الإجراء السلبي إلى الإدارة؟" -#: apps/complaints/models.py:2433 +#: apps/complaints/models.py:2481 msgid "When the adverse action was escalated" msgstr "متى تم رفع الإجراء السلبي" -#: apps/complaints/models.py:2437 +#: apps/complaints/models.py:2485 msgid "Complaint Adverse Action" msgstr "إجراء مضاد للشكاوى" -#: apps/complaints/models.py:2438 +#: apps/complaints/models.py:2486 msgid "Complaint Adverse Actions" msgstr "إجراءات مضادة للشكاوى" -#: apps/complaints/models.py:2477 +#: apps/complaints/models.py:2525 msgid "Attachment file (image, document, audio recording, etc.)" msgstr "ملف مرفق (صورة، مستند، تسجيل صوتي، إلخ)" -#: apps/complaints/models.py:2482 +#: apps/complaints/models.py:2530 msgid "File size in bytes" msgstr "حجم الملف بالبايت" -#: apps/complaints/models.py:2484 +#: apps/complaints/models.py:2532 msgid "Description of what this attachment shows" msgstr "وصف لما يظهره هذا المرفق" -#: apps/complaints/models.py:2492 +#: apps/complaints/models.py:2540 msgid "Adverse Action Attachment" msgstr "مرفق الإجراء المضاد" -#: apps/complaints/models.py:2493 +#: apps/complaints/models.py:2541 msgid "Adverse Action Attachments" msgstr "مرفقات الإجراء المضاد" -#: apps/complaints/models.py:2516 +#: apps/complaints/models.py:2564 msgid "Hospital this template belongs to" msgstr "المستشفى الذي ينتمي إليه هذا القالب" -#: apps/complaints/models.py:2519 +#: apps/complaints/models.py:2567 msgid "Template name (e.g., 'Long Wait Time', 'Rude Staff')" msgstr "اسم القالب(على سبيل المثال، 'وقت الانتظار الطويل'، 'موظف غير مهذب')" -#: apps/complaints/models.py:2520 +#: apps/complaints/models.py:2568 msgid "Default description template with placeholders" msgstr "قالب الوصف الافتراضي مع القيم الفارغة" -#: apps/complaints/models.py:2529 +#: apps/complaints/models.py:2577 msgid "Default category for this template" msgstr "الفئة الافتراضية لهذا القالب" -#: apps/complaints/models.py:2537 +#: apps/complaints/models.py:2585 msgid "Default severity level" msgstr "مستوى الشدة الافتراضي" -#: apps/complaints/models.py:2543 +#: apps/complaints/models.py:2591 msgid "Default priority level" msgstr "مستوى الأولوية الافتراضي" -#: apps/complaints/models.py:2553 +#: apps/complaints/models.py:2601 msgid "Auto-assign to this department when template is used" msgstr "تخصيص تلقائي لهذا القسم عند استخدام القالب" -#: apps/complaints/models.py:2558 +#: apps/complaints/models.py:2606 msgid "Number of times this template has been used" msgstr "عدد المرات التي تم فيها استخدام هذا القالب" -#: apps/complaints/models.py:2564 +#: apps/complaints/models.py:2612 msgid "List of placeholder names used in description" msgstr "قائمة أسماءالمتغيرات الوهمية المستخدمة في الوصف" -#: apps/complaints/models.py:2568 +#: apps/complaints/models.py:2616 msgid "Whether this template is available for selection" msgstr "هل هذا القالب متاح للاختيار" -#: apps/complaints/models.py:2573 +#: apps/complaints/models.py:2621 msgid "Complaint Template" msgstr "قالب الشكوى" -#: apps/complaints/models.py:2574 +#: apps/complaints/models.py:2622 #: templates/complaints/templates/template_list.html:4 #: templates/complaints/templates/template_list.html:156 msgid "Complaint Templates" msgstr "قالب الشكاوى" -#: apps/complaints/models.py:2613 +#: apps/complaints/models.py:2661 msgid "Phone Call" msgstr "مكالمة هاتفية" -#: apps/complaints/models.py:2616 +#: apps/complaints/models.py:2664 msgid "Meeting" msgstr "اجتماع" -#: apps/complaints/models.py:2617 +#: apps/complaints/models.py:2665 msgid "Letter" msgstr "رسالة" -#: apps/complaints/models.py:2630 +#: apps/complaints/models.py:2678 msgid "Related complaint" msgstr "شكوى ذات صلة" -#: apps/complaints/models.py:2635 +#: apps/complaints/models.py:2683 msgid "Type of communication" msgstr "نوع التواصل" -#: apps/complaints/models.py:2641 +#: apps/complaints/models.py:2689 #: templates/callcenter/call_records_list.html:99 #: templates/callcenter/call_records_list.html:163 #: templates/callcenter/call_records_list.html:247 msgid "Inbound" msgstr "الوارد" -#: apps/complaints/models.py:2642 +#: apps/complaints/models.py:2690 #: templates/callcenter/call_records_list.html:111 #: templates/callcenter/call_records_list.html:164 #: templates/callcenter/call_records_list.html:252 msgid "Outbound" msgstr "الخارجي" -#: apps/complaints/models.py:2648 +#: apps/complaints/models.py:2696 msgid "Name of person contacted" msgstr "اسم الشخص الذي تم التواصل معه" -#: apps/complaints/models.py:2650 +#: apps/complaints/models.py:2698 msgid "Role/relation (e.g., Complainant, Patient, Staff)" msgstr "الدور/العلاقة (مثلاً: المُشتكِي، المريض، الموظف)" -#: apps/complaints/models.py:2652 templates/callcenter/complaint_form.html:111 +#: apps/complaints/models.py:2700 templates/callcenter/complaint_form.html:111 #: templates/callcenter/inquiry_form.html:107 #: templates/surveys/his_patient_import.html:139 msgid "Phone number" msgstr "رقم الهاتف" -#: apps/complaints/models.py:2653 templates/callcenter/inquiry_form.html:113 +#: apps/complaints/models.py:2701 templates/callcenter/inquiry_form.html:113 msgid "Email address" msgstr "عنوان البريد الإلكتروني" -#: apps/complaints/models.py:2656 +#: apps/complaints/models.py:2704 msgid "Subject/summary of communication" msgstr "موضوع/ملخص التواصل" -#: apps/complaints/models.py:2657 +#: apps/complaints/models.py:2705 msgid "Details of what was discussed" msgstr "تفاصيل ما تم مناقشته" -#: apps/complaints/models.py:2660 +#: apps/complaints/models.py:2708 msgid "Whether this communication requires follow-up" msgstr "هل يتطلب هذا التواصل متابعة" -#: apps/complaints/models.py:2661 +#: apps/complaints/models.py:2709 msgid "Date when follow-up is needed" msgstr "تاريخ المتابعة المطلوبة" -#: apps/complaints/models.py:2662 +#: apps/complaints/models.py:2710 msgid "Notes from follow-up" msgstr "ملاحظات من المتابعة" -#: apps/complaints/models.py:2669 +#: apps/complaints/models.py:2717 msgid "Attached document (email export, letter, etc.)" msgstr "الوثيقة المرفقة (تصدير البريد الإلكتروني، رسالة، إلخ)" -#: apps/complaints/models.py:2678 +#: apps/complaints/models.py:2726 msgid "User who logged this communication" msgstr "المستخدم الذي سجل هذه المراسلة" -#: apps/complaints/models.py:2683 +#: apps/complaints/models.py:2731 msgid "Complaint Communication" msgstr "الاتصال بشكاوى المرضى" -#: apps/complaints/models.py:2684 +#: apps/complaints/models.py:2732 msgid "Complaint Communications" msgstr "الاتصال بشكاوى المرضى" @@ -2289,7 +2312,7 @@ msgstr "اسم المشتكي مطلوب" msgid "Mobile number is required" msgstr "رقم الجوال مطلوب" -#: apps/complaints/ui_views.py:1929 apps/complaints/ui_views.py:2160 +#: apps/complaints/ui_views.py:1929 apps/complaints/ui_views.py:2186 msgid "Hospital is required" msgstr "المستشفى مطلوب" @@ -2305,187 +2328,187 @@ msgstr "القسم الرئيسي مطلوب" msgid "Complaint details are required" msgstr "تفاصيل الشكوى مطلوبة" -#: apps/complaints/ui_views.py:1941 apps/complaints/ui_views.py:2170 +#: apps/complaints/ui_views.py:1941 apps/complaints/ui_views.py:2196 #: templates/accounts/onboarding/step_activation.html:136 msgid "Please fill in all required fields." msgstr "يرجى تعبئة جميع الحقول المطلوبة." -#: apps/complaints/ui_views.py:2026 apps/complaints/ui_views.py:2242 +#: apps/complaints/ui_views.py:2028 apps/complaints/ui_views.py:2268 msgid "Selected hospital not found." msgstr "المستشفى المحدد غير موجود." -#: apps/complaints/ui_views.py:2084 +#: apps/complaints/ui_views.py:2086 msgid "Please enter a reference number." msgstr "يرجى إدخال رقم مرجعي." -#: apps/complaints/ui_views.py:2098 apps/complaints/ui_views.py:2113 +#: apps/complaints/ui_views.py:2100 apps/complaints/ui_views.py:2115 msgid "" "No complaint found with this reference number. Please check and try again." msgstr "" "لم يتم العثور على شكوى بهذا الرقم المرجعي. يرجى التحقق والمحاولة مرة أخرى." -#: apps/complaints/ui_views.py:2156 +#: apps/complaints/ui_views.py:2182 msgid "Name is required" msgstr "الاسم مطلوب" -#: apps/complaints/ui_views.py:2158 +#: apps/complaints/ui_views.py:2184 msgid "Phone number is required" msgstr "رقم الهاتف مطلوب" -#: apps/complaints/ui_views.py:2162 +#: apps/complaints/ui_views.py:2188 msgid "Subject is required" msgstr "الموضوع مطلوب" -#: apps/complaints/ui_views.py:2164 +#: apps/complaints/ui_views.py:2190 msgid "Message is required" msgstr "الرسالة مطلوب" -#: apps/complaints/ui_views.py:2234 +#: apps/complaints/ui_views.py:2260 msgid "Inquiry submitted successfully!" msgstr "تم إرسال الاستفسار بنجاح!" -#: apps/complaints/ui_views.py:3055 apps/complaints/ui_views.py:3122 -#: apps/complaints/ui_views.py:3195 apps/complaints/ui_views.py:3252 -#: apps/complaints/ui_views.py:3338 apps/complaints/ui_views.py:3405 -#: apps/complaints/ui_views.py:3454 +#: apps/complaints/ui_views.py:3084 apps/complaints/ui_views.py:3151 +#: apps/complaints/ui_views.py:3224 apps/complaints/ui_views.py:3281 +#: apps/complaints/ui_views.py:3367 apps/complaints/ui_views.py:3434 +#: apps/complaints/ui_views.py:3483 msgid "You don't have permission to manage this complaint." msgstr "ليس لديك الصلاحية لإدارة هذه الشكوى." -#: apps/complaints/ui_views.py:3060 +#: apps/complaints/ui_views.py:3089 #, fuzzy #| msgid "No departments found" msgid "No AI department suggestion found." msgstr "لا توجد أقسام" -#: apps/complaints/ui_views.py:3065 +#: apps/complaints/ui_views.py:3094 #, fuzzy #| msgid "This department is already involved in this complaint." msgid "This department is already involved." msgstr "هذا القسم مشارك بالفعل في هذه الشكوى." -#: apps/complaints/ui_views.py:3100 +#: apps/complaints/ui_views.py:3129 #, fuzzy, python-format #| msgid "Department '%(dept)s' added successfully as %(role)s." msgid "Department '%(dept)s' added as Primary (AI suggestion confirmed)." msgstr "تمت إضافة القسم '%(dept)s' بنجاح كـ %(role)s." -#: apps/complaints/ui_views.py:3163 +#: apps/complaints/ui_views.py:3192 #, python-format msgid "Department '%(dept)s' added successfully as %(role)s." msgstr "تمت إضافة القسم '%(dept)s' بنجاح كـ %(role)s." -#: apps/complaints/ui_views.py:3168 apps/complaints/ui_views.py:3223 -#: apps/complaints/ui_views.py:3378 apps/complaints/ui_views.py:3425 +#: apps/complaints/ui_views.py:3197 apps/complaints/ui_views.py:3252 +#: apps/complaints/ui_views.py:3407 apps/complaints/ui_views.py:3454 msgid "Please correct the errors below." msgstr "يرجى تصحيح الأخطاء أدناه." -#: apps/complaints/ui_views.py:3175 +#: apps/complaints/ui_views.py:3204 msgid "Add Involved Department" msgstr "إضافة القسم المعني" -#: apps/complaints/ui_views.py:3220 +#: apps/complaints/ui_views.py:3249 msgid "Department involvement updated successfully." msgstr "تم تحديث إشراك القسم بنجاح." -#: apps/complaints/ui_views.py:3231 +#: apps/complaints/ui_views.py:3260 msgid "Edit Involved Department" msgstr "تعديل القسم المعني" -#: apps/complaints/ui_views.py:3273 +#: apps/complaints/ui_views.py:3302 msgid "Department removed successfully." msgstr "تم إزالة القسم بنجاح." -#: apps/complaints/ui_views.py:3293 +#: apps/complaints/ui_views.py:3322 msgid "You don't have permission to submit a response for this department." msgstr "ليس لديك إذن لإرسال استجابة لهذا القسم." -#: apps/complaints/ui_views.py:3312 +#: apps/complaints/ui_views.py:3341 msgid "Department response submitted successfully." msgstr "تم إرسال استجابة القسم بنجاح." -#: apps/complaints/ui_views.py:3314 +#: apps/complaints/ui_views.py:3343 msgid "Please provide a valid response." msgstr "يرجى تقديم استجابة صالحة." -#: apps/complaints/ui_views.py:3373 +#: apps/complaints/ui_views.py:3402 #, python-format msgid "Staff member '%(staff)s' added successfully as %(role)s." msgstr "تمت إضافة الموظف '%(staff)s' بنجاح كـ %(role)s." -#: apps/complaints/ui_views.py:3385 +#: apps/complaints/ui_views.py:3414 msgid "Add Involved Staff" msgstr "إضافة الموظفين المشاركين" -#: apps/complaints/ui_views.py:3422 +#: apps/complaints/ui_views.py:3451 msgid "Staff involvement updated successfully." msgstr "تم تحديث مشاركة الموظفين بنجاح." -#: apps/complaints/ui_views.py:3433 +#: apps/complaints/ui_views.py:3462 msgid "Edit Involved Staff" msgstr "تعديل الموظفين المشاركين" -#: apps/complaints/ui_views.py:3475 +#: apps/complaints/ui_views.py:3504 msgid "Staff member removed successfully." msgstr "تم إزالة الموظف بنجاح." -#: apps/complaints/ui_views.py:3497 +#: apps/complaints/ui_views.py:3526 msgid "" "You don't have permission to submit an explanation for this staff member." msgstr "ليس لديك إذن لإرسال تفسير لهذا الموظف." -#: apps/complaints/ui_views.py:3516 +#: apps/complaints/ui_views.py:3545 msgid "Explanation submitted successfully." msgstr "تم إرسال التفسير بنجاح." -#: apps/complaints/ui_views.py:3518 +#: apps/complaints/ui_views.py:3547 msgid "Please provide a valid explanation." msgstr "يرجى تقديم تفسير صالح." -#: apps/complaints/ui_views.py:3541 +#: apps/complaints/ui_views.py:3570 msgid "You don't have permission to view adverse actions." msgstr "ليس لديك إذن لعرض الإجراءات السلبية." -#: apps/complaints/ui_views.py:3601 +#: apps/complaints/ui_views.py:3630 msgid "You don't have permission to add adverse actions to this complaint." msgstr "ليس لديك إذن لإضافة إجراءات سلبية إلى هذه الشكوى." -#: apps/complaints/ui_views.py:3616 +#: apps/complaints/ui_views.py:3645 msgid "Description is required." msgstr "الوصف مطلوب." -#: apps/complaints/ui_views.py:3666 +#: apps/complaints/ui_views.py:3695 msgid "Adverse action reported successfully." msgstr "تم الإبلاغ عن الإجراء السلبي بنجاح." -#: apps/complaints/ui_views.py:3696 +#: apps/complaints/ui_views.py:3725 msgid "You don't have permission to edit this adverse action." msgstr "ليس لديك إذن لتعديل هذا الإجراء السلبي." -#: apps/complaints/ui_views.py:3727 +#: apps/complaints/ui_views.py:3756 msgid "Adverse action updated successfully." msgstr "تم تحديث الإجراء السلبي بنجاح." -#: apps/complaints/ui_views.py:3759 +#: apps/complaints/ui_views.py:3788 msgid "You don't have permission to update this adverse action." msgstr "ليس لديك إذن لتحديث هذا الإجراء السلبي." -#: apps/complaints/ui_views.py:3812 +#: apps/complaints/ui_views.py:3841 msgid "Adverse action status updated successfully." msgstr "تم تحديث حالة الإجراء السلبي بنجاح." -#: apps/complaints/ui_views.py:3832 +#: apps/complaints/ui_views.py:3861 msgid "You don't have permission to escalate this adverse action." msgstr "ليس لديك إذن لتصعيد هذا الإجراء السلبي." -#: apps/complaints/ui_views.py:3857 +#: apps/complaints/ui_views.py:3886 msgid "Adverse action escalated successfully." msgstr "تم تصعيد الإجراء السلبي بنجاح." -#: apps/complaints/ui_views.py:3877 +#: apps/complaints/ui_views.py:3906 msgid "You don't have permission to delete adverse actions." msgstr "ليس لديك إذن لحذف الإجراءات السلبية." -#: apps/complaints/ui_views.py:3897 +#: apps/complaints/ui_views.py:3926 msgid "Adverse action deleted successfully." msgstr "تم حذف الإجراء السلبي بنجاح." @@ -2552,72 +2575,72 @@ msgstr "تم إنشاء جدول المناوبات بنجاح." msgid "Error creating on-call schedule. Please try again." msgstr "حدث خطأ أثناء إنشاء جدول المناوبات. يرجى المحاولة مرة أخرى." -#: apps/complaints/ui_views_oncall.py:123 +#: apps/complaints/ui_views_oncall.py:131 msgid "Create On-Call Schedule" msgstr "إنشاء جدول مناوبات" -#: apps/complaints/ui_views_oncall.py:152 +#: apps/complaints/ui_views_oncall.py:157 msgid "On-Call Schedule Details" msgstr "تفاصيل جدول مناوبات" -#: apps/complaints/ui_views_oncall.py:206 +#: apps/complaints/ui_views_oncall.py:211 msgid "On-call schedule updated successfully." msgstr "تم تحديث جدول المناوبات بنجاح." -#: apps/complaints/ui_views_oncall.py:211 +#: apps/complaints/ui_views_oncall.py:216 msgid "Error updating on-call schedule. Please try again." msgstr "حدث خطأ أثناء تحديث جدول المناوبات. يرجى المحاولة مرة أخرى." -#: apps/complaints/ui_views_oncall.py:221 +#: apps/complaints/ui_views_oncall.py:234 msgid "Edit On-Call Schedule" msgstr "تعديل جدول مناوبات" -#: apps/complaints/ui_views_oncall.py:252 +#: apps/complaints/ui_views_oncall.py:265 msgid "On-call schedule deleted successfully." msgstr "تم حذف جدول المناوبات بنجاح." -#: apps/complaints/ui_views_oncall.py:256 +#: apps/complaints/ui_views_oncall.py:269 msgid "Error deleting on-call schedule." msgstr "خطأ في حذف جدول المناوبات." -#: apps/complaints/ui_views_oncall.py:283 +#: apps/complaints/ui_views_oncall.py:300 msgid "Please select an admin user." msgstr "يرجى اختيار مستخدم مسؤول." -#: apps/complaints/ui_views_oncall.py:319 +#: apps/complaints/ui_views_oncall.py:336 msgid "On-call admin added successfully." msgstr "تمت إضافة المسؤول للمناوبة بنجاح." -#: apps/complaints/ui_views_oncall.py:324 +#: apps/complaints/ui_views_oncall.py:341 msgid "Error adding on-call admin. Please try again." msgstr "حدث خطأ أثناء إضافة المسؤول للمناوبة. يرجى المحاولة مرة أخرى." -#: apps/complaints/ui_views_oncall.py:329 +#: apps/complaints/ui_views_oncall.py:349 #: templates/complaints/oncall/schedule_detail.html:32 msgid "Add On-Call Admin" msgstr "إضافة مسؤول المناوبات" -#: apps/complaints/ui_views_oncall.py:379 +#: apps/complaints/ui_views_oncall.py:396 msgid "On-call admin updated successfully." msgstr "تم تحديث مسؤول المناوب بنجاح." -#: apps/complaints/ui_views_oncall.py:384 +#: apps/complaints/ui_views_oncall.py:401 msgid "Error updating on-call admin. Please try again." msgstr "خطأ في تحديث مسؤول المناوب. يرجى المحاولة مرة أخرى." -#: apps/complaints/ui_views_oncall.py:389 +#: apps/complaints/ui_views_oncall.py:406 msgid "Edit On-Call Admin" msgstr "تعديل مسؤول المناوب" -#: apps/complaints/ui_views_oncall.py:425 +#: apps/complaints/ui_views_oncall.py:439 msgid "On-call admin removed successfully." msgstr "تم إزالة مسؤول المناوب بنجاح." -#: apps/complaints/ui_views_oncall.py:429 +#: apps/complaints/ui_views_oncall.py:443 msgid "Error removing on-call admin." msgstr "خطأ في إزالة مسؤول المناوبات." -#: apps/complaints/ui_views_oncall.py:476 +#: apps/complaints/ui_views_oncall.py:492 msgid "On-Call Dashboard" msgstr "لوحة تحكم المناوب" @@ -2675,6 +2698,7 @@ msgstr "الصفحة" #: apps/complaints/views.py:3473 #: templates/accounts/onboarding/step_checklist.html:29 #: templates/accounts/onboarding/step_checklist.html:232 +#: templates/accounts/onboarding/step_content.html:34 #: templates/ai_engine/sentiment_list.html:299 #: templates/analytics/kpi_report_list.html:148 #: templates/analytics/kpi_report_list.html:281 @@ -2683,14 +2707,16 @@ msgstr "الصفحة" #: templates/callcenter/inquiry_list.html:246 #: templates/complaints/complaint_list.html:174 #: templates/complaints/complaint_list.html:317 +#: templates/config/hospital_users.html:224 +#: templates/config/hospital_users.html:313 #: templates/dashboard/partials/actions_table.html:88 #: templates/dashboard/partials/complaints_table.html:84 #: templates/dashboard/partials/feedback_table.html:80 #: templates/dashboard/partials/inquiries_table.html:82 #: templates/dashboard/partials/observations_table.html:74 #: templates/dashboard/partials/tasks_table.html:82 -#: templates/observations/observation_list.html:283 -#: templates/organizations/patient_list.html:364 +#: templates/observations/observation_list.html:284 +#: templates/organizations/patient_list.html:370 #: templates/organizations/staff_list.html:253 #: templates/organizations/staff_list.html:379 #: templates/physicians/doctor_rating_review.html:166 @@ -2715,6 +2741,10 @@ msgstr "من" msgid "Generated" msgstr "تم الإنشاء" +#: apps/core/config_views.py:243 +msgid "Your PX360 Password Has Been Reset" +msgstr "تم إعادة تعيين كلمة مرور PX360 الخاصة بك" + #: apps/core/decorators.py:34 apps/px_sources/decorators.py:168 msgid "Access denied. PX Admin privileges required." msgstr "تم رفض الوصول. مطلوب صلاحيات مسؤول PX." @@ -2779,6 +2809,9 @@ msgstr "تم رفض الوصول." #: templates/complaints/sla_management_form.html:344 #: templates/complaints/templates/template_list.html:181 #: templates/complaints/templates/template_list.html:212 +#: templates/config/hospital_users.html:144 +#: templates/config/hospital_users.html:198 +#: templates/config/hospital_users.html:280 #: templates/config/routing_rules.html:95 templates/config/sla_config.html:76 #: templates/integrations/survey_mapping_settings.html:51 #: templates/integrations/survey_mapping_settings.html:183 @@ -2794,10 +2827,10 @@ msgstr "تم رفض الوصول." #: templates/organizations/department_list.html:129 #: templates/organizations/hierarchy_node.html:61 #: templates/organizations/hospital_list.html:129 -#: templates/organizations/patient_detail.html:356 -#: templates/organizations/patient_list.html:165 -#: templates/organizations/patient_list.html:214 -#: templates/organizations/patient_visit_journey.html:101 +#: templates/organizations/patient_detail.html:363 +#: templates/organizations/patient_list.html:166 +#: templates/organizations/patient_list.html:215 +#: templates/organizations/patient_visit_journey.html:102 #: templates/organizations/section_confirm_delete.html:36 #: templates/organizations/section_form.html:117 #: templates/organizations/section_list.html:92 @@ -2850,13 +2883,16 @@ msgstr "نشط" #: templates/complaints/oncall/schedule_list.html:210 #: templates/complaints/templates/template_list.html:182 #: templates/complaints/templates/template_list.html:217 +#: templates/config/hospital_users.html:157 +#: templates/config/hospital_users.html:199 +#: templates/config/hospital_users.html:282 #: templates/config/routing_rules.html:97 templates/config/sla_config.html:78 #: templates/integrations/survey_mapping_settings.html:53 #: templates/journeys/template_detail.html:76 #: templates/journeys/template_list.html:97 #: templates/observations/category_list.html:125 #: templates/organizations/hierarchy_node.html:66 -#: templates/organizations/patient_list.html:215 +#: templates/organizations/patient_list.html:216 #: templates/organizations/section_confirm_delete.html:36 #: templates/organizations/section_form.html:118 #: templates/organizations/section_list.html:93 @@ -2892,12 +2928,12 @@ msgstr "غير نشط" #: templates/complaints/complaint_form.html:716 #: templates/complaints/complaint_list.html:190 #: templates/complaints/partials/severity_badge.html:4 -#: templates/core/public_submit.html:502 +#: templates/core/public_submit.html:497 #: templates/dashboard/command_center.html:862 #: templates/dashboard/my_dashboard.html:154 #: templates/dashboard/staff_performance_detail.html:200 #: templates/observations/observation_create.html:217 -#: templates/observations/observation_list.html:197 +#: templates/observations/observation_list.html:198 #: templates/observations/public_new.html:118 templates/rca/rca_list.html:188 #: templates/rca/rca_list.html:198 msgid "Critical" @@ -2934,7 +2970,7 @@ msgstr "ملاحظات عامة" #: apps/feedback/models.py:25 templates/complaints/inquiry_detail.html:4 #: templates/complaints/inquiry_detail.html:228 -#: templates/core/public_submit.html:335 +#: templates/core/public_submit.html:329 msgid "Inquiry" msgstr "استفسار" @@ -2944,12 +2980,10 @@ msgstr "فحص الرضا" #: apps/feedback/models.py:32 #: templates/complaints/partials/explanation_panel.html:62 -#: templates/complaints/public_complaint_track.html:191 -#: templates/emails/new_observation_notification.html:91 +#: templates/complaints/public_complaint_track.html:187 #: templates/observations/observation_detail.html:144 #: templates/observations/public_success.html:156 -#: templates/observations/public_track.html:139 -#: templates/observations/public_track.html:154 +#: templates/observations/public_track.html:191 #: templates/surveys/instance_detail.html:382 msgid "Submitted" msgstr "تم الإرسال" @@ -2981,7 +3015,7 @@ msgid "Appointment & Scheduling" msgstr "المواعيد والجدولة" #: apps/feedback/models.py:46 templates/complaints/public_inquiry_form.html:71 -#: templates/core/public_submit.html:681 +#: templates/core/public_submit.html:676 msgid "Billing & Insurance" msgstr "الفوترة والتأمين" @@ -3125,7 +3159,7 @@ msgstr "العيادات الخارجية" #: apps/feedback/models.py:341 templates/complaints/inquiry_list.html:199 #: templates/complaints/public_inquiry_form.html:70 -#: templates/core/public_submit.html:680 +#: templates/core/public_submit.html:675 msgid "Appointments" msgstr "المواعيد" @@ -3154,7 +3188,7 @@ msgid "Housekeeping" msgstr "النظافة" #: apps/feedback/models.py:395 templates/feedback/comment_import_list.html:39 -#: templates/physicians/doctor_rating_job_list.html:138 +#: templates/physicians/doctor_rating_job_list.html:145 #: templates/physicians/doctor_rating_job_status.html:110 msgid "Processing" msgstr "جاري المعالجة" @@ -3191,6 +3225,33 @@ msgstr "الرسالة طويلة جدًا. الحد الأقصى 1600 حرف." msgid "SMS sent successfully to {phone_number}." msgstr "تم إرسال الرسالة القصيرة بنجاح إلى {phone_number}." +#: apps/observations/views.py:143 +#, fuzzy +#| msgid "Please enter a title." +msgid "Please enter a tracking code." +msgstr "الرجاء إدخال العنوان." + +#: apps/observations/views.py:152 apps/observations/views.py:161 +#, fuzzy +#| msgid "" +#| "No complaint found with this reference number. Please check and try again." +msgid "" +"No observation found with this tracking code. Please check and try again." +msgstr "" +"لم يتم العثور على شكوى بهذا الرقم المرجعي. يرجى التحقق والمحاولة مرة أخرى." + +#: apps/observations/views.py:169 +#: templates/complaints/public_complaint_track.html:231 +#: templates/observations/public_track.html:233 +msgid "Status Updated" +msgstr "تم تحديث الحالة" + +#: apps/observations/views.py:180 +#: templates/complaints/public_complaint_track.html:233 +#: templates/observations/public_track.html:238 +msgid "Update Received" +msgstr "تم استلام التحديث" + #: apps/organizations/forms.py:70 msgid "Leave blank to auto-generate" msgstr "اتركه فارغًا للإنشاء التلقائي" @@ -3199,25 +3260,25 @@ msgstr "اتركه فارغًا للإنشاء التلقائي" msgid "A patient with this MRN already exists." msgstr "يوجد مريض بهذا الرقم الطبي بالفعل." -#: apps/organizations/models.py:178 +#: apps/organizations/models.py:179 msgid "Administrative" msgstr "إداري" -#: apps/organizations/models.py:218 apps/organizations/models.py:425 -#: templates/organizations/patient_list.html:222 +#: apps/organizations/models.py:219 apps/organizations/models.py:427 +#: templates/organizations/patient_list.html:223 msgid "Male" msgstr "ذكر" -#: apps/organizations/models.py:218 apps/organizations/models.py:425 -#: templates/organizations/patient_list.html:223 +#: apps/organizations/models.py:219 apps/organizations/models.py:427 +#: templates/organizations/patient_list.html:224 msgid "Female" msgstr "أنثى" -#: apps/organizations/ui_views.py:1294 +#: apps/organizations/ui_views.py:1297 msgid "Create Patient" msgstr "إنشاء مريض" -#: apps/organizations/ui_views.py:1331 +#: apps/organizations/ui_views.py:1334 msgid "Edit Patient" msgstr "تعديل المريض" @@ -3428,137 +3489,137 @@ msgstr "شريك" msgid "Government" msgstr "الحكومة" -#: apps/px_sources/ui_views.py:125 +#: apps/px_sources/ui_views.py:139 msgid "You don't have permission to create sources." msgstr "ليس لديك الصلاحية لإنشاء المصادر." -#: apps/px_sources/ui_views.py:142 +#: apps/px_sources/ui_views.py:156 msgid "Source created successfully!" msgstr "تم إنشاء المصدر بنجاح!" -#: apps/px_sources/ui_views.py:146 +#: apps/px_sources/ui_views.py:160 msgid "Error creating source: {}" msgstr "حدث خطأ أثناء إنشاء المصدر: {}" -#: apps/px_sources/ui_views.py:162 +#: apps/px_sources/ui_views.py:176 msgid "You don't have permission to edit sources." msgstr "ليس لديك صلاحية تعديل المصادر." -#: apps/px_sources/ui_views.py:179 +#: apps/px_sources/ui_views.py:193 msgid "Source updated successfully!" msgstr "تم تحديث المصدر بنجاح!" -#: apps/px_sources/ui_views.py:183 +#: apps/px_sources/ui_views.py:197 msgid "Error updating source: {}" msgstr "حدث خطأ أثناء تحديث المصدر: {}" -#: apps/px_sources/ui_views.py:200 +#: apps/px_sources/ui_views.py:214 msgid "You don't have permission to delete sources." msgstr "ليس لديك صلاحية حذف المصادر." -#: apps/px_sources/ui_views.py:208 +#: apps/px_sources/ui_views.py:222 msgid "Source '{}' deleted successfully!" msgstr "تم حذف المصدر '{}' بنجاح!" -#: apps/px_sources/ui_views.py:227 +#: apps/px_sources/ui_views.py:241 msgid "You don't have permission to toggle source status." msgstr "ليس لديك إذن لتغيير حالة المصدر." -#: apps/px_sources/ui_views.py:233 +#: apps/px_sources/ui_views.py:247 msgid "Invalid request method." msgstr "طريقة الطلب غير صالحة." -#: apps/px_sources/ui_views.py:240 +#: apps/px_sources/ui_views.py:254 msgid "activated" msgstr "مفعل" -#: apps/px_sources/ui_views.py:240 +#: apps/px_sources/ui_views.py:254 msgid "deactivated" msgstr "غير مفعل" -#: apps/px_sources/ui_views.py:241 +#: apps/px_sources/ui_views.py:255 msgid "Source '{}' {} successfully." msgstr "تم تفعيل المصدر '{}' بنجاح." -#: apps/px_sources/ui_views.py:300 apps/px_sources/ui_views.py:591 -#: apps/px_sources/ui_views.py:676 +#: apps/px_sources/ui_views.py:314 apps/px_sources/ui_views.py:605 +#: apps/px_sources/ui_views.py:690 msgid "" "You are not assigned as a source user. Please contact your administrator." msgstr "لم يتم تعيينك كمستخدم مصدر. يرجى التواصل مع مدير النظام." -#: apps/px_sources/ui_views.py:388 +#: apps/px_sources/ui_views.py:402 msgid "Please select a user." msgstr "يرجى اختيار مستخدم." -#: apps/px_sources/ui_views.py:395 +#: apps/px_sources/ui_views.py:409 msgid "User already has a source profile. A user can only manage one source." msgstr "المستخدم لديه ملف مصدر بالفعل. يمكن للمستخدم إدارة مصدر واحد فقط." -#: apps/px_sources/ui_views.py:409 +#: apps/px_sources/ui_views.py:423 msgid "Email is required." msgstr "البريد الإلكتروني مطلوب." -#: apps/px_sources/ui_views.py:411 +#: apps/px_sources/ui_views.py:425 msgid "A user with this email already exists." msgstr "يوجد مستخدم بهذا البريد الإلكتروني بالفعل." -#: apps/px_sources/ui_views.py:414 +#: apps/px_sources/ui_views.py:428 msgid "First name is required." msgstr "الاسم الأول مطلوب." -#: apps/px_sources/ui_views.py:417 +#: apps/px_sources/ui_views.py:431 msgid "Last name is required." msgstr "اسم العائلة مطلوب." -#: apps/px_sources/ui_views.py:420 +#: apps/px_sources/ui_views.py:434 msgid "Password is required." msgstr "كلمة المرور مطلوبة." -#: apps/px_sources/ui_views.py:422 +#: apps/px_sources/ui_views.py:436 msgid "Password must be at least 8 characters." msgstr "يجب أن تكون كلمة المرور بطول 8 أحرف على الأقل." -#: apps/px_sources/ui_views.py:425 +#: apps/px_sources/ui_views.py:439 msgid "Passwords do not match." msgstr "كلمات المرور غير متطابقة." -#: apps/px_sources/ui_views.py:451 +#: apps/px_sources/ui_views.py:465 msgid "New user created successfully!" msgstr "تم إنشاء المستخدم الجديد بنجاح!" -#: apps/px_sources/ui_views.py:472 +#: apps/px_sources/ui_views.py:486 msgid "Source user created successfully!" msgstr "تم إنشاء مستخدم المصدر بنجاح!" -#: apps/px_sources/ui_views.py:476 +#: apps/px_sources/ui_views.py:490 msgid "Error creating source user: {}" msgstr "حدث خطأ أثناء إنشاء مستخدم المصدر: {}" -#: apps/px_sources/ui_views.py:497 +#: apps/px_sources/ui_views.py:511 msgid "You don't have permission to edit source users." msgstr "ليس لديك صلاحية تعديل مستخدمي المصادر." -#: apps/px_sources/ui_views.py:510 +#: apps/px_sources/ui_views.py:524 msgid "Source user updated successfully!" msgstr "تم تحديث مستخدم المصدر بنجاح!" -#: apps/px_sources/ui_views.py:514 +#: apps/px_sources/ui_views.py:528 msgid "Error updating source user: {}" msgstr "حدث خطأ أثناء تحديث مستخدم المصدر: {}" -#: apps/px_sources/ui_views.py:532 +#: apps/px_sources/ui_views.py:546 msgid "You don't have permission to delete source users." msgstr "ليس لديك صلاحية حذف مستخدمي المصادر." -#: apps/px_sources/ui_views.py:541 +#: apps/px_sources/ui_views.py:555 msgid "Source user '{}' deleted successfully!" msgstr "تم حذف مستخدم المصدر '{}' بنجاح!" -#: apps/px_sources/ui_views.py:766 +#: apps/px_sources/ui_views.py:780 msgid "You don't have permission to create complaints." msgstr "ليس لديك إذن لإنشاء الشكاوى." -#: apps/px_sources/ui_views.py:984 +#: apps/px_sources/ui_views.py:998 msgid "You don't have permission to create inquiries." msgstr "ليس لديك إذن لإنشاء الاستفسارات." @@ -3637,12 +3698,13 @@ msgstr "الملف مطلوب عند رفع إصدار جديد." #: templates/complaints/adverse_action_list.html:212 #: templates/complaints/inquiry_list.html:178 #: templates/complaints/templates/template_list.html:186 +#: templates/config/hospital_users.html:203 #: templates/dashboard/my_dashboard.html:134 #: templates/feedback/feedback_list.html:231 #: templates/journeys/instance_list.html:165 -#: templates/observations/observation_list.html:169 -#: templates/organizations/patient_list.html:202 -#: templates/organizations/patient_list.html:467 +#: templates/observations/observation_list.html:170 +#: templates/organizations/patient_list.html:203 +#: templates/organizations/patient_list.html:473 #: templates/organizations/section_list.html:76 #: templates/organizations/staff_list.html:232 #: templates/organizations/subsection_list.html:76 @@ -3783,7 +3845,7 @@ msgid "Phone number must start with country code (e.g., +966)" msgstr "يجب أن يبدأ رقم الهاتف برمز الدولة (مثلاً: +966)" #: apps/surveys/forms.py:330 templates/callcenter/import_call_records.html:84 -#: templates/physicians/doctor_rating_import.html:143 +#: templates/physicians/doctor_rating_import.html:148 #: templates/surveys/manual_send_csv.html:80 msgid "CSV File" msgstr "ملف CSV" @@ -3805,7 +3867,7 @@ msgid "Upload MOH Statistics CSV with patient visit data" msgstr "تحميل إحصائيات وزارة الصحة بصيغة CSV مع بيانات زيارات المرضى" #: apps/surveys/forms.py:387 -#: templates/physicians/doctor_rating_import.html:129 +#: templates/physicians/doctor_rating_import.html:134 msgid "Skip Header Rows" msgstr "تخطي صفوف الرؤوس" @@ -3837,9 +3899,8 @@ msgstr "فئة جديدة" #: templates/accounts/onboarding/dashboard.html:124 #: templates/appreciation/category_form.html:95 #: templates/appreciation/category_list.html:100 -#: templates/layouts/partials/sidebar.html:541 +#: templates/layouts/partials/sidebar.html:546 #: templates/observations/category_form.html:13 -#: templates/observations/observation_list.html:94 #: templates/standards/category_list.html:111 msgid "Categories" msgstr "الفئات" @@ -3871,8 +3932,8 @@ msgstr "الفئات" #: templates/journeys/template_form.html:157 #: templates/journeys/template_list.html:113 #: templates/observations/category_list.html:131 -#: templates/organizations/patient_detail.html:196 -#: templates/organizations/patient_list.html:330 +#: templates/organizations/patient_detail.html:203 +#: templates/organizations/patient_list.html:336 #: templates/organizations/physician_list.html:115 #: templates/organizations/section_list.html:160 #: templates/organizations/staff_detail.html:44 @@ -4040,6 +4101,8 @@ msgstr "اللون" #: templates/complaints/inquiry_list.html:232 #: templates/complaints/oncall/schedule_detail.html:96 #: templates/complaints/templates/template_list.html:178 +#: templates/config/hospital_users.html:195 +#: templates/config/hospital_users.html:236 #: templates/config/routing_rules.html:46 templates/config/sla_config.html:45 #: templates/dashboard/complaint_request_list.html:52 #: templates/dashboard/complaint_request_list.html:88 @@ -4066,14 +4129,14 @@ msgstr "اللون" #: templates/journeys/template_list.html:82 #: templates/observations/category_list.html:91 #: templates/observations/observation_detail.html:367 -#: templates/observations/observation_list.html:178 -#: templates/observations/observation_list.html:299 +#: templates/observations/observation_list.html:179 +#: templates/observations/observation_list.html:300 #: templates/observations/public_success.html:141 #: templates/organizations/department_list.html:165 #: templates/organizations/hospital_list.html:165 -#: templates/organizations/patient_detail.html:254 -#: templates/organizations/patient_detail.html:301 -#: templates/organizations/patient_list.html:211 +#: templates/organizations/patient_detail.html:261 +#: templates/organizations/patient_detail.html:308 +#: templates/organizations/patient_list.html:212 #: templates/organizations/patient_list.html:266 #: templates/organizations/physician_list.html:82 #: templates/organizations/section_confirm_delete.html:34 @@ -4089,7 +4152,7 @@ msgstr "اللون" #: templates/organizations/subsection_form.html:115 #: templates/organizations/subsection_list.html:89 #: templates/organizations/subsection_list.html:127 -#: templates/physicians/doctor_rating_job_list.html:100 +#: templates/physicians/doctor_rating_job_list.html:107 #: templates/physicians/physician_list.html:158 #: templates/physicians/physician_list.html:198 #: templates/projects/convert_action.html:135 @@ -4099,6 +4162,8 @@ msgstr "اللون" #: templates/projects/task_form.html:149 #: templates/projects/template_form.html:147 #: templates/px_sources/source_confirm_delete.html:73 +#: templates/px_sources/source_detail.html:250 +#: templates/px_sources/source_detail.html:318 #: templates/px_sources/source_list.html:169 #: templates/px_sources/source_list.html:200 #: templates/px_sources/source_user_complaint_list.html:113 @@ -4109,7 +4174,7 @@ msgstr "اللون" #: templates/px_sources/source_user_form.html:297 #: templates/px_sources/source_user_inquiry_list.html:113 #: templates/px_sources/source_user_inquiry_list.html:174 -#: templates/rca/rca_detail.html:114 templates/rca/rca_detail.html:502 +#: templates/rca/rca_detail.html:114 templates/rca/rca_detail.html:492 #: templates/rca/rca_form.html:85 templates/rca/rca_list.html:228 #: templates/reports/report_builder.html:90 #: templates/simulator/log_detail.html:76 @@ -4173,11 +4238,12 @@ msgstr "الحالة" #: templates/complaints/inquiry_form.html:367 #: templates/complaints/involved_department_form.html:238 #: templates/complaints/involved_staff_form.html:206 -#: templates/complaints/oncall/admin_form.html:301 +#: templates/complaints/oncall/admin_form.html:324 #: templates/complaints/oncall/schedule_form.html:293 #: templates/complaints/partials/explanation_panel.html:201 #: templates/complaints/request_explanation_form.html:247 #: templates/complaints/sla_management_form.html:354 +#: templates/config/hospital_users.html:394 #: templates/dashboard/my_dashboard.html:275 #: templates/feedback/feedback_form.html:142 #: templates/feedback/feedback_form.html:358 @@ -4204,7 +4270,8 @@ msgstr "الحالة" #: templates/organizations/staff_list.html:477 #: templates/organizations/subsection_confirm_delete.html:46 #: templates/organizations/subsection_form.html:126 -#: templates/physicians/doctor_rating_import.html:171 +#: templates/physicians/doctor_rating_fetch.html:155 +#: templates/physicians/doctor_rating_import.html:176 #: templates/physicians/doctor_rating_review.html:327 #: templates/projects/convert_action.html:92 #: templates/projects/project_confirm_delete.html:59 @@ -4224,8 +4291,8 @@ msgstr "الحالة" #: templates/px_sources/source_user_create_complaint.html:273 #: templates/px_sources/source_user_create_inquiry.html:145 #: templates/px_sources/source_user_form.html:346 -#: templates/rca/rca_detail.html:451 templates/rca/rca_detail.html:516 -#: templates/rca/rca_detail.html:551 templates/rca/rca_form.html:119 +#: templates/rca/rca_detail.html:445 templates/rca/rca_detail.html:506 +#: templates/rca/rca_detail.html:537 templates/rca/rca_form.html:119 #: templates/references/document_form.html:367 #: templates/references/folder_form.html:300 #: templates/reports/report_builder.html:200 @@ -4329,6 +4396,7 @@ msgstr "الترتيب" #: templates/complaints/partials/rca_panel.html:49 #: templates/complaints/sla_management.html:268 #: templates/complaints/sla_management.html:375 +#: templates/config/hospital_users.html:237 #: templates/dashboard/department_benchmarks.html:133 #: templates/dashboard/partials/actions_table.html:30 #: templates/dashboard/partials/complaints_table.html:30 @@ -4345,14 +4413,14 @@ msgstr "الترتيب" #: templates/journeys/template_list.html:83 #: templates/journeys/template_list.html:103 #: templates/observations/category_list.html:92 -#: templates/observations/observation_list.html:303 +#: templates/observations/observation_list.html:304 #: templates/organizations/patient_list.html:267 #: templates/organizations/physician_list.html:83 #: templates/organizations/section_list.html:128 #: templates/organizations/staff_list.html:267 #: templates/organizations/subsection_list.html:128 #: templates/physicians/department_overview.html:112 -#: templates/physicians/doctor_rating_job_list.html:104 +#: templates/physicians/doctor_rating_job_list.html:111 #: templates/physicians/leaderboard.html:184 #: templates/physicians/physician_list.html:199 #: templates/physicians/physician_ratings_dashboard.html:461 @@ -4502,6 +4570,9 @@ msgstr "عنصر جديد" #: templates/complaints/escalation_rule_list.html:253 #: templates/complaints/escalation_rule_list.html:325 #: templates/complaints/escalation_rule_list.html:334 +#: templates/config/hospital_users.html:178 +#: templates/config/hospital_users.html:188 +#: templates/config/hospital_users.html:197 #: templates/config/routing_rules.html:79 #: templates/dashboard/complaint_request_list.html:25 #: templates/dashboard/complaint_request_list.html:34 @@ -4515,9 +4586,9 @@ msgstr "عنصر جديد" #: templates/feedback/comment_list.html:40 #: templates/feedback/comment_list.html:49 #: templates/notifications/inbox.html:42 -#: templates/observations/observation_list.html:244 -#: templates/organizations/patient_list.html:221 -#: templates/organizations/patient_list.html:229 +#: templates/observations/observation_list.html:245 +#: templates/organizations/patient_list.html:222 +#: templates/organizations/patient_list.html:230 #: templates/organizations/staff_hierarchy_d3.html:192 #: templates/organizations/staff_list.html:198 #: templates/organizations/staff_list.html:206 @@ -4546,7 +4617,7 @@ msgstr "الكل" #: templates/complaints/templates/template_list.html:194 #: templates/dashboard/complaint_request_list.html:63 #: templates/feedback/action_plan_list.html:41 -#: templates/organizations/patient_list.html:238 +#: templates/organizations/patient_list.html:239 #: templates/organizations/section_list.html:99 #: templates/organizations/subsection_list.html:99 #: templates/physicians/department_overview.html:55 @@ -4571,10 +4642,11 @@ msgstr "تصفية" #: templates/complaints/complaint_list.html:203 #: templates/complaints/complaint_threshold_list.html:231 #: templates/complaints/escalation_rule_list.html:265 +#: templates/config/hospital_users.html:208 #: templates/dashboard/complaint_request_list.html:67 #: templates/journeys/instance_list.html:218 #: templates/notifications/send_sms_direct.html:77 -#: templates/observations/observation_list.html:268 +#: templates/observations/observation_list.html:269 #: templates/organizations/staff_hierarchy.html:221 #: templates/organizations/staff_list.html:237 #: templates/physicians/individual_ratings_list.html:164 @@ -4645,6 +4717,7 @@ msgid "Track acknowledgement completion rates" msgstr "تتبع معدلات إكمال الإخطار" #: templates/accounts/acknowledgements/compliance.html:39 +#: templates/config/hospital_users.html:131 msgid "Total Users" msgstr "إجمالي المستخدمين" @@ -4687,7 +4760,7 @@ msgstr "المستخدمون غير المتوافقين" #: templates/accounts/onboarding/provisional_list.html:130 #: templates/appreciation/appreciation_send_form.html:121 #: templates/layouts/partials/topbar.html:93 -#: templates/px_sources/source_detail.html:236 +#: templates/px_sources/source_detail.html:392 #: templates/px_sources/source_user_confirm_delete.html:61 msgid "User" msgstr "مستخدم" @@ -4737,7 +4810,7 @@ msgstr "مراجعة والتوقيع على الإقرارات المطلوبة #: templates/dashboard/employee_evaluation.html:965 #: templates/dashboard/employee_evaluation.html:1010 #: templates/dashboard/employee_evaluation.html:1060 -#: templates/observations/observation_list.html:109 +#: templates/observations/observation_list.html:110 #: templates/rca/rca_list.html:89 templates/social/social_platform.html:130 #: templates/standards/dashboard.html:105 #: templates/surveys/analytics_reports.html:38 @@ -4995,7 +5068,7 @@ msgid "Or copy and paste this link into your browser:" msgstr "أو انسخ الرابط التالي والصقه في متصفحك:" #: templates/accounts/email/password_reset_email.html:46 -#: templates/accounts/onboarding/invitation_email.html:120 +#: templates/accounts/onboarding/invitation_email.html:42 #: templates/emails/explanation_reminder.html:76 msgid "Important:" msgstr "مهم:" @@ -5127,7 +5200,7 @@ msgstr "جاري إرسال الدعوات..." #: templates/accounts/onboarding/bulk_invite.html:38 #: templates/callcenter/import_call_records.html:76 -#: templates/physicians/doctor_rating_import.html:104 +#: templates/physicians/doctor_rating_import.html:109 #: templates/surveys/his_patient_import.html:32 msgid "Upload CSV File" msgstr "رفع ملف CSV" @@ -5226,7 +5299,8 @@ msgstr "راقب الإكمال من لوحة التحكم" #: templates/accounts/onboarding/bulk_invite.html:152 #: templates/analytics/kpi_report_generate.html:223 -#: templates/physicians/doctor_rating_import.html:212 +#: templates/physicians/doctor_rating_fetch.html:190 +#: templates/physicians/doctor_rating_import.html:217 msgid "Quick Tips" msgstr "نصائح سريعة" @@ -5280,7 +5354,7 @@ msgstr "الافتراضي" #: templates/accounts/onboarding/provisional_list.html:221 #: templates/complaints/sla_management.html:324 #: templates/complaints/sla_management.html:419 -#: templates/px_sources/source_detail.html:343 +#: templates/px_sources/source_detail.html:499 #: templates/px_sources/source_list.html:274 msgid "Deactivate" msgstr "إلغاء التنشيط" @@ -5289,7 +5363,7 @@ msgstr "إلغاء التنشيط" #: templates/complaints/complaint_detail.html:378 #: templates/complaints/sla_management.html:324 #: templates/complaints/sla_management.html:419 -#: templates/px_sources/source_detail.html:349 +#: templates/px_sources/source_detail.html:505 #: templates/px_sources/source_list.html:274 msgid "Activate" msgstr "تفعيل" @@ -5311,7 +5385,7 @@ msgstr "تفعيل" #: templates/journeys/template_list.html:124 #: templates/journeys/template_list.html:194 #: templates/observations/category_list.html:139 -#: templates/organizations/patient_detail.html:202 +#: templates/organizations/patient_detail.html:209 #: templates/organizations/physician_list.html:118 #: templates/organizations/section_confirm_delete.html:49 #: templates/organizations/section_list.html:163 @@ -5510,6 +5584,7 @@ msgstr "نص العنصر" #: templates/accounts/settings.html:418 #: templates/complaints/involved_department_form.html:149 #: templates/complaints/involved_staff_form.html:142 +#: templates/config/hospital_users.html:186 msgid "Role" msgstr "الدور" @@ -5526,10 +5601,10 @@ msgstr "المحتوى المرتبط" #: templates/complaints/inquiry_list.html:234 #: templates/dashboard/partials/observations_table.html:28 #: templates/feedback/feedback_list.html:362 -#: templates/organizations/patient_detail.html:600 +#: templates/organizations/patient_detail.html:607 #: templates/organizations/staff_detail.html:225 #: templates/organizations/staff_detail.html:286 -#: templates/physicians/doctor_rating_job_list.html:103 +#: templates/physicians/doctor_rating_job_list.html:110 #: templates/physicians/doctor_rating_job_status.html:176 #: templates/projects/template_detail.html:125 #: templates/px_sources/source_detail.html:180 @@ -5732,6 +5807,8 @@ msgstr "غير متوفر" #: templates/accounts/onboarding/completion_email.html:44 #: templates/callcenter/inquiry_success.html:97 +#: templates/config/emails/reset_password_email.html:35 +#: templates/config/hospital_users.html:390 #: templates/core/no_hospital_assigned.html:28 #: templates/emails/appointment_confirmation.html:154 #: templates/journeys/instance_detail.html:342 @@ -5756,7 +5833,8 @@ msgstr "غير معيّن" #: templates/appreciation/appreciation_send_form.html:286 #: templates/callcenter/inquiry_success.html:109 #: templates/emails/appointment_confirmation.html:68 -#: templates/emails/explanation_request.html:87 +#: templates/emails/explanation_request.html:45 +#: templates/emails/new_observation_notification.html:46 #: templates/emails/sla_reminder.html:87 #: templates/emails/sla_second_reminder.html:90 #: templates/journeys/instance_detail.html:174 @@ -6038,7 +6116,7 @@ msgstr "عرض الحسابات المؤقتة" #: templates/accounts/onboarding/dashboard.html:149 #: templates/complaints/complaint_pdf.html:765 -#: templates/px_sources/source_detail.html:223 +#: templates/px_sources/source_detail.html:379 msgid "Recent Activity" msgstr "النشاط الأخير" @@ -6089,7 +6167,7 @@ msgstr "تم تفعيل جميع المستخدمين لحساباتهم" msgid "Welcome to PX360 - Al Hammadi Hospital" msgstr "مرحبًا بك في PX360 - مستشفى الحمادي" -#: templates/accounts/onboarding/invitation_email.html:6 +#: templates/accounts/onboarding/invitation_email.html:5 msgid "You have been invited to join PX360. Complete your account setup." msgstr "لقد تمت دعوتك للانضمام إلى PX360. أكمل إعداد حسابك." @@ -6098,17 +6176,13 @@ msgstr "لقد تمت دعوتك للانضمام إلى PX360. أكمل إعد msgid "Welcome to PX360!" msgstr "مرحبًا بك في PX360!" -#: templates/accounts/onboarding/invitation_email.html:10 -msgid "Your comprehensive Patient Experience management platform" -msgstr "منصة إدارة تجربة المريض الشاملة الخاصة بك" - -#: templates/accounts/onboarding/invitation_email.html:18 +#: templates/accounts/onboarding/invitation_email.html:11 #: templates/accounts/onboarding/reminder_email.html:18 #, python-format msgid "Hello %(name)s," msgstr "مرحبًا %(name)s،" -#: templates/accounts/onboarding/invitation_email.html:21 +#: templates/accounts/onboarding/invitation_email.html:14 msgid "" "You have been invited to join PX360, our comprehensive Patient Experience " "management platform. To complete your account setup, please click the button" @@ -6117,49 +6191,49 @@ msgstr "" "لقد تمت دعوتك للانضمام إلى PX360، منصتنا الشاملة لإدارة تجربة المريض. لإكمال" " إعداد حسابك، يرجى النقر على الزر أدناه." -#: templates/accounts/onboarding/invitation_email.html:32 -msgid "During the onboarding process, you will:" +#: templates/accounts/onboarding/invitation_email.html:19 +#, fuzzy +#| msgid "During the onboarding process, you will:" +msgid "During onboarding, you will:" msgstr "أثناء عملية الإعداد، ستقوم بما يلي:" -#: templates/accounts/onboarding/invitation_email.html:46 -msgid "Learn about PX360 features and your role responsibilities" +#: templates/accounts/onboarding/invitation_email.html:21 +#, fuzzy +#| msgid "Learn about PX360 features and your role responsibilities" +msgid "Learn about PX360 features and your role" msgstr "تعلم ميزات PX360 ومسؤوليات دورك" -#: templates/accounts/onboarding/invitation_email.html:60 +#: templates/accounts/onboarding/invitation_email.html:22 msgid "Set up your profile and preferences" msgstr "قم بإعداد ملفك الشخصي وتفضيلاتك" -#: templates/accounts/onboarding/invitation_email.html:74 -msgid "Complete required training materials" +#: templates/accounts/onboarding/invitation_email.html:23 +#, fuzzy +#| msgid "Complete required training materials" +msgid "Complete required training" msgstr "إكمال المواد التدريبية المطلوبة" -#: templates/accounts/onboarding/invitation_email.html:88 -msgid "Activate your account and start using PX360" -msgstr "تفعيل حسابك والبدء باستخدام PX360" +#: templates/accounts/onboarding/invitation_email.html:24 +#, fuzzy +#| msgid "Create Account" +msgid "Activate your account" +msgstr "إنشاء حساب" -#: templates/accounts/onboarding/invitation_email.html:106 +#: templates/accounts/onboarding/invitation_email.html:34 msgid "Complete Account Setup" msgstr "إكمال إعداد الحساب" -#: templates/accounts/onboarding/invitation_email.html:120 -msgid "" -"This invitation link will expire in 7 days. If you don't complete the setup " -"within this period, you'll need to request a new invitation." -msgstr "" -"ستنتهي صلاحية رابط الدعوة هذا خلال 7 أيام. إذا لم تكمل الإعداد خلال هذه " -"الفترة، ستحتاج إلى طلب دعوة جديدة." +#: templates/accounts/onboarding/invitation_email.html:42 +#, fuzzy +#| msgid "Your invitation will expire in" +msgid "This invitation link will expire in 7 days." +msgstr "ستنتهي صلاحية دعوتك في" -#: templates/accounts/onboarding/invitation_email.html:131 -msgid "" -"If you have any questions or need assistance, please contact our support " -"team at" +#: templates/accounts/onboarding/invitation_email.html:47 +msgid "Need help? Contact support@alhammadi.com or call +966 11 123 4567." msgstr "" -"إذا كان لديك أي أسئلة أو تحتاج إلى مساعدة، يرجى التواصل مع فريق الدعم لدينا " -"على" - -#: templates/accounts/onboarding/invitation_email.html:133 -msgid "or call us at" -msgstr "أو اتصل بنا على" +"تحتاج إلى مساعدة؟ اتصل بـ support@alhammadi.com أو اتصل على +966 11 123 " +"4567." #: templates/accounts/onboarding/preview_wizard.html:4 msgid "Preview Onboarding" @@ -6349,7 +6423,7 @@ msgstr "الرقم الوظيفي" #: templates/accounts/onboarding/provisional_list.html:285 #: templates/accounts/settings.html:79 -#: templates/organizations/patient_detail.html:220 +#: templates/organizations/patient_detail.html:227 #: templates/organizations/staff_form.html:113 #: templates/px_sources/source_user_form.html:218 msgid "First Name" @@ -6357,7 +6431,7 @@ msgstr "الاسم الأول" #: templates/accounts/onboarding/provisional_list.html:294 #: templates/accounts/settings.html:86 -#: templates/organizations/patient_detail.html:225 +#: templates/organizations/patient_detail.html:232 #: templates/organizations/staff_form.html:128 #: templates/px_sources/source_user_form.html:228 msgid "Last Name" @@ -6601,25 +6675,58 @@ msgstr "جاري المعالجة..." msgid "Failed to acknowledge item. Please try again." msgstr "فشل التصعيد. يرجى المحاولة مرة أخرى." -#: templates/accounts/onboarding/step_content.html:18 -msgid "Review Onboarding Material" -msgstr "مراجعة مواد الإعداد" +#: templates/accounts/onboarding/step_content.html:34 +msgid "Step" +msgstr "الخطوة" -#: templates/accounts/onboarding/step_content.html:21 -msgid "Please review the following important information" -msgstr "يرجى مراجعة المعلومات المهمة التالية" - -#: templates/accounts/onboarding/step_content.html:28 -msgid "Step 3 of 3" -msgstr "الخطوة 3 من 3" - -#: templates/accounts/onboarding/step_content.html:61 -msgid "I have reviewed all the onboarding material above" +#: templates/accounts/onboarding/step_content.html:81 +#, fuzzy +#| msgid "I have reviewed all the onboarding material above" +msgid "I have reviewed the onboarding material above" msgstr "لقد قمت بمراجعة جميع مواد الإعداد المذكورة أعلاه" -#: templates/accounts/onboarding/step_content.html:70 -msgid "Complete Onboarding" -msgstr "إكمال التسجيل" +#: templates/accounts/onboarding/step_content.html:93 +#: templates/actions/action_list.html:219 +#: templates/appreciation/appreciation_list.html:291 +#: templates/appreciation/appreciation_list.html:295 +#: templates/appreciation/badge_list.html:156 +#: templates/appreciation/badge_list.html:160 +#: templates/appreciation/leaderboard.html:145 +#: templates/appreciation/leaderboard.html:150 +#: templates/appreciation/my_badges.html:102 +#: templates/appreciation/my_badges.html:107 +#: templates/callcenter/call_records_list.html:304 +#: templates/callcenter/complaint_list.html:249 +#: templates/callcenter/inquiry_list.html:241 +#: templates/complaints/adverse_action_list.html:369 +#: templates/complaints/complaint_threshold_list.html:342 +#: templates/complaints/escalation_rule_list.html:390 +#: templates/complaints/inquiry_list.html:313 +#: templates/dashboard/complaint_request_list.html:140 +#: templates/dashboard/partials/actions_table.html:93 +#: templates/dashboard/partials/complaints_table.html:89 +#: templates/dashboard/partials/feedback_table.html:85 +#: templates/dashboard/partials/inquiries_table.html:87 +#: templates/dashboard/partials/observations_table.html:79 +#: templates/dashboard/partials/tasks_table.html:87 +#: templates/px_sources/source_user_complaint_list.html:275 +#: templates/px_sources/source_user_inquiry_list.html:254 +#: templates/references/search.html:303 +#: templates/reports/saved_reports.html:149 +msgid "Previous" +msgstr "السابق" + +#: templates/accounts/onboarding/step_content.html:98 +#, fuzzy +#| msgid "Next Steps" +msgid "Next Step" +msgstr "الخطوات التالية" + +#: templates/accounts/onboarding/step_content.html:103 +#, fuzzy +#| msgid "Complete Your Checklist" +msgid "Continue to Checklist" +msgstr "أكمل قائمة التحقق الخاصة بك" #: templates/accounts/onboarding/welcome.html:21 msgid "Your journey to better patient experience starts here" @@ -6668,6 +6775,10 @@ msgid "Reset Password - PX360" msgstr "إعادة تعيين كلمة المرور - PX360" #: templates/accounts/password_reset.html:37 +#: templates/config/hospital_users.html:286 +#: templates/config/hospital_users.html:382 +#: templates/config/hospital_users.html:395 +#: templates/config/hospital_users.html:485 #: templates/organizations/staff_detail.html:235 msgid "Reset Password" msgstr "إعادة تعيين كلمة المرور" @@ -6719,7 +6830,7 @@ msgstr "الإعدادات" msgid "Manage your account preferences and configurations" msgstr "إدارة تفضيلات وإعدادات حسابك" -#: templates/accounts/settings.html:28 templates/config/dashboard.html:177 +#: templates/accounts/settings.html:28 #: templates/layouts/partials/topbar.html:50 #: templates/notifications/inbox.html:5 templates/notifications/inbox.html:14 msgid "Notifications" @@ -6955,11 +7066,10 @@ msgstr "تفاصيل إشعار الاستلام" #: templates/callcenter/complaint_form.html:169 #: templates/callcenter/complaint_list.html:177 #: templates/complaints/complaint_form.html:679 -#: templates/core/public_submit.html:509 +#: templates/core/public_submit.html:504 #: templates/dashboard/partials/actions_table.html:25 #: templates/dashboard/partials/tasks_table.html:25 #: templates/emails/new_complaint_admin_notification.html:42 -#: templates/emails/new_observation_notification.html:41 #: templates/emails/observation_assigned.html:41 #: templates/emails/observation_monthly_followup.html:41 #: templates/emails/observation_resolved.html:41 @@ -7118,7 +7228,7 @@ msgstr "تعديل الإقرار" #: templates/accounts/simple_acknowledgements/admin_form.html:95 #: templates/accounts/simple_acknowledgements/admin_list.html:60 -#: templates/layouts/partials/sidebar.html:469 +#: templates/layouts/partials/sidebar.html:474 msgid "Acknowledgements" msgstr "الإقرارات" @@ -7201,10 +7311,10 @@ msgstr "كل التأكيدات" #: templates/dashboard/partials/tasks_table.html:63 #: templates/feedback/feedback_list.html:434 #: templates/journeys/template_list.html:108 -#: templates/organizations/patient_detail.html:364 -#: templates/organizations/patient_list.html:324 +#: templates/organizations/patient_detail.html:371 +#: templates/organizations/patient_list.html:330 #: templates/organizations/staff_list.html:335 -#: templates/physicians/doctor_rating_job_list.html:191 +#: templates/physicians/doctor_rating_job_list.html:198 #: templates/physicians/leaderboard.html:276 #: templates/physicians/physician_list.html:257 #: templates/physicians/ratings_list.html:285 @@ -7333,7 +7443,7 @@ msgstr "عرض جميع الإقرارات الموقعة" #: templates/accounts/simple_acknowledgements/admin_signatures.html:23 #: templates/dashboard/admin_evaluation.html:313 -#: templates/organizations/patient_list.html:132 +#: templates/organizations/patient_list.html:133 #: templates/reports/report_builder.html:144 msgid "Export CSV" msgstr "تصدير CSV" @@ -7382,10 +7492,12 @@ msgstr "الإقرار" #: templates/dashboard/complaint_request_list.html:83 #: templates/dashboard/partials/feedback_table.html:28 #: templates/feedback/comment_import_list.html:24 -#: templates/observations/observation_list.html:295 +#: templates/observations/observation_list.html:296 #: templates/physicians/doctor_rating_review.html:177 #: templates/physicians/individual_ratings_list.html:183 -#: templates/px_sources/source_detail.html:232 +#: templates/px_sources/source_detail.html:253 +#: templates/px_sources/source_detail.html:321 +#: templates/px_sources/source_detail.html:388 #: templates/px_sources/source_user_dashboard.html:165 #: templates/px_sources/source_user_dashboard.html:257 #: templates/standards/attachment_upload.html:201 @@ -7491,7 +7603,7 @@ msgid "Create New" msgstr "إنشاء جديد" #: templates/accounts/simple_acknowledgements/list.html:74 -#: templates/px_sources/source_detail.html:323 +#: templates/px_sources/source_detail.html:479 msgid "Manage" msgstr "إدارة" @@ -7603,7 +7715,7 @@ msgstr "صف خطة العمل..." #: templates/actions/action_create.html:45 #: templates/actions/action_detail.html:453 #: templates/observations/observation_detail.html:299 -#: templates/observations/observation_list.html:229 +#: templates/observations/observation_list.html:230 #: templates/projects/task_form.html:160 templates/rca/rca_detail.html:142 #: templates/rca/rca_form.html:103 templates/rca/rca_list.html:232 msgid "Assigned To" @@ -7656,7 +7768,7 @@ msgstr "حفظ" #: templates/actions/action_detail.html:5 #: templates/complaints/complaint_threshold_list.html:258 -#: templates/organizations/patient_list.html:502 +#: templates/organizations/patient_list.html:508 #: templates/surveys/template_form.html:479 #: templates/surveys/template_form.html:525 msgid "Action" @@ -7708,7 +7820,7 @@ msgstr "الأدلة" #: templates/actions/action_detail.html:142 #: templates/complaints/partials/attachments_panel.html:3 #: templates/complaints/public_complaint_form.html:314 -#: templates/core/public_submit.html:534 +#: templates/core/public_submit.html:529 #: templates/feedback/feedback_detail.html:669 #: templates/observations/observation_create.html:316 #: templates/observations/observation_detail.html:211 @@ -7733,7 +7845,6 @@ msgstr "النتيجة" #: templates/dashboard/command_center.html:130 #: templates/dashboard/employee_evaluation.html:631 #: templates/observations/observation_detail.html:148 -#: templates/observations/public_track.html:143 #: templates/px_sources/source_detail.html:184 #: templates/reports/report_detail.html:216 #: templates/standards/dashboard.html:261 @@ -7867,8 +7978,8 @@ msgstr "تحويل إلى مشروع" #: templates/complaints/complaint_detail.html:620 #: templates/observations/observation_detail.html:386 #: templates/observations/observation_detail.html:402 -#: templates/rca/rca_detail.html:369 templates/rca/rca_detail.html:532 -#: templates/rca/rca_detail.html:552 +#: templates/rca/rca_detail.html:369 templates/rca/rca_detail.html:522 +#: templates/rca/rca_detail.html:539 msgid "Add Note" msgstr "إضافة ملاحظة" @@ -7885,8 +7996,7 @@ msgstr "معلومات التعيين" #: templates/actions/action_detail.html:456 #: templates/complaints/complaint_list.html:219 #: templates/complaints/inquiry_list.html:235 -#: templates/observations/observation_list.html:302 -#: templates/observations/public_track.html:167 +#: templates/observations/observation_list.html:303 msgid "Assigned" msgstr "تم التعيين" @@ -7894,7 +8004,7 @@ msgstr "تم التعيين" #: templates/complaints/complaint_list.html:285 #: templates/complaints/inquiry_list.html:291 #: templates/feedback/action_plan_list.html:52 -#: templates/observations/observation_list.html:376 +#: templates/observations/observation_list.html:377 #: templates/rca/rca_list.html:268 msgid "Unassigned" msgstr "غير معين" @@ -7963,11 +8073,12 @@ msgstr "إدارة خطط تحسين الإجراءات" #: templates/complaints/escalation_rule_list.html:237 #: templates/complaints/inquiry_list.html:172 #: templates/complaints/templates/template_list.html:173 +#: templates/config/hospital_users.html:170 #: templates/dashboard/admin_evaluation.html:239 #: templates/dashboard/employee_evaluation.html:641 #: templates/feedback/feedback_list.html:220 #: templates/journeys/instance_list.html:154 -#: templates/observations/observation_list.html:158 +#: templates/observations/observation_list.html:159 #: templates/organizations/section_list.html:71 #: templates/organizations/staff_list.html:191 #: templates/organizations/subsection_list.html:71 @@ -8022,36 +8133,6 @@ msgstr "لم يتم العثور على خطط إجراءات" msgid "Get started by creating your first action plan" msgstr "ابدأ بإنشاء خطة الإجراءات الأولى الخاصة بك" -#: templates/actions/action_list.html:219 -#: templates/appreciation/appreciation_list.html:291 -#: templates/appreciation/appreciation_list.html:295 -#: templates/appreciation/badge_list.html:156 -#: templates/appreciation/badge_list.html:160 -#: templates/appreciation/leaderboard.html:145 -#: templates/appreciation/leaderboard.html:150 -#: templates/appreciation/my_badges.html:102 -#: templates/appreciation/my_badges.html:107 -#: templates/callcenter/call_records_list.html:304 -#: templates/callcenter/complaint_list.html:249 -#: templates/callcenter/inquiry_list.html:241 -#: templates/complaints/adverse_action_list.html:369 -#: templates/complaints/complaint_threshold_list.html:342 -#: templates/complaints/escalation_rule_list.html:390 -#: templates/complaints/inquiry_list.html:313 -#: templates/dashboard/complaint_request_list.html:140 -#: templates/dashboard/partials/actions_table.html:93 -#: templates/dashboard/partials/complaints_table.html:89 -#: templates/dashboard/partials/feedback_table.html:85 -#: templates/dashboard/partials/inquiries_table.html:87 -#: templates/dashboard/partials/observations_table.html:79 -#: templates/dashboard/partials/tasks_table.html:87 -#: templates/px_sources/source_user_complaint_list.html:275 -#: templates/px_sources/source_user_inquiry_list.html:254 -#: templates/references/search.html:303 -#: templates/reports/saved_reports.html:149 -msgid "Previous" -msgstr "السابق" - #: templates/actions/action_list.html:224 #, python-format msgid "Page %(current)s of %(total)s" @@ -8105,7 +8186,7 @@ msgstr "إدخال النص" #: templates/ai_engine/analyze_text.html:29 #: templates/ai_engine/sentiment_detail.html:18 #: templates/complaints/inquiry_detail.html:766 -#: templates/complaints/partials/ai_panel.html:167 +#: templates/complaints/partials/ai_panel.html:166 #: templates/surveys/comment_list.html:300 msgid "Analyzing..." msgstr "جارٍ التحليل..." @@ -8115,7 +8196,7 @@ msgid "Analysis Options" msgstr "خيارات التحليل" #: templates/ai_engine/analyze_text.html:80 -#: templates/complaints/partials/ai_panel.html:12 +#: templates/complaints/partials/ai_panel.html:14 msgid "Analyze" msgstr "تحليل" @@ -8129,7 +8210,7 @@ msgstr "نتائج التحليل" #: templates/ai_engine/sentiment_list.html:203 #: templates/ai_engine/tags/sentiment_card.html:19 #: templates/dashboard/staff_performance_detail.html:298 -#: templates/organizations/patient_detail.html:419 +#: templates/organizations/patient_detail.html:426 #: templates/simulator/log_detail.html:171 #: templates/social/partials/ai_analysis_bilingual.html:46 #: templates/surveys/instance_detail.html:251 @@ -8145,8 +8226,8 @@ msgstr "النتيجة" #: templates/analytics/command_center.html:570 #: templates/complaints/inquiry_detail.html:319 #: templates/complaints/inquiry_detail.html:798 -#: templates/complaints/partials/ai_panel.html:28 -#: templates/complaints/partials/ai_panel.html:199 +#: templates/complaints/partials/ai_panel.html:30 +#: templates/complaints/partials/ai_panel.html:198 #: templates/observations/partials/ai_panel.html:18 #: templates/social/partials/ai_analysis_bilingual.html:56 #: templates/social/social_comment_detail.html:126 @@ -8323,11 +8404,11 @@ msgstr "تحليل المشاعر" #: templates/dashboard/partials/actions_table.html:26 #: templates/feedback/feedback_list.html:239 #: templates/feedback/feedback_list.html:354 -#: templates/organizations/patient_detail.html:297 -#: templates/organizations/patient_list.html:501 +#: templates/organizations/patient_detail.html:304 +#: templates/organizations/patient_list.html:507 #: templates/organizations/staff_list.html:204 #: templates/organizations/staff_list.html:261 -#: templates/px_sources/source_detail.html:233 +#: templates/px_sources/source_detail.html:389 #: templates/px_sources/source_list.html:198 #: templates/references/folder_view.html:240 #: templates/references/search.html:233 @@ -8370,7 +8451,7 @@ msgid "AI Model" msgstr "نموذج الذكاء الاصطناعي" #: templates/ai_engine/sentiment_detail.html:174 -#: templates/organizations/patient_detail.html:607 +#: templates/organizations/patient_detail.html:614 #: templates/organizations/staff_detail.html:290 msgid "Updated" msgstr "تاريخ التحديث" @@ -8395,8 +8476,8 @@ msgstr "تحليل مشاعر النصوص باستخدام الذكاء الا #: templates/ai_engine/sentiment_list.html:84 #: templates/layouts/partials/sidebar.html:106 #: templates/layouts/partials/sidebar.html:348 -#: templates/layouts/partials/sidebar.html:491 -#: templates/layouts/partials/sidebar.html:526 +#: templates/layouts/partials/sidebar.html:496 +#: templates/layouts/partials/sidebar.html:531 #: templates/layouts/partials/topbar.html:15 #: templates/layouts/source_user_base.html:141 #: templates/physicians/leaderboard.html:52 @@ -8423,13 +8504,13 @@ msgstr "الحد الأدنى للثقة" #: templates/complaints/escalation_rule_list.html:261 #: templates/dashboard/my_dashboard.html:160 #: templates/journeys/instance_list.html:215 -#: templates/observations/observation_list.html:265 +#: templates/observations/observation_list.html:266 msgid "Apply Filters" msgstr "تطبيق الفلاتر" #: templates/ai_engine/sentiment_list.html:187 -#: templates/organizations/patient_list.html:489 -#: templates/physicians/doctor_rating_job_list.html:102 +#: templates/organizations/patient_list.html:495 +#: templates/physicians/doctor_rating_job_list.html:109 #: templates/references/search.html:218 msgid "Results" msgstr "النتائج" @@ -8611,7 +8692,7 @@ msgstr "نطاق مخصص" #: templates/dashboard/admin_evaluation.html:278 #: templates/dashboard/employee_evaluation.html:678 #: templates/journeys/instance_list.html:194 -#: templates/observations/observation_list.html:218 +#: templates/observations/observation_list.html:219 #: templates/organizations/section_list.html:82 #: templates/organizations/staff_hierarchy.html:202 #: templates/physicians/leaderboard.html:135 @@ -8646,9 +8727,10 @@ msgstr "فئة مؤشرات الأداء" #: templates/dashboard/staff_performance_detail.html:269 #: templates/layouts/partials/sidebar.html:153 #: templates/layouts/source_user_base.html:173 -#: templates/organizations/patient_detail.html:279 -#: templates/organizations/patient_detail.html:556 +#: templates/organizations/patient_detail.html:286 +#: templates/organizations/patient_detail.html:563 #: templates/px_sources/source_detail.html:208 +#: templates/px_sources/source_detail.html:230 #: templates/px_sources/source_user_confirm_delete.html:96 msgid "Complaints" msgstr "الشكاوى" @@ -8657,8 +8739,8 @@ msgstr "الشكاوى" #: templates/analytics/command_center.html:392 #: templates/dashboard/command_center.html:660 #: templates/layouts/partials/sidebar.html:233 -#: templates/organizations/patient_detail.html:274 -#: templates/organizations/patient_detail.html:547 +#: templates/organizations/patient_detail.html:281 +#: templates/organizations/patient_detail.html:554 #: templates/physicians/department_overview.html:87 #: templates/physicians/department_overview.html:110 #: templates/physicians/leaderboard.html:181 @@ -8705,7 +8787,7 @@ msgstr "إعادة تعيين" #: templates/dashboard/employee_evaluation.html:767 #: templates/dashboard/employee_evaluation.html:1380 #: templates/dashboard/staff_performance_detail.html:127 -#: templates/px_sources/source_detail.html:282 +#: templates/px_sources/source_detail.html:438 #: templates/px_sources/source_user_dashboard.html:54 msgid "Total Complaints" msgstr "إجمالي الشكاوى" @@ -9232,7 +9314,7 @@ msgstr "شكو مسند يقترب من موعد نهائي لاتفاقية م #: templates/analytics/dashboard.html:730 msgid "Risk Factors" -msgstr "" +msgstr "عوامل الخطر" #: templates/analytics/dashboard.html:779 #, fuzzy @@ -9242,7 +9324,7 @@ msgstr "الإجراء الموصى به:" #: templates/analytics/dashboard.html:780 msgid "Systemic issues identified from complaint pattern analysis" -msgstr "" +msgstr "المشاكل النظامية التي تم تحديدها من تحليل نمط الشكوى" #: templates/analytics/dashboard.html:783 msgid "AI Generated" @@ -9386,7 +9468,7 @@ msgstr "النتيجة (%%)" #: templates/callcenter/complaint_success.html:94 #: templates/callcenter/inquiry_success.html:115 #: templates/complaints/complaint_pdf.html:750 -#: templates/emails/explanation_request.html:95 +#: templates/emails/new_observation_notification.html:34 #: templates/emails/observation_sla_reminder.html:65 #: templates/emails/observation_sla_second_reminder.html:69 #: templates/emails/sla_reminder.html:79 @@ -9399,7 +9481,6 @@ msgstr "الفئة:" #: templates/analytics/kpi_report_pdf.html:817 #: templates/appreciation/badge_list.html:112 #: templates/emails/new_complaint_admin_notification.html:125 -#: templates/emails/new_observation_notification.html:146 msgid "Type:" msgstr "النوع:" @@ -9581,7 +9662,6 @@ msgstr "أعدّه:" #: templates/analytics/kpi_report_pdf.html:1070 #: templates/analytics/kpi_report_pdf.html:1080 #: templates/emails/appointment_confirmation.html:52 -#: templates/emails/explanation_request.html:111 msgid "Date:" msgstr "التاريخ:" @@ -9616,7 +9696,7 @@ msgstr "إنشاء تقرير مؤشرات الأداء الرئيسية" #: templates/analytics/kpi_report_generate.html:11 #: templates/analytics/kpi_report_list.html:4 #: templates/analytics/kpi_report_list.html:61 -#: templates/layouts/partials/sidebar.html:435 +#: templates/layouts/partials/sidebar.html:440 msgid "KPI Reports" msgstr "تقارير مؤشرات الأداء الرئيسية" @@ -9806,6 +9886,7 @@ msgstr "جميع الأشهر" #: templates/analytics/kpi_report_list.html:194 #: templates/complaints/complaint_list.html:202 +#: templates/config/hospital_users.html:207 #: templates/dashboard/admin_evaluation.html:269 #: templates/dashboard/employee_evaluation.html:669 #: templates/organizations/staff_list.html:236 @@ -9834,14 +9915,16 @@ msgstr "..." #: templates/analytics/kpi_report_list.html:281 #: templates/callcenter/call_records_list.html:298 #: templates/complaints/complaint_list.html:317 +#: templates/config/hospital_users.html:224 +#: templates/config/hospital_users.html:313 #: templates/dashboard/partials/actions_table.html:88 #: templates/dashboard/partials/complaints_table.html:84 #: templates/dashboard/partials/feedback_table.html:80 #: templates/dashboard/partials/inquiries_table.html:82 #: templates/dashboard/partials/observations_table.html:74 #: templates/dashboard/partials/tasks_table.html:82 -#: templates/observations/observation_list.html:283 -#: templates/organizations/patient_list.html:364 +#: templates/observations/observation_list.html:284 +#: templates/organizations/patient_list.html:370 #: templates/organizations/staff_list.html:253 #: templates/organizations/staff_list.html:379 #: templates/physicians/doctor_rating_review.html:166 @@ -9859,6 +9942,7 @@ msgstr "عرض" #: templates/analytics/kpi_report_list.html:281 #: templates/complaints/complaint_list.html:317 +#: templates/config/hospital_users.html:313 #: templates/organizations/staff_list.html:379 #: templates/physicians/doctor_rating_review.html:267 #: templates/physicians/individual_ratings_list.html:312 @@ -9871,7 +9955,8 @@ msgstr "إدخالات" #: templates/analytics/kpi_report_list.html:290 #: templates/complaints/complaint_list.html:326 -#: templates/organizations/patient_list.html:372 +#: templates/config/hospital_users.html:321 +#: templates/organizations/patient_list.html:378 #: templates/organizations/staff_list.html:388 templates/rca/rca_list.html:312 #: templates/surveys/instance_list.html:234 msgid "Show" @@ -10040,8 +10125,8 @@ msgstr "من" #: templates/appreciation/appreciation_detail.html:49 #: templates/observations/observation_detail.html:79 -#: templates/observations/observation_list.html:142 -#: templates/observations/observation_list.html:360 +#: templates/observations/observation_list.html:143 +#: templates/observations/observation_list.html:361 msgid "Anonymous" msgstr "مجهول" @@ -10077,7 +10162,6 @@ msgstr "معلومات سريعة" #: templates/appreciation/appreciation_send_form.html:292 #: templates/callcenter/complaint_success.html:89 #: templates/callcenter/inquiry_success.html:103 -#: templates/emails/explanation_request.html:79 #: templates/journeys/instance_detail.html:170 #: templates/journeys/stage_surveys_form.html:180 #: templates/journeys/template_confirm_delete.html:36 @@ -10125,11 +10209,6 @@ msgstr "شاراتي" msgid "Send appreciation to colleagues and celebrate achievements" msgstr "أرسل تقديرًا لزملائك واحتفل بالإنجازات" -#: templates/appreciation/appreciation_list.html:75 -#: templates/appreciation/leaderboard.html:87 -msgid "Received" -msgstr "المستلمة" - #: templates/appreciation/appreciation_list.html:97 #: templates/appreciation/my_badges.html:46 msgid "Badges Earned" @@ -10217,8 +10296,8 @@ msgstr "اختياري: اختر إذا كان مرتبطًا بقسم معين" #: templates/appreciation/appreciation_send_form.html:156 #: templates/complaints/public_inquiry_form.html:67 -#: templates/core/public_submit.html:472 templates/core/public_submit.html:677 -#: templates/core/public_submit.html:897 +#: templates/core/public_submit.html:467 templates/core/public_submit.html:672 +#: templates/core/public_submit.html:893 msgid "Select Category" msgstr "اختر الفئة" @@ -10431,7 +10510,7 @@ msgstr "مرات" #: templates/complaints/complaint_pdf.html:481 #: templates/complaints/complaint_pdf.html:727 #: templates/complaints/explanation_already_submitted.html:96 -#: templates/emails/explanation_request.html:103 +#: templates/emails/new_observation_notification.html:40 #: templates/emails/observation_sla_reminder.html:83 #: templates/emails/observation_sla_second_reminder.html:87 #: templates/emails/sla_reminder.html:131 @@ -10577,7 +10656,7 @@ msgstr "تقدم الشارات" #: templates/appreciation/my_badges.html:187 #: templates/journeys/instance_list.html:241 -#: templates/physicians/doctor_rating_job_list.html:101 +#: templates/physicians/doctor_rating_job_list.html:108 #: templates/surveys/bulk_job_list.html:86 #: templates/surveys/bulk_job_status.html:39 msgid "Progress" @@ -10615,7 +10694,7 @@ msgstr "أظهر الابتكار" #: templates/callcenter/call_records_list.html:60 #: templates/callcenter/call_records_list.html:209 #: templates/callcenter/import_call_records.html:63 -#: templates/config/dashboard.html:203 +#: templates/config/dashboard.html:206 msgid "Call Records" msgstr "سجلات المكالمات" @@ -10675,11 +10754,13 @@ msgid "Call Type" msgstr "نوع المكالمة" #: templates/callcenter/call_records_list.html:178 +#: templates/physicians/doctor_rating_fetch.html:119 #: templates/physicians/individual_ratings_list.html:132 msgid "From Date" msgstr "من تاريخ" #: templates/callcenter/call_records_list.html:184 +#: templates/physicians/doctor_rating_fetch.html:130 #: templates/physicians/individual_ratings_list.html:139 msgid "To Date" msgstr "إلى تاريخ" @@ -10928,7 +11009,7 @@ msgstr "غير متاح" #: templates/callcenter/complaint_list.html:234 #: templates/complaints/complaint_list.html:305 #: templates/dashboard/partials/complaints_table.html:74 -#: templates/organizations/patient_detail.html:478 +#: templates/organizations/patient_detail.html:485 #: templates/px_sources/source_user_complaint_list.html:248 msgid "No complaints found" msgstr "لم يتم العثور على شكاوى" @@ -10956,8 +11037,9 @@ msgstr "رقم الشكوى:" #: templates/complaints/explanation_form.html:200 #: templates/complaints/explanation_success.html:65 #: templates/emails/explanation_reminder.html:43 -#: templates/emails/explanation_request.html:63 +#: templates/emails/explanation_request.html:35 #: templates/emails/explanation_second_reminder.html:46 +#: templates/emails/new_observation_notification.html:28 #: templates/emails/observation_sla_reminder.html:56 #: templates/emails/observation_sla_second_reminder.html:60 #: templates/emails/sla_reminder.html:55 @@ -10967,7 +11049,7 @@ msgstr "العنوان:" #: templates/callcenter/complaint_success.html:78 #: templates/complaints/explanation_form.html:205 -#: templates/emails/explanation_request.html:71 +#: templates/emails/explanation_request.html:40 #: templates/emails/sla_reminder.html:95 #: templates/emails/sla_second_reminder.html:98 #: templates/journeys/instance_detail.html:166 @@ -11151,7 +11233,7 @@ msgstr "ابحث عن مريض موجود أو أدخل تفاصيل الاتص #: templates/complaints/inquiry_detail.html:282 #: templates/complaints/inquiry_form.html:298 #: templates/complaints/public_inquiry_form.html:12 -#: templates/core/public_submit.html:642 +#: templates/core/public_submit.html:637 #: templates/emails/public_inquiry_notification.html:27 msgid "Inquiry Details" msgstr "تفاصيل الاستفسار" @@ -11244,9 +11326,10 @@ msgstr "لم يتم العثور على أي مرضى. يرجى إدخال بي #: templates/dashboard/staff_performance_detail.html:277 #: templates/layouts/partials/sidebar.html:163 #: templates/layouts/source_user_base.html:182 -#: templates/organizations/patient_detail.html:284 -#: templates/organizations/patient_detail.html:565 +#: templates/organizations/patient_detail.html:291 +#: templates/organizations/patient_detail.html:572 #: templates/px_sources/source_detail.html:212 +#: templates/px_sources/source_detail.html:236 #: templates/px_sources/source_user_confirm_delete.html:99 msgid "Inquiries" msgstr "الاستفسارات" @@ -11266,7 +11349,7 @@ msgstr "الاستفسارات المُنشأة عبر مركز الاتصال" #: templates/dashboard/employee_evaluation.html:780 #: templates/dashboard/employee_evaluation.html:1436 #: templates/dashboard/staff_performance_detail.html:136 -#: templates/px_sources/source_detail.html:286 +#: templates/px_sources/source_detail.html:442 #: templates/px_sources/source_user_dashboard.html:80 msgid "Total Inquiries" msgstr "إجمالي الاستفسارات" @@ -11281,7 +11364,7 @@ msgstr "عام" #: templates/callcenter/inquiry_list.html:175 #: templates/complaints/inquiry_list.html:230 #: templates/complaints/oncall/schedule_detail.html:95 -#: templates/organizations/patient_list.html:263 +#: templates/organizations/patient_list.html:264 #: templates/px_sources/source_detail.html:162 #: templates/px_sources/source_user_inquiry_list.html:173 msgid "Contact" @@ -11290,7 +11373,7 @@ msgstr "جهة الاتصال" #: templates/callcenter/inquiry_list.html:226 #: templates/complaints/inquiry_list.html:331 #: templates/dashboard/partials/inquiries_table.html:72 -#: templates/organizations/patient_detail.html:515 +#: templates/organizations/patient_detail.html:522 #: templates/px_sources/source_user_inquiry_list.html:227 msgid "No inquiries found" msgstr "لم يتم العثور على استفسارات" @@ -11404,7 +11487,7 @@ msgid "Report Adverse Action" msgstr "الإبلاغ عن الإجراء السلبي" #: templates/complaints/adverse_action_form.html:115 -#: templates/rca/rca_detail.html:481 +#: templates/rca/rca_detail.html:471 msgid "Action Type" msgstr "نوع الإجراء" @@ -11482,7 +11565,7 @@ msgstr "مرجع أو وصف..." #: templates/complaints/adverse_action_list.html:229 #: templates/complaints/escalation_rule_form.html:326 -#: templates/observations/observation_list.html:193 +#: templates/observations/observation_list.html:194 msgid "All Severities" msgstr "جميع درجات الخطورة" @@ -11563,7 +11646,6 @@ msgid "Timeline" msgstr "الجدول الزمني" #: templates/complaints/complaint_detail.html:146 -#: templates/config/dashboard.html:86 #: templates/dashboard/command_center.html:244 #: templates/dashboard/my_dashboard.html:188 #: templates/dashboard/partials/actions_table.html:6 @@ -11574,7 +11656,7 @@ msgstr "إجراءات تجربة المريض" #: templates/complaints/complaint_pdf.html:662 #: templates/complaints/inquiry_detail.html:297 #: templates/complaints/partials/ai_panel.html:6 -#: templates/complaints/partials/ai_panel.html:192 +#: templates/complaints/partials/ai_panel.html:191 #: templates/observations/partials/ai_panel.html:5 #: templates/social/partials/ai_analysis_bilingual.html:8 #: templates/social/social_comment_detail.html:111 @@ -11615,15 +11697,15 @@ msgstr "النتيجة المتوقعة" #: templates/complaints/complaint_pdf.html:496 #: templates/complaints/public_complaint_form.html:218 #: templates/journeys/instance_detail.html:323 -#: templates/organizations/patient_detail.html:216 +#: templates/organizations/patient_detail.html:223 #: templates/surveys/instance_detail.html:604 msgid "Patient Information" msgstr "معلومات المريض" #: templates/complaints/complaint_detail.html:299 #: templates/journeys/instance_detail.html:166 -#: templates/organizations/patient_detail.html:160 -#: templates/organizations/patient_list.html:261 +#: templates/organizations/patient_detail.html:161 +#: templates/organizations/patient_list.html:262 #: templates/surveys/his_patient_survey_send.html:120 #: templates/surveys/instance_detail.html:616 msgid "MRN" @@ -11831,8 +11913,8 @@ msgstr "تقديم شكوى مريض جديدة مع تتبع اتفاقية م #: templates/complaints/public_complaint_form.html:497 #: templates/complaints/public_inquiry_form.html:5 #: templates/complaints/public_inquiry_form.html:117 -#: templates/core/public_submit.html:611 templates/core/public_submit.html:718 -#: templates/core/public_submit.html:999 +#: templates/core/public_submit.html:606 templates/core/public_submit.html:713 +#: templates/core/public_submit.html:995 #: templates/feedback/feedback_form.html:148 #: templates/px_sources/source_user_create_complaint.html:58 #: templates/px_sources/source_user_create_inquiry.html:58 @@ -11902,6 +11984,8 @@ msgid "168 hours (7 days)" msgstr "١٦٨ ساعة (٧ أيام)" #: templates/complaints/complaint_form.html:781 +#: templates/complaints/complaint_form.html:812 +#: templates/complaints/complaint_form.html:847 #: templates/layouts/partials/topbar.html:59 #: templates/organizations/staff_form.html:451 #: templates/physicians/physician_ratings_dashboard.html:468 @@ -11915,6 +11999,18 @@ msgstr "جارٍ التحميل..." msgid "Error loading departments" msgstr "خطأ في تحميل الأقسام" +#: templates/complaints/complaint_form.html:833 +#, fuzzy +#| msgid "Error loading locations" +msgid "Error loading sections" +msgstr "خطأ في تحميل المواقع" + +#: templates/complaints/complaint_form.html:865 +#, fuzzy +#| msgid "Error loading locations" +msgid "Error loading subsections" +msgstr "خطأ في تحميل المواقع" + #: templates/complaints/complaint_list.html:4 #: templates/complaints/complaint_list.html:75 msgid "Complaints Registry" @@ -11929,7 +12025,7 @@ msgid "Search ID, Name or Dept..." msgstr "البحث عن معرف أو اسم أو قسم..." #: templates/complaints/complaint_list.html:86 -#: templates/observations/observation_list.html:97 +#: templates/observations/observation_list.html:98 msgid "Public Form" msgstr "النموذج العام" @@ -11963,7 +12059,7 @@ msgstr "رقم الشكوى" #: templates/complaints/sla_management.html:263 #: templates/dashboard/employee_evaluation.html:883 #: templates/feedback/feedback_detail.html:658 -#: templates/physicians/doctor_rating_job_list.html:99 +#: templates/physicians/doctor_rating_job_list.html:106 #: templates/physicians/doctor_rating_job_status.html:162 #: templates/physicians/individual_ratings_list.html:146 #: templates/physicians/individual_ratings_list.html:188 @@ -12017,7 +12113,7 @@ msgid "Created Date" msgstr "تاريخ الإنشاء:" #: templates/complaints/complaint_pdf.html:558 -#: templates/complaints/public_complaint_track.html:201 +#: templates/complaints/public_complaint_track.html:197 #: templates/complaints/sla_management_form.html:209 msgid "SLA Deadline" msgstr "الموعد النهائي لاتفاقية مستوى الخدمة (SLA)" @@ -12106,8 +12202,8 @@ msgstr "يتطلب الانتباه" #: templates/complaints/complaint_pdf.html:666 #: templates/complaints/inquiry_detail.html:313 #: templates/complaints/inquiry_detail.html:795 -#: templates/complaints/partials/ai_panel.html:21 -#: templates/complaints/partials/ai_panel.html:196 +#: templates/complaints/partials/ai_panel.html:23 +#: templates/complaints/partials/ai_panel.html:195 #: templates/observations/partials/ai_panel.html:11 msgid "Emotion Analysis" msgstr "تحليل المشاعر" @@ -12121,8 +12217,8 @@ msgstr "الثقة" #: templates/complaints/complaint_pdf.html:676 #: templates/complaints/inquiry_detail.html:323 #: templates/complaints/inquiry_detail.html:801 -#: templates/complaints/partials/ai_panel.html:33 -#: templates/complaints/partials/ai_panel.html:201 +#: templates/complaints/partials/ai_panel.html:35 +#: templates/complaints/partials/ai_panel.html:200 #: templates/observations/partials/ai_panel.html:23 msgid "Intensity" msgstr "الشدة" @@ -12132,7 +12228,7 @@ msgid "AI Summary" msgstr "ملخص الذكاء الاصطناعي" #: templates/complaints/complaint_pdf.html:699 -#: templates/complaints/partials/ai_panel.html:104 +#: templates/complaints/partials/ai_panel.html:106 #: templates/observations/partials/ai_panel.html:93 msgid "Suggested Action" msgstr "الإجراء المقترح" @@ -12185,7 +12281,6 @@ msgstr "جميع الحقوق محفوظة." #: templates/complaints/complaint_threshold_form.html:4 #: templates/complaints/complaint_threshold_list.html:4 #: templates/complaints/complaint_threshold_list.html:185 -#: templates/config/dashboard.html:59 msgid "Complaint Thresholds" msgstr "حدود الشكاوى" @@ -12361,7 +12456,7 @@ msgid "Send Email" msgstr "إرسال بريد إلكتروني" #: templates/complaints/complaint_threshold_form.html:453 -#: templates/complaints/oncall/admin_form.html:256 +#: templates/complaints/oncall/admin_form.html:279 msgid "Send email notifications" msgstr "إرسال إشعارات عبر البريد الإلكتروني" @@ -12413,7 +12508,6 @@ msgstr "قم بإنشاء أول عتبة للبدء" #: templates/complaints/escalation_rule_form.html:4 #: templates/complaints/escalation_rule_list.html:4 #: templates/complaints/escalation_rule_list.html:220 -#: templates/config/dashboard.html:46 msgid "Escalation Rules" msgstr "قواعد التصعيد" @@ -12611,7 +12705,7 @@ msgstr "معلومات الشكوى" #: templates/complaints/explanation_form.html:196 #: templates/complaints/explanation_success.html:61 #: templates/emails/explanation_reminder.html:35 -#: templates/emails/explanation_request.html:55 +#: templates/emails/explanation_request.html:30 #: templates/emails/explanation_second_reminder.html:38 #: templates/emails/public_inquiry_notification.html:32 #: templates/emails/sla_reminder.html:47 @@ -12664,7 +12758,7 @@ msgstr "تقديم الإيضاح" #: templates/complaints/explanation_form.html:99 #: templates/emails/explanation_reminder.html:102 -#: templates/emails/explanation_request.html:152 +#: templates/emails/explanation_request.html:70 msgid "Submit Your Explanation" msgstr "تقديم إيضاحك" @@ -12690,7 +12784,7 @@ msgstr "" #: templates/complaints/explanation_form.html:214 #: templates/emails/explanation_reminder.html:52 -#: templates/emails/explanation_request.html:121 +#: templates/emails/explanation_request.html:56 #: templates/emails/explanation_second_reminder.html:55 #: templates/journeys/template_detail.html:81 msgid "Description:" @@ -12808,16 +12902,16 @@ msgstr "الرد" #: templates/complaints/inquiry_detail.html:336 #: templates/complaints/inquiry_detail.html:808 -#: templates/complaints/partials/ai_panel.html:45 -#: templates/complaints/partials/ai_panel.html:208 +#: templates/complaints/partials/ai_panel.html:47 +#: templates/complaints/partials/ai_panel.html:207 #: templates/observations/partials/ai_panel.html:35 msgid "AI Summary (English)" msgstr "ملخص الذكاء الاصطناعي (الإنجليزية)" #: templates/complaints/inquiry_detail.html:344 #: templates/complaints/inquiry_detail.html:815 -#: templates/complaints/partials/ai_panel.html:52 -#: templates/complaints/partials/ai_panel.html:214 +#: templates/complaints/partials/ai_panel.html:54 +#: templates/complaints/partials/ai_panel.html:213 #: templates/observations/partials/ai_panel.html:42 msgid "AI Summary (Arabic)" msgstr "ملخص الذكاء الاصطناعي (العربية)" @@ -12948,14 +13042,14 @@ msgid "AI analysis complete" msgstr "مستوى الثقة في التحليل" #: templates/complaints/inquiry_detail.html:827 -#: templates/complaints/partials/ai_panel.html:235 +#: templates/complaints/partials/ai_panel.html:234 #, fuzzy #| msgid "Analysis confidence" msgid "Analysis failed" msgstr "مستوى الثقة في التحليل" #: templates/complaints/inquiry_detail.html:836 -#: templates/complaints/partials/ai_panel.html:241 +#: templates/complaints/partials/ai_panel.html:240 #, fuzzy #| msgid "An error occurred loading the item" msgid "An error occurred" @@ -13007,8 +13101,10 @@ msgstr "جميع الاستفسارات" #: templates/complaints/inquiry_list.html:228 #: templates/emails/new_complaint_admin_notification.html:35 -#: templates/observations/observation_list.html:294 -#: templates/px_sources/source_detail.html:234 +#: templates/observations/observation_list.html:295 +#: templates/px_sources/source_detail.html:248 +#: templates/px_sources/source_detail.html:315 +#: templates/px_sources/source_detail.html:390 #: templates/px_sources/source_user_complaint_list.html:182 #: templates/px_sources/source_user_dashboard.html:160 #: templates/px_sources/source_user_dashboard.html:253 @@ -13088,7 +13184,8 @@ msgstr "القسم الرئيسي يتحمل المسؤولية الأساسية #: templates/complaints/involved_department_form.html:197 #: templates/complaints/involved_department_form.html:219 #: templates/complaints/involved_staff_form.html:187 -#: templates/physicians/doctor_rating_import.html:93 +#: templates/physicians/doctor_rating_fetch.html:98 +#: templates/physicians/doctor_rating_import.html:98 #: templates/px_sources/source_user_form.html:252 #: templates/surveys/manual_send.html:183 #: templates/surveys/manual_send_csv.html:147 @@ -13186,98 +13283,124 @@ msgid "Select Admin" msgstr "اختر المسؤول" #: templates/complaints/oncall/admin_form.html:141 -msgid "PX Admin User" -msgstr "مسؤول المستخدم PX" +#, fuzzy +#| msgid "On-Call Admins" +msgid "On-Call User" +msgstr "المسؤولون المناوبون" #: templates/complaints/oncall/admin_form.html:144 -msgid "Select an admin..." -msgstr "اختر مسؤولًا..." +#, fuzzy +#| msgid "Select user..." +msgid "Select a user..." +msgstr "اختر المستخدم..." -#: templates/complaints/oncall/admin_form.html:150 -msgid "No available PX Admins" +#: templates/complaints/oncall/admin_form.html:146 +#, fuzzy +#| msgid "PX Admin" +msgid "PX Admins" +msgstr "مشرف PX" + +#: templates/complaints/oncall/admin_form.html:155 +#, fuzzy +#| msgid "PX Coordinator" +msgid "PX Coordinators" +msgstr "منسق تجربة المرضى" + +#: templates/complaints/oncall/admin_form.html:164 +#, fuzzy +#| msgid "Hospital Admin" +msgid "Hospital Admins" +msgstr "مشرف المستشفى" + +#: templates/complaints/oncall/admin_form.html:173 +#, fuzzy +#| msgid "No available PX Admins" +msgid "No available users" msgstr "لا يوجد مسؤولين متاحين لـ PX" -#: templates/complaints/oncall/admin_form.html:156 -msgid "All PX Admins are already assigned to this schedule." +#: templates/complaints/oncall/admin_form.html:179 +#, fuzzy +#| msgid "All PX Admins are already assigned to this schedule." +msgid "All eligible users are already assigned to this schedule." msgstr "جميع مسؤولي PX مُعينون بالفعل لهذا الجدول." -#: templates/complaints/oncall/admin_form.html:165 +#: templates/complaints/oncall/admin_form.html:188 #: templates/complaints/oncall/schedule_detail.html:92 #: templates/dashboard/employee_evaluation.html:1045 #: templates/organizations/staff_list.html:209 msgid "Admin" msgstr "مسؤول" -#: templates/complaints/oncall/admin_form.html:185 +#: templates/complaints/oncall/admin_form.html:208 #: templates/complaints/oncall/schedule_detail.html:93 msgid "Active Period" msgstr "فترة النشاط" -#: templates/complaints/oncall/admin_form.html:191 +#: templates/complaints/oncall/admin_form.html:214 #: templates/surveys/generate_enhanced_report.html:45 msgid "Start Date (Optional)" msgstr "تاريخ البدء (اختياري)" -#: templates/complaints/oncall/admin_form.html:196 +#: templates/complaints/oncall/admin_form.html:219 msgid "Leave empty for immediate activation" msgstr "اتركه فارغاً للتنشيط الفوري" -#: templates/complaints/oncall/admin_form.html:200 +#: templates/complaints/oncall/admin_form.html:223 #: templates/surveys/generate_enhanced_report.html:52 msgid "End Date (Optional)" msgstr "تاريخ الانتهاء (اختياري)" -#: templates/complaints/oncall/admin_form.html:205 +#: templates/complaints/oncall/admin_form.html:228 msgid "Leave empty for permanent assignment" msgstr "اتركه فارغًا للتعيين الدائم" -#: templates/complaints/oncall/admin_form.html:214 +#: templates/complaints/oncall/admin_form.html:237 #: templates/notifications/settings.html:5 #: templates/notifications/settings.html:211 msgid "Notification Settings" msgstr "إعدادات الإشعارات" -#: templates/complaints/oncall/admin_form.html:220 +#: templates/complaints/oncall/admin_form.html:243 msgid "Notification Priority" msgstr "أولوية الإشعار" -#: templates/complaints/oncall/admin_form.html:225 +#: templates/complaints/oncall/admin_form.html:248 msgid "Highest" msgstr "الأعلى" -#: templates/complaints/oncall/admin_form.html:225 +#: templates/complaints/oncall/admin_form.html:248 msgid "Lowest" msgstr "الأدنى" -#: templates/complaints/oncall/admin_form.html:229 +#: templates/complaints/oncall/admin_form.html:252 msgid "Lower numbers = higher priority in notification order" msgstr "الأرقام الأقل = أولوية أعلى في ترتيب الإشعارات" -#: templates/complaints/oncall/admin_form.html:237 +#: templates/complaints/oncall/admin_form.html:260 msgid "On-call assignment is active" msgstr "تعيين المناوبات نشط" -#: templates/complaints/oncall/admin_form.html:247 +#: templates/complaints/oncall/admin_form.html:270 msgid "Contact Preferences" msgstr "تفضيلات الاتصال" -#: templates/complaints/oncall/admin_form.html:265 +#: templates/complaints/oncall/admin_form.html:288 msgid "Send SMS notifications (for high priority complaints)" msgstr "إرسال إشعارات الرسائل النصية (لشكاوى الأولوية العالية)" -#: templates/complaints/oncall/admin_form.html:271 +#: templates/complaints/oncall/admin_form.html:294 msgid "SMS Phone Number (Optional)" msgstr "رقم الهاتف (رسائل نصية) (اختياري)" -#: templates/complaints/oncall/admin_form.html:278 +#: templates/complaints/oncall/admin_form.html:301 msgid "Leave empty to use the user's profile phone number" msgstr "اتركه فارغًا لاستخدام رقم هاتف ملف تعريف المستخدم" -#: templates/complaints/oncall/admin_form.html:289 +#: templates/complaints/oncall/admin_form.html:312 msgid "When are on-call admins notified?" msgstr "متى يتم إخطار مسؤولي المناوبات؟" -#: templates/complaints/oncall/admin_form.html:291 +#: templates/complaints/oncall/admin_form.html:314 msgid "" "On-call admins are notified outside of working hours (as configured in the " "schedule) via BOTH email and SMS. During working hours, ALL PX Admins are " @@ -13287,11 +13410,11 @@ msgstr "" "البريد الإلكتروني والرسائل النصية. خلال ساعات العمل، يتم إخطار جميع مسؤولي " "PX عبر البريد الإلكتروني فقط." -#: templates/complaints/oncall/admin_form.html:306 +#: templates/complaints/oncall/admin_form.html:329 msgid "Update Assignment" msgstr "تحديث التكليف" -#: templates/complaints/oncall/admin_form.html:306 +#: templates/complaints/oncall/admin_form.html:329 msgid "Add Admin" msgstr "إضافة مسؤول" @@ -13336,7 +13459,8 @@ msgstr "ساعات العمل" #: templates/complaints/oncall/dashboard.html:78 #: templates/complaints/oncall/schedule_detail.html:62 -#: templates/complaints/public_complaint_track.html:168 +#: templates/complaints/public_complaint_track.html:163 +#: templates/observations/public_track.html:163 msgid "Current Status" msgstr "الحالة الحالية" @@ -13448,7 +13572,7 @@ msgid "Working Days" msgstr "أيام العمل" #: templates/complaints/oncall/schedule_detail.html:120 -#: templates/observations/observation_list.html:283 +#: templates/observations/observation_list.html:284 #: templates/surveys/comment_list.html:344 msgid "to" msgstr "إلى" @@ -13590,8 +13714,8 @@ msgid "No PX actions created yet" msgstr "لم يتم إنشاء أي إجراءات PX حتى الآن" #: templates/complaints/partials/actions_panel.html:31 -#: templates/complaints/partials/ai_panel.html:91 -#: templates/complaints/partials/ai_panel.html:110 +#: templates/complaints/partials/ai_panel.html:93 +#: templates/complaints/partials/ai_panel.html:112 #: templates/observations/partials/ai_panel.html:81 #: templates/observations/partials/ai_panel.html:99 msgid "Create Action" @@ -13633,61 +13757,68 @@ msgstr "لم يتم الإبلاغ عن أي إجراءات سلبية لهذه msgid "Report an adverse action" msgstr "الإبلاغ عن إجراء سلبي" -#: templates/complaints/partials/ai_panel.html:61 -#: templates/complaints/partials/ai_panel.html:219 +#: templates/complaints/partials/ai_panel.html:12 +#: templates/complaints/partials/ai_panel.html:191 +#, fuzzy +#| msgid "Re-analyze" +msgid "Reanalyze" +msgstr "إعادة التحليل" + +#: templates/complaints/partials/ai_panel.html:63 +#: templates/complaints/partials/ai_panel.html:218 #: templates/observations/partials/ai_panel.html:51 msgid "Suggested Actions" msgstr "الإجراءات المقترحة" -#: templates/complaints/partials/ai_panel.html:121 +#: templates/complaints/partials/ai_panel.html:123 msgid "No AI analysis available for this complaint" msgstr "لا تتوفر تحليلات الذكاء الاصطناعي لهذه الشكوى" #: templates/complaints/partials/ai_panel.html:125 #, fuzzy -#| msgid "AI Analysis" -msgid "Run AI Analysis" -msgstr "تحليل الذكاء الاصطناعي" +#| msgid "Click Nodes" +msgid "Click \\" +msgstr "انقر على العقد" -#: templates/complaints/partials/ai_panel.html:171 +#: templates/complaints/partials/ai_panel.html:170 #, fuzzy #| msgid "Generating AI analysis... This may take a moment." msgid "Analyzing complaint with AI, this may take a moment..." msgstr "جاري إنشاء تحليل الذكاء الاصطناعي... قد يستغرق هذا بضع لحظات." -#: templates/complaints/partials/ai_panel.html:192 +#: templates/complaints/partials/ai_panel.html:191 #, fuzzy #| msgid "Analysis confidence" msgid "Analysis complete" msgstr "مستوى الثقة في التحليل" -#: templates/complaints/partials/ai_panel.html:241 +#: templates/complaints/partials/ai_panel.html:240 #, fuzzy #| msgid "Refresh" msgid "Refresh Page" msgstr "تحديث" -#: templates/complaints/partials/ai_panel.html:248 +#: templates/complaints/partials/ai_panel.html:247 #: templates/observations/partials/ai_panel.html:120 msgid "Are you sure you want to create a PX Action from this suggestion?" msgstr "هل أنت متأكد من رغبتك في إنشاء إجراء تجربة المريض من هذا الاقتراح؟" -#: templates/complaints/partials/ai_panel.html:278 +#: templates/complaints/partials/ai_panel.html:277 #: templates/observations/partials/ai_panel.html:148 msgid "CSRF token not found. Please refresh the page." msgstr "لم يتم العثور على رمز CSRF. يرجي تحديث الصفحة." -#: templates/complaints/partials/ai_panel.html:299 +#: templates/complaints/partials/ai_panel.html:298 #: templates/observations/partials/ai_panel.html:168 msgid "PX Action created successfully!" msgstr "تم إنشاء إجراء PX بنجاح!" -#: templates/complaints/partials/ai_panel.html:307 +#: templates/complaints/partials/ai_panel.html:306 #: templates/observations/partials/ai_panel.html:173 msgid "Failed to create action" msgstr "فشل في إنشاء الإجراء" -#: templates/complaints/partials/ai_panel.html:312 +#: templates/complaints/partials/ai_panel.html:311 #: templates/observations/partials/ai_panel.html:178 msgid "An error occurred while creating the action" msgstr "حدث خطأ أثناء إنشاء الإجراء" @@ -14097,7 +14228,6 @@ msgstr "شكرًا لمشاركتك تجربتك معنا" #: templates/complaints/public_complaint_success.html:87 #: templates/complaints/public_inquiry_success.html:115 #: templates/observations/convert_to_action.html:34 -#: templates/observations/public_track.html:102 msgid "Reference Number" msgstr "رقم المرجع" @@ -14128,16 +14258,16 @@ msgstr "ستتلقى تحديثات عبر الرسائل النصية القص #: templates/complaints/patient_complaint_success.html:66 #: templates/complaints/public_complaint_success.html:156 #: templates/complaints/public_complaint_track.html:4 -#: templates/complaints/public_complaint_track.html:111 +#: templates/complaints/public_complaint_track.html:106 msgid "Track Your Complaint" msgstr "تتبع شكواك" #: templates/complaints/patient_complaint_visit_form.html:60 #: templates/complaints/patient_complaint_visits.html:43 -#: templates/config/dashboard.html:164 +#: templates/config/dashboard.html:166 #: templates/organizations/hospital_list.html:4 #: templates/organizations/hospital_list.html:95 -#: templates/organizations/patient_list.html:178 +#: templates/organizations/patient_list.html:179 msgid "Hospitals" msgstr "المستشفيات" @@ -14146,19 +14276,19 @@ msgid "Complaint Form" msgstr "نموذج الشكوى" #: templates/complaints/patient_complaint_visit_form.html:88 -#: templates/organizations/patient_visit_journey.html:118 +#: templates/organizations/patient_visit_journey.html:119 msgid "Admission" msgstr "الاستشفاء" #: templates/complaints/patient_complaint_visit_form.html:92 -#: templates/organizations/patient_detail.html:299 +#: templates/organizations/patient_detail.html:306 #: templates/physicians/doctor_rating_review.html:174 #: templates/physicians/individual_ratings_list.html:184 msgid "Doctor" msgstr "طبيب" #: templates/complaints/patient_complaint_visit_form.html:97 -#: templates/organizations/patient_visit_journey.html:122 +#: templates/organizations/patient_visit_journey.html:123 msgid "Discharge" msgstr "الإفراج" @@ -14262,7 +14392,7 @@ msgstr "القسم الرئيسي" #: templates/complaints/public_complaint_form.html:289 #: templates/complaints/public_complaint_form.html:406 -#: templates/core/public_submit.html:817 +#: templates/core/public_submit.html:813 msgid "Select Main Section" msgstr "اختر القسم الرئيسي" @@ -14271,7 +14401,7 @@ msgid "Please describe your complaint in detail..." msgstr "يرجى وصف شكواك بالتفصيل..." #: templates/complaints/public_complaint_form.html:318 -#: templates/core/public_submit.html:538 +#: templates/core/public_submit.html:533 #: templates/observations/observation_create.html:323 #: templates/observations/public_new.html:173 msgid "Click to upload files" @@ -14287,7 +14417,7 @@ msgstr "خطأ في تحميل المواقع" #: templates/complaints/public_complaint_form.html:513 #: templates/complaints/public_inquiry_form.html:134 -#: templates/core/public_submit.html:387 +#: templates/core/public_submit.html:381 msgid "Submitted Successfully!" msgstr "تم الإرسال بنجاح!" @@ -14300,11 +14430,11 @@ msgstr "رقم مرجعيك هو:" #: templates/complaints/public_complaint_form.html:533 #: templates/complaints/public_inquiry_form.html:144 #: templates/complaints/public_inquiry_form.html:154 -#: templates/core/public_submit.html:451 templates/core/public_submit.html:466 -#: templates/core/public_submit.html:625 templates/core/public_submit.html:628 -#: templates/core/public_submit.html:732 templates/core/public_submit.html:735 -#: templates/core/public_submit.html:1025 -#: templates/core/public_submit.html:1034 +#: templates/core/public_submit.html:446 templates/core/public_submit.html:461 +#: templates/core/public_submit.html:620 templates/core/public_submit.html:623 +#: templates/core/public_submit.html:727 templates/core/public_submit.html:730 +#: templates/core/public_submit.html:1021 +#: templates/core/public_submit.html:1030 #: templates/dashboard/my_dashboard.html:504 #: templates/dashboard/my_dashboard.html:508 #: templates/physicians/doctor_rating_job_status.html:255 @@ -14316,8 +14446,8 @@ msgstr "خطأ" #: templates/complaints/public_complaint_form.html:534 #: templates/complaints/public_inquiry_form.html:145 #: templates/complaints/public_inquiry_form.html:155 -#: templates/core/public_submit.html:1026 -#: templates/core/public_submit.html:1035 +#: templates/core/public_submit.html:1022 +#: templates/core/public_submit.html:1031 msgid "Failed to submit. Please try again." msgstr "فشل الإرسال. يرجى المحاولة مرة أخرى." @@ -14373,53 +14503,49 @@ msgid "Submit Another" msgstr "إرسال ملاحظة أخرى" #: templates/complaints/public_complaint_success.html:188 -#: templates/layouts/public_base.html:215 +#: templates/layouts/public_base.html:228 msgid "Your feedback helps us improve our services" msgstr "ملاحظاتك تساعدنا على تحسين خدماتنا" -#: templates/complaints/public_complaint_track.html:113 +#: templates/complaints/public_complaint_track.html:108 msgid "" "Enter your reference number below to see real-time updates on your request." msgstr "أدخل رقم المرجع الخاص بك أدناه لترى تحديثات فورية على طلبك." -#: templates/complaints/public_complaint_track.html:130 +#: templates/complaints/public_complaint_track.html:125 msgid "e.g., CMP-20240101-123456" msgstr "مثال: CMP-20240101-123456" -#: templates/complaints/public_complaint_track.html:137 +#: templates/complaints/public_complaint_track.html:132 #: templates/observations/public_success.html:167 +#: templates/observations/public_track.html:132 msgid "Track Status" msgstr "تتبع الحالة" -#: templates/complaints/public_complaint_track.html:142 +#: templates/complaints/public_complaint_track.html:137 +#: templates/observations/public_track.html:137 msgid "Found in your confirmation email" msgstr "الموجود في بريدك الإلكتروني للتأكيد" -#: templates/complaints/public_complaint_track.html:152 +#: templates/complaints/public_complaint_track.html:147 msgid "Reference Not Found" msgstr "لم يتم العثور على المرجع" -#: templates/complaints/public_complaint_track.html:163 +#: templates/complaints/public_complaint_track.html:158 msgid "Case Reference" msgstr "مرجع الحالة" -#: templates/complaints/public_complaint_track.html:217 +#: templates/complaints/public_complaint_track.html:213 +#: templates/observations/public_track.html:214 msgid "Resolution Journey" msgstr "مسار الحل" -#: templates/complaints/public_complaint_track.html:235 -msgid "Status Updated" -msgstr "تم تحديث الحالة" - -#: templates/complaints/public_complaint_track.html:236 +#: templates/complaints/public_complaint_track.html:232 +#: templates/observations/public_track.html:240 msgid "Final Resolution" msgstr "الحل النهائي" -#: templates/complaints/public_complaint_track.html:237 -msgid "Update Received" -msgstr "تم استلام التحديث" - -#: templates/complaints/public_complaint_track.html:255 +#: templates/complaints/public_complaint_track.html:251 msgid "Your complaint is being reviewed. Updates will appear here." msgstr "شكواك قيد المراجعة. ستظهر التحديثات هنا." @@ -14430,27 +14556,27 @@ msgid "" msgstr "اطرح سؤالًا أو اطلب معلومات، وسنرد خلال 24–48 ساعة." #: templates/complaints/public_inquiry_form.html:68 -#: templates/core/public_submit.html:678 +#: templates/core/public_submit.html:673 msgid "General Inquiry" msgstr "استفسار عام" #: templates/complaints/public_inquiry_form.html:69 -#: templates/core/public_submit.html:679 +#: templates/core/public_submit.html:674 msgid "Services Information" msgstr "معلومات الخدمات" #: templates/complaints/public_inquiry_form.html:84 -#: templates/core/public_submit.html:689 +#: templates/core/public_submit.html:684 msgid "Brief subject of your inquiry" msgstr "موضوع مختصر للاستفسار" #: templates/complaints/public_inquiry_form.html:94 -#: templates/core/public_submit.html:695 +#: templates/core/public_submit.html:690 msgid "Please describe your inquiry in detail..." msgstr "يرجى وصف استفسارك بالتفصيل..." #: templates/complaints/public_inquiry_form.html:102 -#: templates/core/public_submit.html:701 +#: templates/core/public_submit.html:696 #: templates/px_sources/source_user_create_inquiry.html:140 msgid "Submit Inquiry" msgstr "إرسال الاستفسار" @@ -14522,7 +14648,7 @@ msgid "Enter any additional context or instructions for the recipients..." msgstr "أدخل أي سياق أو تعليمات إضافية للمستلمين..." #: templates/complaints/request_explanation_form.html:237 -#: templates/organizations/patient_detail.html:530 +#: templates/organizations/patient_detail.html:537 #: templates/simulator/log_list.html:280 #: templates/social/partials/ai_analysis_bilingual.html:75 #: templates/surveys/instance_detail.html:419 @@ -14805,155 +14931,317 @@ msgstr "إدارة إعدادات اتفاقية مستوى الخدمة للم msgid "Configure SLA" msgstr "تكوين اتفاقية مستوى الخدمة" -#: templates/config/dashboard.html:47 -msgid "Configure automatic escalation when deadlines are exceeded" -msgstr "إعداد التصعيد التلقائي عند تجاوز المهل الزمنية" - -#: templates/config/dashboard.html:50 -#, fuzzy -#| msgid "Manage Schedules" -msgid "Manage Rules" -msgstr "إدارة الجداول" - -#: templates/config/dashboard.html:60 -msgid "Set alert thresholds for complaint volume monitoring" -msgstr "تحديد حدود التنبيه لمراقبة حجم الشكاوى" - -#: templates/config/dashboard.html:63 -#, fuzzy -#| msgid "Thresholds" -msgid "Manage Thresholds" -msgstr "الحدود" - -#: templates/config/dashboard.html:72 +#: templates/config/dashboard.html:73 msgid "On-Call Schedules" msgstr "جداول المناوبات" -#: templates/config/dashboard.html:73 +#: templates/config/dashboard.html:74 msgid "active schedules" msgstr "الجداول النشطة" -#: templates/config/dashboard.html:76 +#: templates/config/dashboard.html:77 msgid "Manage On-Call" msgstr "إدارة المناوبات" -#: templates/config/dashboard.html:94 -msgid "General SLA" -msgstr "اتفاقية مستوى الخدمة العامة" - -#: templates/config/dashboard.html:95 -msgid "active configs" -msgstr "الإعدادات النشطة" - -#: templates/config/dashboard.html:98 -#, fuzzy -#| msgid "Manage" -msgid "Manage SLA" -msgstr "إدارة" - -#: templates/config/dashboard.html:107 templates/config/routing_rules.html:14 -#: templates/surveys/template_form.html:442 -msgid "Routing Rules" -msgstr "قواعد التوجيه" - -#: templates/config/dashboard.html:108 -msgid "active rules" -msgstr "القواعد النشطة" - -#: templates/config/dashboard.html:111 -#, fuzzy -#| msgid "Manage Routing Rules" -msgid "Manage Routing" -msgstr "إدارة قواعد التوجيه" - -#: templates/config/dashboard.html:121 +#: templates/config/dashboard.html:123 msgid "User Management" msgstr "إدارة المستخدمين" -#: templates/config/dashboard.html:129 +#: templates/config/dashboard.html:131 msgid "Users" msgstr "المستخدمون" -#: templates/config/dashboard.html:130 +#: templates/config/dashboard.html:132 #, fuzzy -#| msgid "Manage system settings and configurations" -msgid "Manage system users and permissions" -msgstr "إدارة إعدادات وتكوينات النظام" +#| msgid "active rules" +msgid "active users" +msgstr "القواعد النشطة" -#: templates/config/dashboard.html:133 +#: templates/config/dashboard.html:135 #, fuzzy #| msgid "Manager" msgid "Manage Users" msgstr "المدير" -#: templates/config/dashboard.html:142 +#: templates/config/dashboard.html:144 msgid "Onboarding" msgstr "تهيئة المستخدم" -#: templates/config/dashboard.html:143 +#: templates/config/dashboard.html:145 msgid "pending users" msgstr "المستخدمون المعلقون" -#: templates/config/dashboard.html:146 +#: templates/config/dashboard.html:148 msgid "Manage Onboarding" msgstr "إدارة الإعداد" -#: templates/config/dashboard.html:156 +#: templates/config/dashboard.html:158 #, fuzzy #| msgid "Survey Settings" msgid "System Settings" msgstr "إعدادات الاستبيان" -#: templates/config/dashboard.html:165 +#: templates/config/dashboard.html:167 msgid "active hospitals" msgstr "المستشفيات النشطة" -#: templates/config/dashboard.html:168 +#: templates/config/dashboard.html:170 msgid "Manage Hospitals" msgstr "إدارة المستشفيات" -#: templates/config/dashboard.html:178 -#, fuzzy -#| msgid "Hospital Notifications" -msgid "Configure hospital notification settings" -msgstr "إشعارات المستشفى" - -#: templates/config/dashboard.html:181 -msgid "Configure" -msgstr "إعداد" - -#: templates/config/dashboard.html:190 templates/config/dashboard.html:194 -#: templates/layouts/partials/sidebar.html:448 +#: templates/config/dashboard.html:193 templates/config/dashboard.html:197 +#: templates/layouts/partials/sidebar.html:453 #: templates/notifications/send_sms_direct.html:4 #: templates/notifications/send_sms_direct.html:12 #: templates/notifications/send_sms_direct.html:81 msgid "Send SMS" msgstr "إرسال رسالة نصية" -#: templates/config/dashboard.html:191 +#: templates/config/dashboard.html:194 msgid "Send text messages directly to any phone number" msgstr "إرسال رسائل نصية مباشرة إلى أي رقم هاتف" -#: templates/config/dashboard.html:204 +#: templates/config/dashboard.html:207 #: templates/reports/report_detail.html:228 #: templates/standards/standard_confirm_delete.html:101 msgid "records" msgstr "السجلات" -#: templates/config/dashboard.html:207 +#: templates/config/dashboard.html:210 #, fuzzy #| msgid "View Report" msgid "View Records" msgstr "عرض التقرير" -#: templates/config/routing_rules.html:16 -msgid "Manage action routing and assignment rules" -msgstr "إدارة توجيه الإجراءات وقواعد التعيين" +#: templates/config/emails/reset_password_email.html:4 +#, fuzzy +#| msgid "Your PX360 Account Credentials - Al Hammadi Hospital" +msgid "Your PX360 Password Has Been Reset - Al Hammadi Hospital" +msgstr "بيانات دخول حساب PX360 الخاص بك - مستشفى الحمادي" +#: templates/config/emails/reset_password_email.html:6 +#, fuzzy +#| msgid "" +#| "Your PX360 account has been created. Find your login credentials below." +msgid "" +"Your password has been reset by an administrator. Find your new credentials " +"below." +msgstr "" +"تم إنشاء حساب PX360 الخاص بك. ابحث عن بيانات تسجيل الدخول الخاصة بك أدناه." + +#: templates/config/emails/reset_password_email.html:8 +#, fuzzy +#| msgid "Password Reset Request" +msgid "Password Reset" +msgstr "طلب إعادة تعيين كلمة المرور" + +#: templates/config/emails/reset_password_email.html:10 +msgid "Your PX360 account password has been reset" +msgstr "" + +#: templates/config/emails/reset_password_email.html:17 +#: templates/emails/appointment_confirmation.html:17 +#: templates/emails/explanation_reminder.html:17 +#: templates/emails/explanation_request.html:11 +#: templates/emails/explanation_second_reminder.html:20 +#: templates/emails/invitation_expired.html:17 +#: templates/emails/new_complaint_admin_notification.html:18 +#: templates/emails/new_observation_notification.html:11 +#: templates/emails/observation_assigned.html:17 +#: templates/emails/observation_monthly_followup.html:17 +#: templates/emails/observation_resolved.html:17 +#: templates/emails/observation_sla_reminder.html:23 +#: templates/emails/observation_sla_second_reminder.html:33 +#: templates/emails/sla_reminder.html:23 +#: templates/emails/sla_second_reminder.html:26 +#: templates/emails/survey_invitation.html:11 +#: templates/emails/survey_results_notification.html:17 +#: templates/organizations/emails/staff_credentials.html:17 +msgid "Dear" +msgstr "عزيزي/عزيزتي" + +#: templates/config/emails/reset_password_email.html:20 +#, fuzzy +#| msgid "" +#| "Your PX360 account has been created. Find your login credentials below." +msgid "" +"Your PX360 account password has been reset by an administrator. Please use " +"the new credentials below to login." +msgstr "" +"تم إنشاء حساب PX360 الخاص بك. ابحث عن بيانات تسجيل الدخول الخاصة بك أدناه." + +#: templates/config/emails/reset_password_email.html:30 +#, fuzzy +#| msgid "Your credentials are ready" +msgid "Your New Credentials" +msgstr "بيانات الدخول جاهزة" + +#: templates/config/emails/reset_password_email.html:43 +#: templates/organizations/emails/staff_credentials.html:40 +msgid "Password:" +msgstr "كلمة المرور:" + +#: templates/config/emails/reset_password_email.html:58 +#: templates/organizations/emails/staff_credentials.html:63 +msgid "Security Notice:" +msgstr "ملاحظة الأمان:" + +#: templates/config/emails/reset_password_email.html:61 +#, fuzzy +#| msgid "" +#| "Please change your password after your first login for security purposes." +msgid "" +"Please change your password immediately after logging in for security " +"purposes." +msgstr "يرجى تغيير كلمة المرور بعد تسجيل الدخول الأول لأغراض الأمان." + +#: templates/config/emails/reset_password_email.html:69 +#: templates/organizations/emails/staff_credentials.html:74 +msgid "Login to PX360" +msgstr "تسجيل الدخول إلى PX360" + +#: templates/config/emails/reset_password_email.html:71 +#: templates/emails/explanation_reminder.html:104 +#: templates/emails/explanation_second_reminder.html:107 +#: templates/organizations/emails/staff_credentials.html:76 +msgid "Need Assistance?" +msgstr "هل تحتاج إلى مساعدة؟" + +#: templates/config/emails/reset_password_email.html:73 +#, fuzzy +#| msgid "" +#| "If you have any questions or need assistance, please contact your system " +#| "administrator." +msgid "" +"If you did not expect this password reset, please contact your system " +"administrator immediately." +msgstr "" +"إذا كان لديك أي أسئلة أو تحتاج إلى مساعدة، يرجى الاتصال بمسؤول النظام." + +#: templates/config/hospital_users.html:4 +#: templates/config/hospital_users.html:112 +#, fuzzy +#| msgid "Total Users" +msgid "Hospital Users" +msgstr "إجمالي المستخدمين" + +#: templates/config/hospital_users.html:113 +#, fuzzy +#| msgid "Manage hospital sections and departments" +msgid "Manage hospital user accounts and reset passwords" +msgstr "إدارة أقسام وفروع المستشفى" + +#: templates/config/hospital_users.html:116 #: templates/config/routing_rules.html:20 templates/config/sla_config.html:20 msgid "Back to Config" msgstr "العودة إلى التكوين" +#: templates/config/hospital_users.html:204 +#, fuzzy +#| msgid "Name, ID..." +msgid "Name, email, ID..." +msgstr "الاسم، الرقم التعريفي..." + +#: templates/config/hospital_users.html:221 +#, fuzzy +#| msgid "User Account" +msgid "User Accounts" +msgstr "حساب المستخدم" + +#: templates/config/hospital_users.html:235 +#, fuzzy +#| msgid "Roles" +msgid "Role(s)" +msgstr "الأدوار" + +#: templates/config/hospital_users.html:298 +#, fuzzy +#| msgid "No surveys found" +msgid "No users found" +msgstr "لا توجد استبيانات" + +#: templates/config/hospital_users.html:299 +#: templates/physicians/physician_list.html:271 +#: templates/physicians/ratings_list.html:298 +msgid "Try adjusting your filters" +msgstr "جرب تعديل عوامل التصفية الخاصة بك" + +#: templates/config/hospital_users.html:389 +#, fuzzy +#| msgid "Reset Password" +msgid "Reset password for" +msgstr "إعادة تعيين كلمة المرور" + +#: templates/config/hospital_users.html:391 +#, fuzzy +#| msgid "A new password will be generated and sent to" +msgid "" +"A new temporary password will be generated and sent to this user's email." +msgstr "سيتم إنشاء كلمة مرور جديدة وإرسالها إلى" + +#: templates/config/hospital_users.html:406 +#, fuzzy +#| msgid "Password Reset Successful" +msgid "Password Reset Successfully" +msgstr "تمت إعادة تعيين كلمة المرور بنجاح" + +#: templates/config/hospital_users.html:413 +#, fuzzy +#| msgid "A new password will be generated and sent to" +msgid "The password has been reset for" +msgstr "سيتم إنشاء كلمة مرور جديدة وإرسالها إلى" + +#: templates/config/hospital_users.html:415 +#, fuzzy +#| msgid "New Password" +msgid "New Temporary Password" +msgstr "كلمة المرور الجديدة" + +#: templates/config/hospital_users.html:418 +#: templates/simulator/log_detail.html:268 +#: templates/simulator/log_detail.html:283 +#: templates/social/partials/ai_analysis_bilingual.html:83 +#: templates/social/partials/ai_analysis_bilingual.html:89 +msgid "Copy" +msgstr "نسخ" + +#: templates/config/hospital_users.html:425 +#: templates/surveys/generate_enhanced_report.html:88 +#: templates/surveys/his_patient_import.html:155 +msgid "Note:" +msgstr "ملاحظة:" + +#: templates/config/hospital_users.html:425 +#, fuzzy +#| msgid "" +#| "The new password will be displayed to you after reset. Make sure to share it" +#| " securely if needed." +msgid "" +"This password has been sent to the user's email. Please share it securely if" +" needed." +msgstr "" +"سيتم عرض كلمة المرور الجديدة لك بعد إعادة التعيين. تأكد من مشاركتها بشكل آمن" +" عند الحاجة." + +#: templates/config/hospital_users.html:430 +#: templates/core/public_submit.html:388 +#: templates/organizations/staff_detail.html:397 +msgid "Done" +msgstr "تم" + +#: templates/config/hospital_users.html:459 +msgid "Resetting..." +msgstr "جارٍ إعادة التعيين..." + +#: templates/config/routing_rules.html:14 +#: templates/surveys/template_form.html:442 +msgid "Routing Rules" +msgstr "قواعد التوجيه" + +#: templates/config/routing_rules.html:16 +msgid "Manage action routing and assignment rules" +msgstr "إدارة توجيه الإجراءات وقواعد التعيين" + #: templates/config/routing_rules.html:30 msgid "Routing Rules List" msgstr "قائمة قواعد التوجيه" @@ -15032,7 +15320,7 @@ msgid "Information:" msgstr "معلومات:" #: templates/core/no_hospital_assigned.html:36 -#: templates/layouts/partials/sidebar.html:622 +#: templates/layouts/partials/sidebar.html:627 #: templates/layouts/partials/topbar.html:127 #: templates/layouts/source_user_base.html:200 msgid "Logout" @@ -15051,7 +15339,7 @@ msgstr "" "إذا كنت تعتقد أن هذا خطأ، يرجى التواصل مع مسؤول PX360 أو فريق الدعم الفني." #: templates/core/no_hospital_assigned.html:59 -#: templates/layouts/partials/sidebar.html:771 +#: templates/layouts/partials/sidebar.html:776 #: templates/layouts/partials/topbar.html:138 msgid "Are you sure you want to logout?" msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" @@ -15060,12 +15348,12 @@ msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" msgid "Submit Feedback" msgstr "إرسال ملاحظات" -#: templates/core/public_submit.html:293 +#: templates/core/public_submit.html:287 #: templates/emails/survey_invitation.html:8 msgid "We Value Your Feedback" msgstr "نقدّر ملاحظاتك" -#: templates/core/public_submit.html:295 +#: templates/core/public_submit.html:289 msgid "" "Your feedback helps us improve our services and provide better care for " "everyone. Choose a category below to get started." @@ -15073,69 +15361,63 @@ msgstr "" "يساعدنا ملاحظاتك على تحسين خدماتنا وتقديم رعاية أفضل للجميع. اختر فئة أدناه " "للبدء." -#: templates/core/public_submit.html:311 +#: templates/core/public_submit.html:305 msgid "" "Report an issue with our services. We take all concerns seriously and will " "investigate thoroughly." msgstr "الإبلاغ عن مشكلة في الخدمات. نأخذ جميع الشكاوى بجدية ونحقق فيها." -#: templates/core/public_submit.html:322 +#: templates/core/public_submit.html:316 msgid "Observation" msgstr "ملاحظة" -#: templates/core/public_submit.html:324 +#: templates/core/public_submit.html:318 msgid "" "Help us improve safety by sharing what you've noticed. Anonymous submissions" " are welcome." msgstr "" "ساعدنا على تحسين السلامة من خلال مشاركة ما لاحظته. نرحب بالإرسال المجهول." -#: templates/core/public_submit.html:337 +#: templates/core/public_submit.html:331 msgid "" "Have questions? We're here to help with appointments, services, or general " "information." msgstr "" "لديك أسئلة؟ نحن هنا للمساعدة بخصوص المواعيد أو الخدمات أو المعلومات العامة." -#: templates/core/public_submit.html:349 +#: templates/core/public_submit.html:343 msgid "Back to Selection" msgstr "العودة للاختيار" -#: templates/core/public_submit.html:356 +#: templates/core/public_submit.html:350 msgid "Loading form..." msgstr "جارٍ تحميل النموذج..." -#: templates/core/public_submit.html:365 +#: templates/core/public_submit.html:359 msgid "Already submitted something?" msgstr "هل سبق أن قمت بإرسال شيء؟" -#: templates/core/public_submit.html:370 +#: templates/core/public_submit.html:364 msgid "Track Complaint" msgstr "تتبع الشكوى" -#: templates/core/public_submit.html:375 -#: templates/observations/public_track.html:5 +#: templates/core/public_submit.html:369 msgid "Track Observation" msgstr "متابعة الملاحظة" -#: templates/core/public_submit.html:388 +#: templates/core/public_submit.html:382 msgid "Your submission has been received and will be reviewed." msgstr "تم استلام طلبك وسيتم مراجعته." -#: templates/core/public_submit.html:390 +#: templates/core/public_submit.html:384 msgid "Reference Number:" msgstr "رقم المرجع:" -#: templates/core/public_submit.html:394 -#: templates/organizations/staff_detail.html:397 -msgid "Done" -msgstr "تم" - -#: templates/core/public_submit.html:451 templates/core/public_submit.html:466 +#: templates/core/public_submit.html:446 templates/core/public_submit.html:461 msgid "Failed to load form." msgstr "فشل تحميل النموذج." -#: templates/core/public_submit.html:472 +#: templates/core/public_submit.html:467 #: templates/observations/observation_create.html:161 #: templates/observations/observation_create.html:174 #: templates/observations/observation_create.html:246 @@ -15149,74 +15431,74 @@ msgstr "فشل تحميل النموذج." msgid "optional" msgstr "اختياري" -#: templates/core/public_submit.html:484 +#: templates/core/public_submit.html:479 #: templates/observations/public_new.html:5 #: templates/observations/public_new.html:54 msgid "Report an Observation" msgstr "الإبلاغ عن ملاحظة" -#: templates/core/public_submit.html:486 +#: templates/core/public_submit.html:481 msgid "" "Help us improve by reporting issues you notice. Your input is valuable." msgstr "" "ساعدنا على التحسين عن طريق الإبلاغ عن المشكلات التي تلاحظها. مدخلاتك قيّمة." -#: templates/core/public_submit.html:510 +#: templates/core/public_submit.html:505 msgid "Brief descriptive title" msgstr "عنوان وصفي موجز" -#: templates/core/public_submit.html:516 +#: templates/core/public_submit.html:511 msgid "Describe what you observed in detail..." msgstr "صف ما لاحظته بالتفصيل..." -#: templates/core/public_submit.html:523 +#: templates/core/public_submit.html:518 msgid "Where did this occur?" msgstr "أين حدث ذلك؟" -#: templates/core/public_submit.html:527 +#: templates/core/public_submit.html:522 msgid "When" msgstr "عندما" -#: templates/core/public_submit.html:539 +#: templates/core/public_submit.html:534 msgid "Images, PDF, Word (max 10MB)" msgstr "الصور، PDF، Word (الحد الأقصى 10 ميجابايت)" -#: templates/core/public_submit.html:546 +#: templates/core/public_submit.html:541 msgid "Your Info" msgstr "معلوماتك" -#: templates/core/public_submit.html:546 +#: templates/core/public_submit.html:541 msgid "optional - leave blank for anonymous" msgstr "اختياري - اتركه فارغًا للانضمام كمجهول" -#: templates/core/public_submit.html:548 +#: templates/core/public_submit.html:543 #: templates/observations/observation_detail.html:180 #: templates/observations/public_new.html:193 msgid "Staff ID" msgstr "معرّف الموظف" -#: templates/core/public_submit.html:557 +#: templates/core/public_submit.html:552 #: templates/observations/public_new.html:214 msgid "Submit Observation" msgstr "إرسال الملاحظة" -#: templates/core/public_submit.html:625 templates/core/public_submit.html:628 +#: templates/core/public_submit.html:620 templates/core/public_submit.html:623 msgid "Failed to submit" msgstr "فشل الإرسال" -#: templates/core/public_submit.html:644 +#: templates/core/public_submit.html:639 msgid "Ask a question. We'll respond within 24-48 hours." msgstr "اطرح سؤالاً. سنرد خلال 24-48 ساعة." -#: templates/core/public_submit.html:881 +#: templates/core/public_submit.html:877 msgid "Select Domain" msgstr "اختر المجال" -#: templates/core/public_submit.html:918 +#: templates/core/public_submit.html:914 msgid "Select Subcategory" msgstr "اختر الفئة الفرعية" -#: templates/core/public_submit.html:939 +#: templates/core/public_submit.html:935 msgid "Select Classification" msgstr "اختر التصنيف" @@ -15812,10 +16094,10 @@ msgstr "CCHI" #: templates/dashboard/employee_evaluation.html:900 #: templates/layouts/partials/sidebar.html:323 -#: templates/organizations/patient_detail.html:132 -#: templates/organizations/patient_list.html:5 -#: templates/organizations/patient_list.html:122 -#: templates/organizations/patient_visit_journey.html:71 +#: templates/organizations/patient_detail.html:133 +#: templates/organizations/patient_list.html:6 +#: templates/organizations/patient_list.html:123 +#: templates/organizations/patient_visit_journey.html:72 msgid "Patients" msgstr "المرضى" @@ -16088,12 +16370,12 @@ msgid "inquiries" msgstr "الاستفسارات" #: templates/dashboard/partials/observations_table.html:64 -#: templates/observations/observation_list.html:390 +#: templates/observations/observation_list.html:391 msgid "No observations found" msgstr "لم يتم العثور على ملاحظات" #: templates/dashboard/partials/observations_table.html:74 -#: templates/observations/observation_list.html:283 +#: templates/observations/observation_list.html:284 msgid "observations" msgstr "ملاحظات" @@ -16217,27 +16499,7 @@ msgid "" msgstr "لقد تم جدولة موعدك الصحي في مستشفى الحمادي بنجاح" #: templates/emails/appointment_confirmation.html:17 -#: templates/emails/explanation_reminder.html:17 -#: templates/emails/explanation_request.html:18 -#: templates/emails/explanation_second_reminder.html:20 -#: templates/emails/invitation_expired.html:17 -#: templates/emails/new_complaint_admin_notification.html:18 -#: templates/emails/new_observation_notification.html:17 -#: templates/emails/observation_assigned.html:17 -#: templates/emails/observation_monthly_followup.html:17 -#: templates/emails/observation_resolved.html:17 -#: templates/emails/observation_sla_reminder.html:23 -#: templates/emails/observation_sla_second_reminder.html:33 -#: templates/emails/sla_reminder.html:23 -#: templates/emails/sla_second_reminder.html:26 -#: templates/emails/survey_invitation.html:17 -#: templates/emails/survey_results_notification.html:17 -#: templates/organizations/emails/staff_credentials.html:17 -msgid "Dear" -msgstr "عزيزي/عزيزتي" - -#: templates/emails/appointment_confirmation.html:17 -#: templates/emails/survey_invitation.html:17 +#: templates/emails/survey_invitation.html:11 #, fuzzy #| msgid "Save Patient" msgid "Valued Patient" @@ -16271,7 +16533,6 @@ msgstr "المواعيد:" #: templates/emails/appointment_confirmation.html:60 #: templates/emails/new_complaint_admin_notification.html:126 -#: templates/emails/new_observation_notification.html:147 msgid "Time:" msgstr "الوقت:" @@ -16290,8 +16551,6 @@ msgstr "الموقع" #: templates/emails/appointment_confirmation.html:87 #: templates/emails/appointment_confirmation.html:160 -#: templates/emails/base_email_template.html:159 -#: templates/emails/survey_invitation.html:94 #: templates/emails/survey_results_notification.html:135 #, fuzzy #| msgid "Add Hospital" @@ -16350,43 +16609,18 @@ msgstr "" "للحالات الطارئة، يرجى الاتصال برقم 997 أو زيارة قسم الطوارئ لدينا على الفور" #: templates/emails/appointment_confirmation.html:159 -#: templates/emails/survey_invitation.html:93 #: templates/emails/survey_results_notification.html:134 #, fuzzy #| msgid "Patient Experience Management System" msgid "Patient Experience Management Department" msgstr "نظام إدارة تجربة المريض" -#: templates/emails/base_email_template.html:168 -#: templates/standards/source_confirm_delete.html:112 -#: templates/standards/source_list.html:129 -msgid "Website" -msgstr "الموقع الإلكتروني" - -#: templates/emails/base_email_template.html:171 -#, fuzzy -#| msgid "Contact" -msgid "Contact Us" -msgstr "جهة الاتصال" - -#: templates/emails/base_email_template.html:174 -msgid "Privacy Policy" -msgstr "سياسة الخصوصية" - -#: templates/emails/base_email_template.html:182 +#: templates/emails/base_email_template.html:68 #, fuzzy #| msgid "All rights reserved." msgid "Al Hammadi Hospital. All rights reserved." msgstr "جميع الحقوق محفوظة." -#: templates/emails/base_email_template.html:194 -msgid "Unsubscribe" -msgstr "إلغاء الاشتراك" - -#: templates/emails/base_email_template.html:194 -msgid "from these emails" -msgstr "من هذه الرسائل الإلكترونية" - #: templates/emails/explanation_reminder.html:4 msgid "Reminder: Explanation Request" msgstr "تذكير: طلب توضيح" @@ -16422,13 +16656,6 @@ msgid "" "your manager." msgstr "يرجى تقديم توضيحك قبل الموعد النهائي لتجنب التصعيد إلى مديرك." -#: templates/emails/explanation_reminder.html:104 -#: templates/emails/explanation_request.html:154 -#: templates/emails/explanation_second_reminder.html:107 -#: templates/organizations/emails/staff_credentials.html:76 -msgid "Need Assistance?" -msgstr "هل تحتاج إلى مساعدة؟" - #: templates/emails/explanation_reminder.html:106 #: templates/emails/explanation_second_reminder.html:109 msgid "" @@ -16440,7 +16667,7 @@ msgstr "إذا كان لديك أي أسئلة، يرجى التواصل مع ا msgid "Explanation Request - Al Hammadi Hospital" msgstr "طلب تفسير - مستشفى الحمادي" -#: templates/emails/explanation_request.html:6 +#: templates/emails/explanation_request.html:5 msgid "" "You have been assigned to provide an explanation for a patient complaint" msgstr "لقد تم تكليفك بتقديم تفسير لشكوى مريض" @@ -16449,11 +16676,7 @@ msgstr "لقد تم تكليفك بتقديم تفسير لشكوى مريض" msgid "Explanation Request" msgstr "طلب إيضاح" -#: templates/emails/explanation_request.html:10 -msgid "Please review the complaint details and submit your response" -msgstr "يرجى مراجعة تفاصيل الشكوى وإرسال ردك" - -#: templates/emails/explanation_request.html:21 +#: templates/emails/explanation_request.html:14 msgid "" "You have been assigned to provide an explanation for the following patient " "complaint. Please review the details and submit your response using the " @@ -16462,44 +16685,28 @@ msgstr "" "لقد تم تكليفك بتقديم تفسير لشكوى المريض التالية. يرجى مراجعة التفاصيل وإرسال" " ردك باستخدام الزر أدناه." -#: templates/emails/explanation_request.html:33 +#: templates/emails/explanation_request.html:20 msgid "Note from PX Team:" msgstr "ملاحظة من فريق PX:" -#: templates/emails/explanation_request.html:138 -msgid "Important Information:" -msgstr "معلومات مهمة:" +#: templates/emails/explanation_request.html:50 +#, fuzzy +#| msgid "SLA Deadline:" +msgid "Deadline:" +msgstr "الموعد النهائي لاتفاقية SLA:" -#: templates/emails/explanation_request.html:141 -msgid "This link is unique and can only be used once" -msgstr "هذا الرابط فريد ويمكن استخدامه مرة واحدة فقط" - -#: templates/emails/explanation_request.html:142 -msgid "You can attach supporting documents to your explanation" -msgstr "يمكنك إرفاق مستندات داعمة لإيضاحك" - -#: templates/emails/explanation_request.html:143 -msgid "Your response will be reviewed by the PX team" -msgstr "سيتم مراجعة ردك من قبل فريق PX" - -#: templates/emails/explanation_request.html:144 -msgid "Please submit your explanation at your earliest convenience" -msgstr "يرجى تقديم إيضاحك في أقرب وقت ممكن" - -#: templates/emails/explanation_request.html:156 -msgid "" -"If you have any questions or concerns, please contact the PX team directly." +#: templates/emails/explanation_request.html:77 +#, fuzzy +#| msgid "" +#| "If you have any questions or concerns, please contact the PX team directly." +msgid "If you have any questions, please contact the PX team." msgstr "في حال وجود أي استفسارات أو ملاحظات، يرجى التواصل مباشرة مع فريق PX." -#: templates/emails/explanation_request.html:157 -#: templates/surveys/generate_enhanced_report.html:88 -#: templates/surveys/his_patient_import.html:155 -msgid "Note:" -msgstr "ملاحظة:" - -#: templates/emails/explanation_request.html:157 -msgid "" -"This is an automated email. Please do not reply directly to this message." +#: templates/emails/explanation_request.html:78 +#, fuzzy +#| msgid "" +#| "This is an automated email. Please do not reply directly to this message." +msgid "This is an automated email. Please do not reply." msgstr "هذه رسالة بريد إلكتروني آلية. يرجى عدم الرد مباشرة على هذه الرسالة." #: templates/emails/explanation_second_reminder.html:4 @@ -16603,28 +16810,24 @@ msgid "" msgstr "يرجى مراجعة وتفعيل هذه الشكوى في أقرب وقت مناسب لك." #: templates/emails/new_complaint_admin_notification.html:123 -#: templates/emails/new_observation_notification.html:144 #: templates/emails/observation_resolved.html:130 msgid "Notification Details" msgstr "تفاصيل الإشعار" #: templates/emails/new_complaint_admin_notification.html:127 -#: templates/emails/new_observation_notification.html:148 #: templates/emails/observation_resolved.html:132 msgid "This is an automated notification from the PX 360 system." msgstr "هذه إشعار آلي من نظام PX 360." #: templates/emails/new_observation_notification.html:4 #, fuzzy -#| msgid "New Complaint Notification - Al Hammadi Hospital" -msgid "New Observation Notification - Al Hammadi Hospital" -msgstr "إشعار شكوى جديد - مستشفى الحمادي" +#| msgid "Explanation Request - Al Hammadi Hospital" +msgid "New Observation Submitted - Al Hammadi Hospital" +msgstr "طلب تفسير - مستشفى الحمادي" -#: templates/emails/new_observation_notification.html:6 -#, fuzzy -#| msgid "Your observation has been submitted successfully." -msgid "A new observation has been submitted and requires review." -msgstr "تم إرسال ملاحظتك بنجاح." +#: templates/emails/new_observation_notification.html:5 +msgid "A new observation requires your review and triage" +msgstr "هناك ملاحظة جديدة تتطلب مراجعتك وتصنيفك" #: templates/emails/new_observation_notification.html:8 #, fuzzy @@ -16632,51 +16835,30 @@ msgstr "تم إرسال ملاحظتك بنجاح." msgid "New Observation Submitted" msgstr "تم إرسال الملاحظة" -#: templates/emails/new_observation_notification.html:10 -msgid "A new observation requires your review and triage" -msgstr "هناك ملاحظة جديدة تتطلب مراجعتك وتصنيفك" - -#: templates/emails/new_observation_notification.html:20 +#: templates/emails/new_observation_notification.html:14 #, fuzzy #| msgid "" #| "A new complaint has been submitted and requires your attention. Please " #| "review the details below." msgid "" -"A new observation has been submitted and requires your review. Please assess" -" the details below and take appropriate action." +"A new observation has been submitted and requires your review. Please review" +" the details below." msgstr "تم تقديم شكوى جديدة وتتطلب انتباهك. يرجى مراجعة التفاصيل أدناه." -#: templates/emails/new_observation_notification.html:33 -#: templates/emails/observation_assigned.html:33 -#: templates/emails/observation_monthly_followup.html:33 -#: templates/emails/observation_resolved.html:33 -msgid "Tracking Code" +#: templates/emails/new_observation_notification.html:22 +#: templates/emails/observation_sla_reminder.html:47 +#: templates/emails/observation_sla_second_reminder.html:51 +#, fuzzy +#| msgid "Tracking Code" +msgid "Tracking Code:" msgstr "رمز التتبع" -#: templates/emails/new_observation_notification.html:84 -#: templates/observations/observation_list.html:300 -msgid "Reporter" -msgstr "المُبلّغ" - -#: templates/emails/new_observation_notification.html:120 +#: templates/emails/new_observation_notification.html:54 #, fuzzy -#| msgid "" -#| "Please review this complaint and take appropriate action before the SLA " -#| "deadline to avoid breach." +#| msgid "This is an automated notification from the PX 360 system." msgid "" -"Please review this observation and assign it to the appropriate team member " -"for further action." -msgstr "" -"يرجى مراجعة هذه الشكوى واتخاذ الإجراء المناسب قبل موعد SLA لتجنب الإخلال." - -#: templates/emails/new_observation_notification.html:142 -#: templates/emails/observation_assigned.html:150 -#: templates/emails/observation_resolved.html:128 -#: templates/emails/observation_sla_reminder.html:171 -#, fuzzy -#| msgid "Observation" -msgid "View Observation" -msgstr "ملاحظة" +"This is an automated notification. Please log in to PX360 for full details." +msgstr "هذه إشعار آلي من نظام PX 360." #: templates/emails/observation_assigned.html:4 #, fuzzy @@ -16712,6 +16894,13 @@ msgid "" "the details below." msgstr "تم تقديم شكوى جديدة وتتطلب انتباهك. يرجى مراجعة التفاصيل أدناه." +#: templates/emails/observation_assigned.html:33 +#: templates/emails/observation_monthly_followup.html:33 +#: templates/emails/observation_resolved.html:33 +#: templates/observations/public_track.html:158 +msgid "Tracking Code" +msgstr "رمز التتبع" + #: templates/emails/observation_assigned.html:124 #: templates/emails/observation_sla_reminder.html:136 #: templates/emails/sla_reminder.html:146 @@ -16733,6 +16922,14 @@ msgstr "تحقق من الحالة وقيّمها" msgid "Update the observation status and add notes as needed" msgstr "تحديث حالة الملاحظة وإضافة ملاحظات حسب الحاجة" +#: templates/emails/observation_assigned.html:150 +#: templates/emails/observation_resolved.html:128 +#: templates/emails/observation_sla_reminder.html:171 +#, fuzzy +#| msgid "Observation" +msgid "View Observation" +msgstr "ملاحظة" + #: templates/emails/observation_assigned.html:155 #, fuzzy #| msgid "" @@ -16933,13 +17130,6 @@ msgstr "" msgid "Observation Details" msgstr "تفاصيل الملاحظة" -#: templates/emails/observation_sla_reminder.html:47 -#: templates/emails/observation_sla_second_reminder.html:51 -#, fuzzy -#| msgid "Tracking Code" -msgid "Tracking Code:" -msgstr "رمز التتبع" - #: templates/emails/observation_sla_reminder.html:113 #: templates/emails/observation_sla_second_reminder.html:107 #: templates/emails/sla_reminder.html:115 @@ -17295,26 +17485,19 @@ msgstr "" msgid "Patient Survey Invitation - Al Hammadi Hospital" msgstr "إشعار شكوى جديد - مستشفى الحمادي" -#: templates/emails/survey_invitation.html:6 +#: templates/emails/survey_invitation.html:5 #, fuzzy #| msgid "Thank you for sharing your experience with us" msgid "We value your feedback! Please share your experience with us." msgstr "شكرًا لمشاركتك تجربتك معنا" -#: templates/emails/survey_invitation.html:10 -msgid "" -"Help us improve our services by sharing your recent experience at Al Hammadi" -" Hospital" -msgstr "" -"ساعدنا في تحسين خدماتنا من خلال مشاركة تجربتك الأخيرة في مستشفى الحمادي" - -#: templates/emails/survey_invitation.html:20 +#: templates/emails/survey_invitation.html:14 #, fuzzy #| msgid "No recent activity" msgid "your recent visit" msgstr "لا يوجد نشاط حديث" -#: templates/emails/survey_invitation.html:20 +#: templates/emails/survey_invitation.html:14 #, python-format msgid "" "Thank you for choosing Al Hammadi Hospital for your healthcare needs. We " @@ -17323,112 +17506,31 @@ msgstr "" "نشكركم على اختياركم مستشفى الحمادي لتلبية احتياجاتكم الصحية. نأمل أن زيارتكم" " الأخيرة في %(visit)s قد تحققت توقعاتكم." -#: templates/emails/survey_invitation.html:23 -#: templates/emails/survey_invitation.html:87 -#, fuzzy -#| msgid "1-5" -msgid "3-5" -msgstr "1-5" - -#: templates/emails/survey_invitation.html:23 -#, python-format +#: templates/emails/survey_invitation.html:17 msgid "" -"Your feedback is invaluable in helping us maintain and improve the quality " -"of care we provide. Would you mind taking %(duration)s minutes to complete " -"our patient experience survey?" +"We would greatly appreciate it if you could take a few minutes to complete " +"our satisfaction survey. Your feedback helps us improve our services." msgstr "" -"ملاحظاتكم لا تقدر بثمن في مساعدتنا على الحفاظ على جودة الرعاية التي نقدمها " -"وتحسينها. هل تمانعون في تخصيص %(duration)s دقيقة لإكمال استبيان تجربة المريض" -" الخاص بنا؟" -#: templates/emails/survey_invitation.html:33 +#: templates/emails/survey_invitation.html:23 +msgid "Takes only 3-5 minutes" +msgstr "تستغرق 3-5 دقائق فقط" + +#: templates/emails/survey_invitation.html:24 #, fuzzy -#| msgid "We Value Your Feedback" -msgid "Why Your Feedback Matters:" -msgstr "نقدّر ملاحظاتك" - -#: templates/emails/survey_invitation.html:46 -#, fuzzy -#| msgid "Improve Patient Experience:" -msgid "Improve Patient Care:" -msgstr "تحسين تجربة المريض:" - -#: templates/emails/survey_invitation.html:46 -#, fuzzy -#| msgid "Your feedback helps us improve our services" -msgid "Your insights help us enhance our services" -msgstr "ملاحظاتك تساعدنا على تحسين خدماتنا" - -#: templates/emails/survey_invitation.html:59 -#, fuzzy -#| msgid "Patient Experience" -msgid "Better Experience:" -msgstr "تجربة المريض" - -#: templates/emails/survey_invitation.html:59 -msgid "Help us create a better experience for all patients" -msgstr "ساعدنا في خلق تجربة أفضل لجميع المرضى" - -#: templates/emails/survey_invitation.html:72 -#, fuzzy -#| msgid "Total Standards" -msgid "Quality Standards:" -msgstr "إجمالي المعايير" - -#: templates/emails/survey_invitation.html:72 -msgid "Contribute to our commitment to excellence" -msgstr "ساهم في التزامنا بالتميز" - -#: templates/emails/survey_invitation.html:83 -#, fuzzy -#| msgid "Has Survey" -msgid "Start Survey" -msgstr "يوجد استبيان" - -#: templates/emails/survey_invitation.html:85 -#: templates/surveys/instance_detail.html:538 -msgid "Survey Information" -msgstr "معلومات الاستبيان" - -#: templates/emails/survey_invitation.html:87 -#, fuzzy -#| msgid "Duration" -msgid "Duration:" -msgstr "المدة" - -#: templates/emails/survey_invitation.html:87 -msgid "Approximately" -msgstr "تقريباً" - -#: templates/emails/survey_invitation.html:87 -msgid "minutes" -msgstr "دقيقة" - -#: templates/emails/survey_invitation.html:88 -#, fuzzy -#| msgid "Confidence" -msgid "Confidentiality:" -msgstr "الثقة" - -#: templates/emails/survey_invitation.html:88 -msgid "Your responses are completely confidential" +#| msgid "Your responses are completely confidential" +msgid "Your responses are confidential" msgstr "إجاباتك سرية تماماً" -#: templates/emails/survey_invitation.html:89 +#: templates/emails/survey_invitation.html:34 #, fuzzy -#| msgid "SLA Deadline:" -msgid "Deadline:" -msgstr "الموعد النهائي لاتفاقية SLA:" +#| msgid "Create Survey" +msgid "Take Survey" +msgstr "إنشاء استبيان" -#: templates/emails/survey_invitation.html:89 -#, fuzzy -#| msgid "Please Specify" -msgid "Please complete by" -msgstr "يرجى التحديد" - -#: templates/emails/survey_invitation.html:89 -msgid "the end of this week" -msgstr "نهاية هذا الأسبوع" +#: templates/emails/survey_invitation.html:41 +msgid "Thank you for your time and feedback." +msgstr "نشكركم على وقتكم وردودكم." #: templates/emails/survey_results_notification.html:4 #, fuzzy @@ -17791,7 +17893,7 @@ msgstr "أعلى تقييم" #: templates/feedback/feedback_list.html:301 #: templates/journeys/instance_list.html:204 -#: templates/observations/observation_list.html:252 +#: templates/observations/observation_list.html:253 #: templates/simulator/log_list.html:248 #: templates/social/social_comment_list.html:196 #: templates/social/social_platform.html:178 @@ -17800,7 +17902,7 @@ msgstr "من التاريخ" #: templates/feedback/feedback_list.html:305 #: templates/journeys/instance_list.html:208 -#: templates/observations/observation_list.html:257 +#: templates/observations/observation_list.html:258 #: templates/simulator/log_list.html:252 #: templates/social/social_comment_list.html:200 #: templates/social/social_platform.html:182 @@ -17934,8 +18036,8 @@ msgid "Encounter ID:" msgstr "معرّف الزيارة" #: templates/journeys/instance_detail.html:181 -#: templates/organizations/patient_detail.html:352 -#: templates/organizations/patient_visit_journey.html:97 +#: templates/organizations/patient_detail.html:359 +#: templates/organizations/patient_visit_journey.html:98 msgid "Complete" msgstr "إكمال" @@ -18445,19 +18547,26 @@ msgid "All Physicians" msgstr "جميع الأطباء" #: templates/layouts/partials/sidebar.html:364 -#: templates/physicians/doctor_rating_job_list.html:210 +#: templates/physicians/doctor_rating_fetch.html:151 +#, fuzzy +#| msgid "Ratings" +msgid "Fetch Ratings" +msgstr "التقييمات" + +#: templates/layouts/partials/sidebar.html:369 +#: templates/physicians/doctor_rating_job_list.html:217 #: templates/physicians/individual_ratings_list.html:82 #: templates/physicians/individual_ratings_list.html:297 #: templates/physicians/physician_ratings_dashboard.html:369 msgid "Import Ratings" msgstr "استيراد التقييمات" -#: templates/layouts/partials/sidebar.html:369 +#: templates/layouts/partials/sidebar.html:374 #: templates/physicians/individual_ratings_list.html:74 msgid "Individual Ratings" msgstr "التقييمات الفردية" -#: templates/layouts/partials/sidebar.html:382 +#: templates/layouts/partials/sidebar.html:387 #: templates/px_sources/source_confirm_delete.html:11 #: templates/px_sources/source_detail.html:84 #: templates/px_sources/source_form.html:140 @@ -18468,14 +18577,14 @@ msgstr "التقييمات الفردية" msgid "PX Sources" msgstr "مصادر PX" -#: templates/layouts/partials/sidebar.html:389 +#: templates/layouts/partials/sidebar.html:394 #: templates/physicians/individual_ratings_list.html:148 #: templates/px_sources/source_list.html:153 #: templates/reports/saved_reports.html:62 templates/standards/search.html:105 msgid "All Sources" msgstr "جميع المصادر" -#: templates/layouts/partials/sidebar.html:418 +#: templates/layouts/partials/sidebar.html:423 #: templates/reports/report_detail.html:27 #: templates/reports/saved_reports.html:26 #: templates/surveys/analytics_reports.html:42 @@ -18485,12 +18594,12 @@ msgstr "جميع المصادر" msgid "Reports" msgstr "التقارير" -#: templates/layouts/partials/sidebar.html:425 +#: templates/layouts/partials/sidebar.html:430 #: templates/reports/saved_reports.html:177 msgid "Create Report" msgstr "إنشاء تقرير" -#: templates/layouts/partials/sidebar.html:430 +#: templates/layouts/partials/sidebar.html:435 #: templates/reports/report_builder.html:44 #: templates/reports/report_detail.html:29 #: templates/reports/saved_reports.html:5 @@ -18499,22 +18608,22 @@ msgstr "إنشاء تقرير" msgid "Saved Reports" msgstr "التقارير المحفوظة" -#: templates/layouts/partials/sidebar.html:459 +#: templates/layouts/partials/sidebar.html:464 #, fuzzy #| msgid "System Configuration" msgid "System Config" msgstr "إعدادات النظام" -#: templates/layouts/partials/sidebar.html:484 +#: templates/layouts/partials/sidebar.html:489 msgid "References" msgstr "المراجع" -#: templates/layouts/partials/sidebar.html:496 +#: templates/layouts/partials/sidebar.html:501 #: templates/references/search.html:4 templates/references/search.html:162 msgid "Search Documents" msgstr "بحث في المستندات" -#: templates/layouts/partials/sidebar.html:501 +#: templates/layouts/partials/sidebar.html:506 #: templates/references/document_form.html:5 #: templates/references/document_form.html:173 #: templates/references/folder_view.html:288 @@ -18522,7 +18631,7 @@ msgstr "بحث في المستندات" msgid "Upload Document" msgstr "رفع مستند" -#: templates/layouts/partials/sidebar.html:506 +#: templates/layouts/partials/sidebar.html:511 #: templates/references/dashboard.html:136 #: templates/references/folder_form.html:5 #: templates/references/folder_form.html:152 @@ -18531,33 +18640,33 @@ msgstr "رفع مستند" msgid "New Folder" msgstr "مجلد جديد" -#: templates/layouts/partials/sidebar.html:519 +#: templates/layouts/partials/sidebar.html:524 msgid "Standards" msgstr "المعايير" -#: templates/layouts/partials/sidebar.html:531 +#: templates/layouts/partials/sidebar.html:536 #: templates/standards/dashboard.html:85 templates/standards/search.html:4 #: templates/standards/search.html:66 msgid "Search Standards" msgstr "بحث المعايير" -#: templates/layouts/partials/sidebar.html:536 +#: templates/layouts/partials/sidebar.html:541 #: templates/standards/source_list.html:111 msgid "Sources" msgstr "المصادر" -#: templates/layouts/partials/sidebar.html:664 +#: templates/layouts/partials/sidebar.html:669 msgid "Failed to switch hospital: " msgstr "فشل في تبديل المستشفى:" -#: templates/layouts/partials/sidebar.html:664 +#: templates/layouts/partials/sidebar.html:669 #: templates/standards/department_standards.html:505 #: templates/standards/department_standards.html:570 #: templates/standards/department_standards.html:786 msgid "Unknown error" msgstr "خطأ غير معروف" -#: templates/layouts/partials/sidebar.html:670 +#: templates/layouts/partials/sidebar.html:675 msgid "An error occurred while switching hospitals" msgstr "حدث خطأ أثناء تبديل المستشفيات" @@ -18573,11 +18682,11 @@ msgstr "صباح الخير" msgid "Welcome to PX360 Patient Experience Management" msgstr "مرحبًا بك في نظام إدارة تجربة المريض PX360" -#: templates/layouts/public_base.html:178 +#: templates/layouts/public_base.html:191 msgid "Patient Experience Management" msgstr "إدارة تجربة المريض" -#: templates/layouts/public_base.html:212 +#: templates/layouts/public_base.html:225 msgid "All rights reserved." msgstr "جميع الحقوق محفوظة." @@ -18618,7 +18727,7 @@ msgid "Read" msgstr "مقروء" #: templates/notifications/inbox.html:83 -#: templates/observations/observation_list.html:120 +#: templates/observations/observation_list.html:121 #: templates/surveys/his_patient_review.html:196 msgid "New" msgstr "جديد" @@ -18925,7 +19034,7 @@ msgstr "تفاصيل الملاحظة" #: templates/observations/observation_detail.html:133 #: templates/observations/observation_detail.html:137 #: templates/observations/public_success.html:127 -#: templates/observations/public_track.html:125 +#: templates/observations/public_track.html:197 #: templates/standards/standard_detail.html:135 #: templates/standards/standard_detail.html:145 msgid "Not specified" @@ -18936,7 +19045,6 @@ msgid "Incident Date/Time" msgstr "تاريخ/وقت الحادثة" #: templates/observations/observation_detail.html:153 -#: templates/observations/public_track.html:160 msgid "Triaged" msgstr "تم الفرز" @@ -18981,30 +19089,34 @@ msgstr "لوحة تحكم الملاحظات" msgid "Manage and triage staff-reported observations" msgstr "إدارة وفرز الملاحظات المُبلّغ عنها من قِبل الموظفين" -#: templates/observations/observation_list.html:171 +#: templates/observations/observation_list.html:172 msgid "Tracking code, description..." msgstr "رمز التتبع، الوصف..." -#: templates/observations/observation_list.html:231 +#: templates/observations/observation_list.html:232 msgid "All Users" msgstr "جميع المستخدمين" -#: templates/observations/observation_list.html:242 +#: templates/observations/observation_list.html:243 msgid "Reporter Type" msgstr "نوع المُبلّغ" -#: templates/observations/observation_list.html:245 +#: templates/observations/observation_list.html:246 msgid "Anonymous Only" msgstr "مجهول فقط" -#: templates/observations/observation_list.html:246 +#: templates/observations/observation_list.html:247 msgid "Identified Only" msgstr "معرّف فقط" -#: templates/observations/observation_list.html:281 +#: templates/observations/observation_list.html:282 msgid "Observations List" msgstr "قائمة الملاحظات" +#: templates/observations/observation_list.html:301 +msgid "Reporter" +msgstr "المُبلّغ" + #: templates/observations/partials/ai_panel.html:113 #, fuzzy #| msgid "No AI analysis available for this complaint" @@ -19087,51 +19199,36 @@ msgstr "يمكنك تتبع حالة ملاحظتك في أي وقت باستخ msgid "Copied!" msgstr "تم النسخ!" -#: templates/observations/public_track.html:71 +#: templates/observations/public_track.html:4 +#: templates/observations/public_track.html:106 msgid "Track Your Observation" msgstr "تتبع ملاحظتك" -#: templates/observations/public_track.html:72 -msgid "Enter your reference number to check the status" -msgstr "أدخل رقمك المرجعي للتحقق من الحالة" +#: templates/observations/public_track.html:108 +#, fuzzy +#| msgid "" +#| "Enter your reference number below to see real-time updates on your request." +msgid "" +"Enter your tracking code below to see real-time updates on your observation." +msgstr "أدخل رقم المرجع الخاص بك أدناه لترى تحديثات فورية على طلبك." -#: templates/observations/public_track.html:87 +#: templates/observations/public_track.html:125 msgid "e.g., OBS-ABC123" msgstr "مثال: OBS-ABC123" -#: templates/observations/public_track.html:92 -msgid "Track" -msgstr "تتبع" +#: templates/observations/public_track.html:147 +#, fuzzy +#| msgid "Tracking Code" +msgid "Tracking Code Not Found" +msgstr "رمز التتبع" -#: templates/observations/public_track.html:150 -msgid "Status Progress" -msgstr "تقدم الحالة" +#: templates/observations/public_track.html:259 +#, fuzzy +#| msgid "Your complaint is being reviewed. Updates will appear here." +msgid "Your observation is being reviewed. Updates will appear here." +msgstr "شكواك قيد المراجعة. ستظهر التحديثات هنا." -#: templates/observations/public_track.html:168 -msgid "Being reviewed by our team" -msgstr "قيد المراجعة من قبل فريقنا" - -#: templates/observations/public_track.html:175 -msgid "Action is being taken" -msgstr "يتم اتخاذ إجراء" - -#: templates/observations/public_track.html:195 -msgid "Rejected" -msgstr "مرفوض" - -#: templates/observations/public_track.html:196 -msgid "This observation was not accepted" -msgstr "لم يتم قبول هذه الملاحظة" - -#: templates/observations/public_track.html:202 -msgid "Duplicate" -msgstr "مكررة" - -#: templates/observations/public_track.html:203 -msgid "This observation was marked as duplicate" -msgstr "تم تحديد هذه الملاحظة كمكررة" - -#: templates/observations/public_track.html:212 +#: templates/observations/public_track.html:267 msgid "" "For privacy reasons, detailed notes and internal communications are not " "shown here." @@ -19139,10 +19236,6 @@ msgstr "" "لأسباب تتعلق بالخصوصية، لا يتم عرض الملاحظات التفصيلية والمراسلات الداخلية " "هنا." -#: templates/observations/public_track.html:222 -msgid "Submit a new observation" -msgstr "إرسال ملاحظة جديدة" - #: templates/organizations/department_list.html:96 msgid "Manage hospital departments and their structure" msgstr "إدارة أقسام المستشفى وهيكلها" @@ -19197,23 +19290,11 @@ msgstr "تفاصيل حسابك" msgid "Username:" msgstr "اسم المستخدم:" -#: templates/organizations/emails/staff_credentials.html:40 -msgid "Password:" -msgstr "كلمة المرور:" - -#: templates/organizations/emails/staff_credentials.html:63 -msgid "Security Notice:" -msgstr "ملاحظة الأمان:" - #: templates/organizations/emails/staff_credentials.html:66 msgid "" "Please change your password after your first login for security purposes." msgstr "يرجى تغيير كلمة المرور بعد تسجيل الدخول الأول لأغراض الأمان." -#: templates/organizations/emails/staff_credentials.html:74 -msgid "Login to PX360" -msgstr "تسجيل الدخول إلى PX360" - #: templates/organizations/emails/staff_credentials.html:78 msgid "" "If you have any questions or need assistance, please contact your system " @@ -19247,7 +19328,7 @@ msgid "Hospital List" msgstr "قائمة المستشفيات" #: templates/organizations/hospital_list.html:163 -#: templates/organizations/patient_detail.html:250 +#: templates/organizations/patient_detail.html:257 msgid "City" msgstr "المدينة" @@ -19270,90 +19351,95 @@ msgid "" "Are you sure you want to delete this patient? This action cannot be undone." msgstr "هل أنت متأكد أنك تريد حذف هذا المريض؟ لا يمكن التراجع عن هذا الإجراء." -#: templates/organizations/patient_detail.html:5 +#: templates/organizations/patient_detail.html:6 msgid "Patient Details" msgstr "تفاصيل المريض" -#: templates/organizations/patient_detail.html:166 -#: templates/organizations/patient_list.html:497 +#: templates/organizations/patient_detail.html:167 +#: templates/organizations/patient_list.html:503 msgid "SSN" msgstr "رقم الضمان الاجتماعي" -#: templates/organizations/patient_detail.html:187 +#: templates/organizations/patient_detail.html:170 +#: templates/organizations/patient_list.html:294 +msgid "Toggle" +msgstr "تبديل" + +#: templates/organizations/patient_detail.html:194 msgid "No phone number on file" msgstr "لا يوجد رقم هاتف مسجل" -#: templates/organizations/patient_detail.html:191 +#: templates/organizations/patient_detail.html:198 #, fuzzy #| msgid "Send Complaint Notification" msgid "Send Complaint Link To Patient" msgstr "إرسال إشعار الشكوى" -#: templates/organizations/patient_detail.html:230 -#: templates/organizations/patient_list.html:219 +#: templates/organizations/patient_detail.html:237 +#: templates/organizations/patient_list.html:220 msgid "Gender" msgstr "الجنس" -#: templates/organizations/patient_detail.html:234 +#: templates/organizations/patient_detail.html:241 msgid "Date of Birth" msgstr "تاريخ الميلاد" -#: templates/organizations/patient_detail.html:238 -#: templates/organizations/patient_list.html:227 -#: templates/organizations/patient_list.html:264 -#: templates/organizations/patient_list.html:499 -#: templates/organizations/patient_visit_journey.html:332 +#: templates/organizations/patient_detail.html:245 +#: templates/organizations/patient_list.html:228 +#: templates/organizations/patient_list.html:265 +#: templates/organizations/patient_list.html:505 +#: templates/organizations/patient_visit_journey.html:333 msgid "Nationality" msgstr "الجنسية" -#: templates/organizations/patient_detail.html:269 -#: templates/organizations/patient_detail.html:538 +#: templates/organizations/patient_detail.html:276 +#: templates/organizations/patient_detail.html:545 msgid "HIS Visits" msgstr "زيارات نظام معلومات المستشفى" -#: templates/organizations/patient_detail.html:296 +#: templates/organizations/patient_detail.html:303 msgid "Adm ID" msgstr "رقم المريض" -#: templates/organizations/patient_detail.html:298 +#: templates/organizations/patient_detail.html:305 #: templates/standards/standard_form.html:311 msgid "Dates" msgstr "التواريخ" -#: templates/organizations/patient_detail.html:300 -#: templates/organizations/patient_visit_journey.html:134 +#: templates/organizations/patient_detail.html:307 +#: templates/organizations/patient_visit_journey.html:135 msgid "Insurance" msgstr "التأمين" -#: templates/organizations/patient_detail.html:302 +#: templates/organizations/patient_detail.html:309 #: templates/simulator/log_detail.html:145 #: templates/surveys/instance_detail.html:661 msgid "Journey" msgstr "رحلة المريض" -#: templates/organizations/patient_detail.html:354 -#: templates/organizations/patient_visit_journey.html:99 +#: templates/organizations/patient_detail.html:361 +#: templates/organizations/patient_visit_journey.html:100 msgid "Survey Sent" msgstr "تم إرسال الاستبيان" -#: templates/organizations/patient_detail.html:362 +#: templates/organizations/patient_detail.html:369 msgid "View visit journey" msgstr "عرض مسار الزيارة" -#: templates/organizations/patient_detail.html:375 +#: templates/organizations/patient_detail.html:382 msgid "No HIS visits found" msgstr "لم يتم العثور على زيارات HIS" -#: templates/organizations/patient_detail.html:435 +#: templates/organizations/patient_detail.html:442 #: templates/surveys/instance_list.html:212 msgid "No surveys found" msgstr "لا توجد استبيانات" -#: templates/organizations/patient_detail.html:594 +#: templates/organizations/patient_detail.html:601 msgid "Record Info" msgstr "معلومات السجل" -#: templates/organizations/patient_detail.html:646 +#: templates/organizations/patient_detail.html:653 msgid "Complaint Link" msgstr "رابط الشكوى" @@ -19370,137 +19456,137 @@ msgstr "المعلومات الأساسية" msgid "Save Patient" msgstr "حفظ المريض" -#: templates/organizations/patient_list.html:123 +#: templates/organizations/patient_list.html:124 msgid "Manage patient records and information" msgstr "إدارة سجلات وبيانات المرضى" -#: templates/organizations/patient_list.html:128 -#: templates/organizations/patient_list.html:439 +#: templates/organizations/patient_list.html:129 +#: templates/organizations/patient_list.html:445 msgid "Search HIS" msgstr "البحث في نظام معلومات المستشفى" -#: templates/organizations/patient_list.html:136 +#: templates/organizations/patient_list.html:137 msgid "Add Patient" msgstr "إضافة مريض" -#: templates/organizations/patient_list.html:152 +#: templates/organizations/patient_list.html:153 msgid "Total Patients" msgstr "إجمالي المرضى" -#: templates/organizations/patient_list.html:191 +#: templates/organizations/patient_list.html:192 msgid "Encounters" msgstr "المواعيد" -#: templates/organizations/patient_list.html:207 +#: templates/organizations/patient_list.html:208 msgid "Name, MRN, SSN, Phone..." msgstr "الاسم، رقم المريض، رقم الضمان الاجتماعي، الهاتف..." -#: templates/organizations/patient_list.html:240 +#: templates/organizations/patient_list.html:241 #: templates/px_sources/source_user_complaint_list.html:158 #: templates/px_sources/source_user_inquiry_list.html:146 msgid "Clear filters" msgstr "مسح عوامل التصفية" -#: templates/organizations/patient_list.html:253 +#: templates/organizations/patient_list.html:254 msgid "Patient List" msgstr "قائمة المرضى" -#: templates/organizations/patient_list.html:262 +#: templates/organizations/patient_list.html:263 #: templates/surveys/his_patient_import.html:135 msgid "National ID" msgstr "رقم الهوية الوطنية" -#: templates/organizations/patient_list.html:344 -#: templates/organizations/patient_list.html:515 +#: templates/organizations/patient_list.html:350 +#: templates/organizations/patient_list.html:521 msgid "No patients found" msgstr "لم يتم العثور على مرضى" -#: templates/organizations/patient_list.html:345 +#: templates/organizations/patient_list.html:351 msgid "Try adjusting your filters or add a new patient" msgstr "جرب تعديل عوامل التصفية أو أضف مريضًا جديدًا" -#: templates/organizations/patient_list.html:349 +#: templates/organizations/patient_list.html:355 msgid "Add First Patient" msgstr "إضافة أول مريض" -#: templates/organizations/patient_list.html:440 +#: templates/organizations/patient_list.html:446 msgid "Search for patients in the Hospital Information System" msgstr "ابحث عن المرضى في نظام معلومات المستشفى" -#: templates/organizations/patient_list.html:452 +#: templates/organizations/patient_list.html:458 msgid "National ID (SSN)" msgstr "الهوية الوطنية (رقم الضمان الاجتماعي)" -#: templates/organizations/patient_list.html:463 +#: templates/organizations/patient_list.html:469 msgid "Enter SSN or mobile number to search" msgstr "أدخل رقم السجل المدني أو رقم الجوال للبحث" -#: templates/organizations/patient_list.html:484 +#: templates/organizations/patient_list.html:490 msgid "Searching HIS..." msgstr "جاري البحث في نظام HIS..." -#: templates/organizations/patient_list.html:498 +#: templates/organizations/patient_list.html:504 msgid "Mobile" msgstr "الجوال" -#: templates/organizations/patient_list.html:516 +#: templates/organizations/patient_list.html:522 msgid "Try a different SSN or mobile number" msgstr "جرب رقم سجل مدني أو رقم جوال مختلف" -#: templates/organizations/patient_list.html:524 +#: templates/organizations/patient_list.html:530 msgid "Enter a National ID or Mobile number to search" msgstr "أدخل رقم الهوية الوطنية أو رقم الجوال للبحث" -#: templates/organizations/patient_visit_journey.html:4 -#: templates/organizations/patient_visit_journey.html:79 -#: templates/organizations/patient_visit_journey.html:91 +#: templates/organizations/patient_visit_journey.html:5 +#: templates/organizations/patient_visit_journey.html:80 +#: templates/organizations/patient_visit_journey.html:92 msgid "Visit Journey" msgstr "مسار الزيارة" -#: templates/organizations/patient_visit_journey.html:149 +#: templates/organizations/patient_visit_journey.html:150 #: templates/surveys/instance_detail.html:633 msgid "Visit Timeline" msgstr "الجدول الزمني للزيارة" -#: templates/organizations/patient_visit_journey.html:150 +#: templates/organizations/patient_visit_journey.html:151 msgid "events" msgstr "الأحداث" -#: templates/organizations/patient_visit_journey.html:202 +#: templates/organizations/patient_visit_journey.html:203 msgid "No timeline events recorded for this visit" msgstr "لا توجد أحداث مسجلة في الجدول الزمني لهذه الزيارة" -#: templates/organizations/patient_visit_journey.html:256 +#: templates/organizations/patient_visit_journey.html:257 msgid "Medical Team" msgstr "الفريق الطبي" -#: templates/organizations/patient_visit_journey.html:261 -#: templates/organizations/patient_visit_journey.html:278 +#: templates/organizations/patient_visit_journey.html:262 +#: templates/organizations/patient_visit_journey.html:279 msgid "Primary Doctor" msgstr "الطبيب الرئيسي" -#: templates/organizations/patient_visit_journey.html:285 -#: templates/organizations/patient_visit_journey.html:302 +#: templates/organizations/patient_visit_journey.html:286 +#: templates/organizations/patient_visit_journey.html:303 msgid "Consultant" msgstr "استشاري" -#: templates/organizations/patient_visit_journey.html:315 +#: templates/organizations/patient_visit_journey.html:316 msgid "Visit Details" msgstr "تفاصيل الزيارة" -#: templates/organizations/patient_visit_journey.html:320 +#: templates/organizations/patient_visit_journey.html:321 msgid "Company" msgstr "الشركة" -#: templates/organizations/patient_visit_journey.html:326 +#: templates/organizations/patient_visit_journey.html:327 msgid "Grade" msgstr "الدرجة" -#: templates/organizations/patient_visit_journey.html:337 +#: templates/organizations/patient_visit_journey.html:338 msgid "Patient ID (HIS)" msgstr "معرف المريض (HIS)" -#: templates/organizations/patient_visit_journey.html:342 +#: templates/organizations/patient_visit_journey.html:343 msgid "Last HIS Sync" msgstr "آخر مزامنة مع نظام معلومات المستشفى" @@ -20213,6 +20299,142 @@ msgstr "ترتيب القسم" msgid "No department data available for this period" msgstr "لا توجد بيانات للقسم لهذه الفترة" +#: templates/physicians/doctor_rating_fetch.html:5 +#: templates/physicians/doctor_rating_fetch.html:29 +#, fuzzy +#| msgid "Import doctor ratings from HIS CSV export" +msgid "Fetch Doctor Ratings from HIS" +msgstr "استورد تقييمات الأطباء من تصدير CSV الخاص بـ HIS" + +#: templates/physicians/doctor_rating_fetch.html:31 +msgid "Fetch doctor ratings directly from HIS API by date range" +msgstr "جلب تقييمات الأطباء مباشرة من واجهة HIS البرمجية حسب نطاق التاريخ" + +#: templates/physicians/doctor_rating_fetch.html:37 +#, fuzzy +#| msgid "HIS Import" +msgid "CSV Import" +msgstr "استيراد نظام معلومات المستشفى" + +#: templates/physicians/doctor_rating_fetch.html:42 +#: templates/physicians/doctor_rating_import.html:42 +#: templates/physicians/doctor_rating_job_list.html:70 +#: templates/physicians/doctor_rating_job_list.html:72 +#: templates/physicians/individual_ratings_list.html:87 +msgid "Import History" +msgstr "سجل الاستيراد" + +#: templates/physicians/doctor_rating_fetch.html:56 +#, fuzzy +#| msgid "How it works" +msgid "How It Works" +msgstr "كيف يعمل" + +#: templates/physicians/doctor_rating_fetch.html:62 +#, fuzzy +#| msgid "On Process" +msgid "Fetch Process:" +msgstr "قيد المعالجة" + +#: templates/physicians/doctor_rating_fetch.html:64 +msgid "" +"Ratings are fetched from the HIS FetchDoctorRatingMAPI1 endpoint for the " +"selected date range." +msgstr "" +"يتم جلب التقييمات من نقطة النهاية HIS FetchDoctorRatingMAPI1 لنطاق التاريخ " +"المحدد." + +#: templates/physicians/doctor_rating_fetch.html:69 +msgid "Select a date range and submit" +msgstr "اختر نطاقًا زمنيًا ثم قم بالإرسال" + +#: templates/physicians/doctor_rating_fetch.html:73 +msgid "A background job fetches and processes ratings" +msgstr "تعمل مهمة خلفية في الخلفية لجلب ومعالجة التقييمات" + +#: templates/physicians/doctor_rating_fetch.html:77 +msgid "Track progress on the job status page" +msgstr "تتبع التقدم على صفحة حالة المهمة" + +#: templates/physicians/doctor_rating_fetch.html:82 +#, fuzzy +#| msgid "Response Time" +msgid "HIS Response Fields:" +msgstr "وقت الاستجابة" + +#: templates/physicians/doctor_rating_fetch.html:90 +#, fuzzy +#| msgid "Failed to switch hospital: " +msgid "Matched to PX360 hospital" +msgstr "فشل في تبديل المستشفى:" + +#: templates/physicians/doctor_rating_fetch.html:94 +#: templates/physicians/doctor_rating_import.html:94 +msgid "1-5 rating value" +msgstr "قيمة تقييم من 1 إلى 5" + +#: templates/physicians/doctor_rating_fetch.html:109 +#, fuzzy +#| msgid "Date Range" +msgid "Select Date Range" +msgstr "نطاق التاريخ" + +#: templates/physicians/doctor_rating_fetch.html:112 +#, fuzzy +#| msgid "Searching..." +msgid "Fetching..." +msgstr "جارٍ البحث..." + +#: templates/physicians/doctor_rating_fetch.html:125 +#, fuzzy +#| msgid "Due date for implementing actions" +msgid "Start date for fetching ratings" +msgstr "تاريخ الاستحقاق لتنفيذ الإجراءات" + +#: templates/physicians/doctor_rating_fetch.html:136 +#, fuzzy +#| msgid "Due date for implementing actions" +msgid "End date for fetching ratings" +msgstr "تاريخ الاستحقاق لتنفيذ الإجراءات" + +#: templates/physicians/doctor_rating_fetch.html:169 +msgid "About HIS Fetch" +msgstr "حول HIS Fetch" + +#: templates/physicians/doctor_rating_fetch.html:174 +msgid "" +"This fetches ratings directly from the HIS system via the " +"FetchDoctorRatingMAPI1 API endpoint." +msgstr "" +"يقوم هذا بجلب التقييمات مباشرةً من نظام HIS عبر نقطة نهاية واجهة برمجة " +"التطبيقات FetchDoctorRatingMAPI1." + +#: templates/physicians/doctor_rating_fetch.html:177 +msgid "# HIS API Endpoint" +msgstr "نقطة نهاية واجهة برمجة تطبيقات نظام معلومات المستشفيات" + +#: templates/physicians/doctor_rating_fetch.html:182 +msgid "The monthly scheduled task uses the same process." +msgstr "تستخدم المهمة المجدولة الشهرية نفس العملية." + +#: templates/physicians/doctor_rating_fetch.html:195 +msgid "Use narrow date ranges for faster results" +msgstr "استخدم نطاقات تواريخ ضيقة للحصول على نتائج أسرع" + +#: templates/physicians/doctor_rating_fetch.html:199 +msgid "Hospitals are matched by name from HIS data" +msgstr "يتم مطابقة المستشفيات بالاسم من بيانات نظام معلومات المستشفيات" + +#: templates/physicians/doctor_rating_fetch.html:203 +#, fuzzy +#| msgid "Duplicate ratings for same patient/doctor are ignored" +msgid "Duplicate ratings for same doctor/date are skipped" +msgstr "سيتم تجاهل التقييمات المكررة لنفس المريض/الطبيب" + +#: templates/physicians/doctor_rating_fetch.html:207 +msgid "Job runs in background — you can leave the page" +msgstr "يعمل Job في الخلفية — يمكنك مغادرة الصفحة" + #: templates/physicians/doctor_rating_import.html:5 #: templates/physicians/doctor_rating_import.html:29 msgid "Import Doctor Ratings" @@ -20223,118 +20445,112 @@ msgid "Import doctor ratings from HIS CSV export" msgstr "استورد تقييمات الأطباء من تصدير CSV الخاص بـ HIS" #: templates/physicians/doctor_rating_import.html:37 -#: templates/physicians/doctor_rating_job_list.html:70 -#: templates/physicians/doctor_rating_job_list.html:72 -#: templates/physicians/individual_ratings_list.html:87 -msgid "Import History" -msgstr "سجل الاستيراد" +#: templates/physicians/doctor_rating_job_list.html:80 +msgid "Fetch from HIS" +msgstr "جلب من نظام HIS" -#: templates/physicians/doctor_rating_import.html:51 +#: templates/physicians/doctor_rating_import.html:56 msgid "Instructions" msgstr "التعليمات" -#: templates/physicians/doctor_rating_import.html:57 +#: templates/physicians/doctor_rating_import.html:62 msgid "Expected CSV Format:" msgstr "التنسيق المتوقع لملف CSV:" -#: templates/physicians/doctor_rating_import.html:59 +#: templates/physicians/doctor_rating_import.html:64 msgid "Upload Doctor Rating Report CSV from your HIS system." msgstr "تحميل تقرير تقييم الأطباء بصيغة CSV من نظام HIS الخاص بك." -#: templates/physicians/doctor_rating_import.html:64 +#: templates/physicians/doctor_rating_import.html:69 msgid "Header rows are automatically skipped" msgstr "يتم تخطي صفوف الترويسة تلقائيًا" -#: templates/physicians/doctor_rating_import.html:68 +#: templates/physicians/doctor_rating_import.html:73 msgid "Department headers are detected automatically" msgstr "يتم اكتشاف عناوين الأقسام تلقائيًا" -#: templates/physicians/doctor_rating_import.html:72 +#: templates/physicians/doctor_rating_import.html:77 msgid "Doctor IDs are extracted from names like '10738-NAME'" msgstr "يتم استخراج معرفات الأطباء من الأسماء مثل '10738-NAME'" -#: templates/physicians/doctor_rating_import.html:77 +#: templates/physicians/doctor_rating_import.html:82 msgid "Required Columns:" msgstr "الأعمدة المطلوبة:" -#: templates/physicians/doctor_rating_import.html:81 +#: templates/physicians/doctor_rating_import.html:86 #: templates/simulator/log_detail.html:244 msgid "Patient MRN" msgstr "الرقم الطبي للمريض (MRN)" -#: templates/physicians/doctor_rating_import.html:85 +#: templates/physicians/doctor_rating_import.html:90 msgid "With or without ID prefix" msgstr "مع أو بدون بادئة المعرف" -#: templates/physicians/doctor_rating_import.html:89 -msgid "1-5 rating value" -msgstr "قيمة تقييم من 1 إلى 5" - -#: templates/physicians/doctor_rating_import.html:107 +#: templates/physicians/doctor_rating_import.html:112 #: templates/surveys/his_patient_import.html:35 msgid "Importing..." msgstr "جارٍ الاستيراد..." -#: templates/physicians/doctor_rating_import.html:121 +#: templates/physicians/doctor_rating_import.html:126 msgid "Select hospital these ratings belong to" msgstr "اختر المستشفى الذي تنتمي إليه هذه التقييمات" -#: templates/physicians/doctor_rating_import.html:136 +#: templates/physicians/doctor_rating_import.html:141 msgid "Default is 6 rows (Doctor Rating Report format)" msgstr "الافتراضي هو 6 صفوف (تنسيق تقرير تقييم الطبيب)" -#: templates/physicians/doctor_rating_import.html:150 +#: templates/physicians/doctor_rating_import.html:155 msgid "Drag and drop your CSV file here" msgstr "اسحب وأفلت ملف CSV هنا" -#: templates/physicians/doctor_rating_import.html:151 +#: templates/physicians/doctor_rating_import.html:156 #: templates/references/document_form.html:231 msgid "or click to browse" msgstr "أو انقر للتصفح" -#: templates/physicians/doctor_rating_import.html:160 +#: templates/physicians/doctor_rating_import.html:165 msgid "Maximum file size: 10MB. Only .csv files accepted." msgstr "الحد الأقصى لحجم الملف: 10 ميجابايت. يتم قبول ملفات .csv فقط." -#: templates/physicians/doctor_rating_import.html:167 +#: templates/physicians/doctor_rating_import.html:172 msgid "Upload & Preview" msgstr "تحميل ومعاينة" -#: templates/physicians/doctor_rating_import.html:185 +#: templates/physicians/doctor_rating_import.html:190 msgid "API Integration" msgstr "تكامل واجهة برمجة التطبيقات (API)" -#: templates/physicians/doctor_rating_import.html:190 +#: templates/physicians/doctor_rating_import.html:195 msgid "You can also integrate directly with your HIS system using our API:" msgstr "" "يمكنك أيضًا التكامل مباشرة مع نظام HIS الخاص بك باستخدام واجهة برمجة " "التطبيقات (API) الخاصة بنا:" -#: templates/physicians/doctor_rating_import.html:194 +#: templates/physicians/doctor_rating_import.html:199 msgid "# Or for single ratings:" msgstr "# أو للتقييمات الفردية:" -#: templates/physicians/doctor_rating_import.html:199 +#: templates/physicians/doctor_rating_import.html:204 msgid "See API documentation for more details." msgstr "راجع وثائق واجهة برمجة التطبيقات (API) للمزيد من التفاصيل." -#: templates/physicians/doctor_rating_import.html:203 +#: templates/physicians/doctor_rating_import.html:208 msgid "View API Docs" msgstr "عرض وثائق واجهة برمجة التطبيقات (API)" -#: templates/physicians/doctor_rating_import.html:217 +#: templates/physicians/doctor_rating_import.html:222 msgid "Ensure CSV is exported with UTF-8 encoding" msgstr "تأكد من تصدير ملف CSV بترميز UTF-8" -#: templates/physicians/doctor_rating_import.html:221 +#: templates/physicians/doctor_rating_import.html:226 msgid "Verify doctor IDs match PX360 records" msgstr "تحقق من مطابقة معرفات الأطباء مع سجلات PX360" -#: templates/physicians/doctor_rating_import.html:225 +#: templates/physicians/doctor_rating_import.html:230 msgid "Ratings outside 1-5 range will be skipped" msgstr "سيتم تجاهل التقييمات خارج نطاق 1-5" -#: templates/physicians/doctor_rating_import.html:229 +#: templates/physicians/doctor_rating_import.html:234 msgid "Duplicate ratings for same patient/doctor are ignored" msgstr "سيتم تجاهل التقييمات المكررة لنفس المريض/الطبيب" @@ -20352,39 +20568,39 @@ msgstr "تقييمات الأطباء" msgid "Track doctor rating import jobs" msgstr "تتبع وظائف استيراد تقييمات الأطباء" -#: templates/physicians/doctor_rating_job_list.html:79 +#: templates/physicians/doctor_rating_job_list.html:85 msgid "New Import" msgstr "استيراد جديد" -#: templates/physicians/doctor_rating_job_list.html:90 +#: templates/physicians/doctor_rating_job_list.html:97 #: templates/physicians/doctor_rating_job_status.html:34 msgid "Import Jobs" msgstr "استيراد المهام" -#: templates/physicians/doctor_rating_job_list.html:97 +#: templates/physicians/doctor_rating_job_list.html:104 msgid "Job Name" msgstr "اسم المهمة" -#: templates/physicians/doctor_rating_job_list.html:120 +#: templates/physicians/doctor_rating_job_list.html:127 #: templates/physicians/individual_ratings_list.html:259 msgid "HIS API" msgstr "HIS API" -#: templates/physicians/doctor_rating_job_list.html:125 +#: templates/physicians/doctor_rating_job_list.html:132 msgid "CSV Upload" msgstr "رفع ملف CSV" -#: templates/physicians/doctor_rating_job_list.html:153 +#: templates/physicians/doctor_rating_job_list.html:160 #: templates/simulator/log_list.html:132 templates/simulator/log_list.html:231 #: templates/standards/dashboard.html:133 msgid "Partial" msgstr "جزئي" -#: templates/physicians/doctor_rating_job_list.html:205 +#: templates/physicians/doctor_rating_job_list.html:212 msgid "No Import Jobs" msgstr "لا توجد مهام استيراد" -#: templates/physicians/doctor_rating_job_list.html:206 +#: templates/physicians/doctor_rating_job_list.html:213 msgid "No import jobs found. Start by importing doctor ratings." msgstr "لم يتم العثور على وظائف استيراد. ابدأ باستيراد تقييمات الأطباء." @@ -20751,11 +20967,6 @@ msgstr "استبيانات" msgid "No data" msgstr "لا توجد بيانات" -#: templates/physicians/physician_list.html:271 -#: templates/physicians/ratings_list.html:298 -msgid "Try adjusting your filters" -msgstr "جرب تعديل عوامل التصفية الخاصة بك" - #: templates/physicians/physician_ratings_dashboard.html:4 #: templates/physicians/physician_ratings_dashboard.html:243 msgid "Physician Ratings Dashboard" @@ -21009,7 +21220,7 @@ msgid "Start Date" msgstr "تاريخ البدء" #: templates/projects/project_detail.html:204 -#: templates/rca/rca_detail.html:510 +#: templates/rca/rca_detail.html:500 msgid "Target Date" msgstr "التاريخ المستهدف" @@ -21438,27 +21649,53 @@ msgstr "إحصائيات الاستخدام (آخر 30 يومًا)" msgid "Total Usage" msgstr "إجمالي الاستخدام" -#: templates/px_sources/source_detail.html:262 +#: templates/px_sources/source_detail.html:223 +#, fuzzy +#| msgid "Create Item" +msgid "Related Items" +msgstr "إنشاء عنصر" + +#: templates/px_sources/source_detail.html:302 +#, fuzzy +#| msgid "No complaints from this source yet." +msgid "No complaints from this source" +msgstr "لا توجد شكاوى من هذا المصدر حتى الآن." + +#: templates/px_sources/source_detail.html:303 +msgid "Complaints will appear here when submitted through this source" +msgstr "ستظهر الشكاوى هنا عند إرسالها من خلال هذا المصدر" + +#: templates/px_sources/source_detail.html:366 +#, fuzzy +#| msgid "No inquiries from this source yet." +msgid "No inquiries from this source" +msgstr "لا توجد استفسارات من هذا المصدر حتى الآن." + +#: templates/px_sources/source_detail.html:367 +msgid "Inquiries will appear here when submitted through this source" +msgstr "ستظهر الاستفسارات هنا عند إرسالها من خلال هذا المصدر" + +#: templates/px_sources/source_detail.html:418 msgid "No usage records found" msgstr "لم يتم العثور على سجلات استخدام" -#: templates/px_sources/source_detail.html:263 +#: templates/px_sources/source_detail.html:419 msgid "Activity will appear here once feedback is submitted" msgstr "ستظهر النشاطات هنا بمجرد تقديم الملاحظات" -#: templates/px_sources/source_detail.html:277 +#: templates/px_sources/source_detail.html:433 msgid "Quick Stats" msgstr "إحصائيات سريعة" -#: templates/px_sources/source_detail.html:291 +#: templates/px_sources/source_detail.html:447 msgid "Source Users" msgstr "مستخدمو المصدر" -#: templates/px_sources/source_detail.html:331 +#: templates/px_sources/source_detail.html:487 msgid "Add Source User" msgstr "إضافة مستخدم مصدر" -#: templates/px_sources/source_detail.html:337 +#: templates/px_sources/source_detail.html:493 #: templates/px_sources/source_form.html:4 #: templates/px_sources/source_form.html:143 #: templates/px_sources/source_form.html:154 @@ -21813,7 +22050,7 @@ msgstr "تاريخ الإنجاز المستهدف" #: templates/rca/rca_detail.html:174 templates/rca/rca_form.html:64 msgid "Background" -msgstr "" +msgstr "الخلفية" #: templates/rca/rca_detail.html:182 templates/rca/rca_form.html:70 #, fuzzy @@ -21833,8 +22070,8 @@ msgstr "الجدول الزمني المقترح" msgid "No status changes recorded." msgstr "لم يتم العثور على سجلات استخدام" -#: templates/rca/rca_detail.html:261 templates/rca/rca_detail.html:408 -#: templates/rca/rca_detail.html:452 +#: templates/rca/rca_detail.html:261 templates/rca/rca_detail.html:406 +#: templates/rca/rca_detail.html:447 #, fuzzy #| msgid "Add Note" msgid "Add Root Cause" @@ -21842,7 +22079,7 @@ msgstr "إضافة ملاحظة" #: templates/rca/rca_detail.html:294 msgid "No root causes added." -msgstr "" +msgstr "لا توجد أسباب جوهرية مضافة." #: templates/rca/rca_detail.html:307 #, fuzzy @@ -21850,7 +22087,7 @@ msgstr "" msgid "Corrective Actions" msgstr "إنشاء إجراء" -#: templates/rca/rca_detail.html:311 templates/rca/rca_detail.html:517 +#: templates/rca/rca_detail.html:311 templates/rca/rca_detail.html:508 #, fuzzy #| msgid "Add Section" msgid "Add Action" @@ -21868,33 +22105,33 @@ msgstr "حالة الإجراءات التصحيحية" msgid "No notes added." msgstr "تمت إضافة ملاحظة" -#: templates/rca/rca_detail.html:437 +#: templates/rca/rca_detail.html:431 msgid "Likelihood" -msgstr "" +msgstr "الاحتمال" -#: templates/rca/rca_detail.html:441 +#: templates/rca/rca_detail.html:435 #: templates/standards/category_confirm_delete.html:139 #: templates/standards/source_confirm_delete.html:147 msgid "Impact" msgstr "التأثير" -#: templates/rca/rca_detail.html:446 +#: templates/rca/rca_detail.html:440 msgid "Contributing Factors" msgstr "العوامل المساعدة" -#: templates/rca/rca_detail.html:467 +#: templates/rca/rca_detail.html:461 #, fuzzy #| msgid "Create Action" msgid "Add Corrective Action" msgstr "إنشاء إجراء" -#: templates/rca/rca_detail.html:491 +#: templates/rca/rca_detail.html:481 #, fuzzy #| msgid "Related To" msgid "Related Root Cause" msgstr "مرتبط بـ" -#: templates/rca/rca_detail.html:547 +#: templates/rca/rca_detail.html:533 #, fuzzy #| msgid "Internal" msgid "Internal note" @@ -22756,13 +22993,6 @@ msgstr "معرّف الرحلة" msgid "Request Payload" msgstr "محتوى الطلب (Payload)" -#: templates/simulator/log_detail.html:268 -#: templates/simulator/log_detail.html:283 -#: templates/social/partials/ai_analysis_bilingual.html:83 -#: templates/social/partials/ai_analysis_bilingual.html:89 -msgid "Copy" -msgstr "نسخ" - #: templates/simulator/log_detail.html:280 msgid "Response Data" msgstr "بيانات الاستجابة" @@ -23497,6 +23727,11 @@ msgstr "العودة إلى المصادر" msgid "Are you sure you want to delete this source?" msgstr "هل أنت متأكد من رغبتك في حذف هذا المصدر؟" +#: templates/standards/source_confirm_delete.html:112 +#: templates/standards/source_list.html:129 +msgid "Website" +msgstr "الموقع الإلكتروني" + #: templates/standards/source_confirm_delete.html:151 msgid "Deleting this source will affect:" msgstr "سيؤثر حذف هذا المصدر على:" @@ -24572,6 +24807,10 @@ msgstr "رابط الاستبيان" msgid "Copy Link" msgstr "نسخ الرابط" +#: templates/surveys/instance_detail.html:538 +msgid "Survey Information" +msgstr "معلومات الاستبيان" + #: templates/surveys/instance_detail.html:557 msgid "Total Score" msgstr "الدرجة الإجمالية" @@ -25072,6 +25311,243 @@ msgstr "لا يمكن التراجع عن هذا الإجراء" msgid "Are you sure you want to delete the survey template" msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاستبيان" +#~ msgid "Your comprehensive Patient Experience management platform" +#~ msgstr "منصة إدارة تجربة المريض الشاملة الخاصة بك" + +#~ msgid "Activate your account and start using PX360" +#~ msgstr "تفعيل حسابك والبدء باستخدام PX360" + +#~ msgid "" +#~ "This invitation link will expire in 7 days. If you don't complete the setup " +#~ "within this period, you'll need to request a new invitation." +#~ msgstr "" +#~ "ستنتهي صلاحية رابط الدعوة هذا خلال 7 أيام. إذا لم تكمل الإعداد خلال هذه " +#~ "الفترة، ستحتاج إلى طلب دعوة جديدة." + +#~ msgid "" +#~ "If you have any questions or need assistance, please contact our support " +#~ "team at" +#~ msgstr "" +#~ "إذا كان لديك أي أسئلة أو تحتاج إلى مساعدة، يرجى التواصل مع فريق الدعم لدينا " +#~ "على" + +#~ msgid "or call us at" +#~ msgstr "أو اتصل بنا على" + +#~ msgid "Review Onboarding Material" +#~ msgstr "مراجعة مواد الإعداد" + +#~ msgid "Please review the following important information" +#~ msgstr "يرجى مراجعة المعلومات المهمة التالية" + +#~ msgid "Step 3 of 3" +#~ msgstr "الخطوة 3 من 3" + +#~ msgid "Complete Onboarding" +#~ msgstr "إكمال التسجيل" + +#~ msgid "PX Admin User" +#~ msgstr "مسؤول المستخدم PX" + +#~ msgid "Select an admin..." +#~ msgstr "اختر مسؤولًا..." + +#, fuzzy +#~ msgid "Run AI Analysis" +#~ msgstr "تحليل الذكاء الاصطناعي" + +#~ msgid "Configure automatic escalation when deadlines are exceeded" +#~ msgstr "إعداد التصعيد التلقائي عند تجاوز المهل الزمنية" + +#, fuzzy +#~ msgid "Manage Rules" +#~ msgstr "إدارة الجداول" + +#~ msgid "Set alert thresholds for complaint volume monitoring" +#~ msgstr "تحديد حدود التنبيه لمراقبة حجم الشكاوى" + +#, fuzzy +#~ msgid "Manage Thresholds" +#~ msgstr "الحدود" + +#~ msgid "General SLA" +#~ msgstr "اتفاقية مستوى الخدمة العامة" + +#~ msgid "active configs" +#~ msgstr "الإعدادات النشطة" + +#, fuzzy +#~ msgid "Manage SLA" +#~ msgstr "إدارة" + +#, fuzzy +#~ msgid "Manage Routing" +#~ msgstr "إدارة قواعد التوجيه" + +#, fuzzy +#~ msgid "Manage system users and permissions" +#~ msgstr "إدارة إعدادات وتكوينات النظام" + +#, fuzzy +#~ msgid "Configure hospital notification settings" +#~ msgstr "إشعارات المستشفى" + +#~ msgid "Configure" +#~ msgstr "إعداد" + +#, fuzzy +#~ msgid "Contact Us" +#~ msgstr "جهة الاتصال" + +#~ msgid "Privacy Policy" +#~ msgstr "سياسة الخصوصية" + +#~ msgid "Unsubscribe" +#~ msgstr "إلغاء الاشتراك" + +#~ msgid "from these emails" +#~ msgstr "من هذه الرسائل الإلكترونية" + +#~ msgid "Please review the complaint details and submit your response" +#~ msgstr "يرجى مراجعة تفاصيل الشكوى وإرسال ردك" + +#~ msgid "Important Information:" +#~ msgstr "معلومات مهمة:" + +#~ msgid "This link is unique and can only be used once" +#~ msgstr "هذا الرابط فريد ويمكن استخدامه مرة واحدة فقط" + +#~ msgid "You can attach supporting documents to your explanation" +#~ msgstr "يمكنك إرفاق مستندات داعمة لإيضاحك" + +#~ msgid "Your response will be reviewed by the PX team" +#~ msgstr "سيتم مراجعة ردك من قبل فريق PX" + +#~ msgid "Please submit your explanation at your earliest convenience" +#~ msgstr "يرجى تقديم إيضاحك في أقرب وقت ممكن" + +#, fuzzy +#~ msgid "New Observation Notification - Al Hammadi Hospital" +#~ msgstr "إشعار شكوى جديد - مستشفى الحمادي" + +#, fuzzy +#~ msgid "A new observation has been submitted and requires review." +#~ msgstr "تم إرسال ملاحظتك بنجاح." + +#, fuzzy +#~ msgid "" +#~ "A new observation has been submitted and requires your review. Please assess" +#~ " the details below and take appropriate action." +#~ msgstr "تم تقديم شكوى جديدة وتتطلب انتباهك. يرجى مراجعة التفاصيل أدناه." + +#, fuzzy +#~ msgid "" +#~ "Please review this observation and assign it to the appropriate team member " +#~ "for further action." +#~ msgstr "" +#~ "يرجى مراجعة هذه الشكوى واتخاذ الإجراء المناسب قبل موعد SLA لتجنب الإخلال." + +#~ msgid "" +#~ "Help us improve our services by sharing your recent experience at Al Hammadi" +#~ " Hospital" +#~ msgstr "" +#~ "ساعدنا في تحسين خدماتنا من خلال مشاركة تجربتك الأخيرة في مستشفى الحمادي" + +#, fuzzy +#~ msgid "3-5" +#~ msgstr "1-5" + +#, python-format +#~ msgid "" +#~ "Your feedback is invaluable in helping us maintain and improve the quality " +#~ "of care we provide. Would you mind taking %(duration)s minutes to complete " +#~ "our patient experience survey?" +#~ msgstr "" +#~ "ملاحظاتكم لا تقدر بثمن في مساعدتنا على الحفاظ على جودة الرعاية التي نقدمها " +#~ "وتحسينها. هل تمانعون في تخصيص %(duration)s دقيقة لإكمال استبيان تجربة المريض" +#~ " الخاص بنا؟" + +#, fuzzy +#~ msgid "Why Your Feedback Matters:" +#~ msgstr "نقدّر ملاحظاتك" + +#, fuzzy +#~ msgid "Improve Patient Care:" +#~ msgstr "تحسين تجربة المريض:" + +#, fuzzy +#~ msgid "Your insights help us enhance our services" +#~ msgstr "ملاحظاتك تساعدنا على تحسين خدماتنا" + +#, fuzzy +#~ msgid "Better Experience:" +#~ msgstr "تجربة المريض" + +#~ msgid "Help us create a better experience for all patients" +#~ msgstr "ساعدنا في خلق تجربة أفضل لجميع المرضى" + +#, fuzzy +#~ msgid "Quality Standards:" +#~ msgstr "إجمالي المعايير" + +#~ msgid "Contribute to our commitment to excellence" +#~ msgstr "ساهم في التزامنا بالتميز" + +#, fuzzy +#~ msgid "Start Survey" +#~ msgstr "يوجد استبيان" + +#, fuzzy +#~ msgid "Duration:" +#~ msgstr "المدة" + +#~ msgid "Approximately" +#~ msgstr "تقريباً" + +#~ msgid "minutes" +#~ msgstr "دقيقة" + +#, fuzzy +#~ msgid "Confidentiality:" +#~ msgstr "الثقة" + +#, fuzzy +#~ msgid "Please complete by" +#~ msgstr "يرجى التحديد" + +#~ msgid "the end of this week" +#~ msgstr "نهاية هذا الأسبوع" + +#~ msgid "Enter your reference number to check the status" +#~ msgstr "أدخل رقمك المرجعي للتحقق من الحالة" + +#~ msgid "Track" +#~ msgstr "تتبع" + +#~ msgid "Status Progress" +#~ msgstr "تقدم الحالة" + +#~ msgid "Being reviewed by our team" +#~ msgstr "قيد المراجعة من قبل فريقنا" + +#~ msgid "Action is being taken" +#~ msgstr "يتم اتخاذ إجراء" + +#~ msgid "Rejected" +#~ msgstr "مرفوض" + +#~ msgid "This observation was not accepted" +#~ msgstr "لم يتم قبول هذه الملاحظة" + +#~ msgid "Duplicate" +#~ msgstr "مكررة" + +#~ msgid "This observation was marked as duplicate" +#~ msgstr "تم تحديد هذه الملاحظة كمكررة" + +#~ msgid "Submit a new observation" +#~ msgstr "إرسال ملاحظة جديدة" + #~ msgid "Medical Director" #~ msgstr "المدير الطبي" @@ -25168,9 +25644,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ msgid "Person responsible for implementing actions" #~ msgstr "الشخص المسؤول عن تنفيذ الإجراءات" -#~ msgid "Due date for implementing actions" -#~ msgstr "تاريخ الاستحقاق لتنفيذ الإجراءات" - #~ msgid "Verified Effective" #~ msgstr "تم التحقق من الفعالية" @@ -25216,9 +25689,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ msgid "Onboarding Checklist" #~ msgstr "قائمة مراجعة الإعداد" -#~ msgid "Complete Your Checklist" -#~ msgstr "أكمل قائمة التحقق الخاصة بك" - #~ msgid "Please complete the following onboarding tasks" #~ msgstr "يرجى إكمال مهام التسجيل هذه" @@ -25769,9 +26239,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ msgid "You've reviewed all system content" #~ msgstr "تمت مراجعة جميع محتويات النظام" -#~ msgid "Your credentials are ready" -#~ msgstr "بيانات الدخول جاهزة" - #~ msgid "What's Next?" #~ msgstr "ما الخطوة التالية؟" @@ -26620,9 +27087,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ msgid "Are you sure you want to send a new invitation email to" #~ msgstr "هل أنت متأكد من رغبتك في إرسال بريد دعوة جديد إلى" -#~ msgid "A new password will be generated and sent to" -#~ msgstr "سيتم إنشاء كلمة مرور جديدة وإرسالها إلى" - #~ msgid "Are you sure you want to unlink the user account from" #~ msgstr "هل أنت متأكد من رغبتك في فصل حساب المستخدم عن" @@ -26633,9 +27097,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ "هل أنت متأكد أنك تريد إعادة تعيين كلمة المرور وإعادة إرسال بيانات الدخول؟ " #~ "سيتم إنشاء كلمة مرور جديدة وعرضها لك." -#~ msgid "Resetting..." -#~ msgstr "جارٍ إعادة التعيين..." - #~ msgid "Password has been reset and credentials email sent!" #~ msgstr "" #~ "تمت إعادة تعيين كلمة المرور وإرسال بيانات الدخول عبر البريد الإلكتروني!" @@ -26686,13 +27147,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف نموذج الاست #~ msgid "Are you sure you want to reset the password for" #~ msgstr "هل أنت متأكد أنك تريد إعادة تعيين كلمة المرور لـ" -#~ msgid "" -#~ "The new password will be displayed to you after reset. Make sure to share it" -#~ " securely if needed." -#~ msgstr "" -#~ "سيتم عرض كلمة المرور الجديدة لك بعد إعادة التعيين. تأكد من مشاركتها بشكل آمن" -#~ " عند الحاجة." - #~ msgid "Copy this password and share it securely with the user:" #~ msgstr "انسخ كلمة المرور هذه وشاركها بأمان مع المستخدم:" diff --git a/requirements.txt b/requirements.txt index be530ce..1d8dd03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,58 +1,141 @@ -httpx==0.28.1 +aiohappyeyeballs==2.6.1 +aiohttp==3.13.3 +aiohttp-retry==2.9.1 +aiosignal==1.4.0 amqp==5.3.1 +annotated-types==0.7.0 +anyio==4.12.0 asgiref==3.11.0 +attrs==25.4.0 billiard==4.2.4 +brotli==1.2.0 +cachetools==6.2.4 celery==5.6.2 certifi==2026.1.4 +cffi==2.0.0 charset-normalizer==3.4.4 click==8.3.1 click-didyoumean==0.3.1 click-plugins==1.1.1.2 click-repl==0.3.0 -cron_descriptor==2.0.6 -Django==5.2.10 -django-celery-beat==2.8.1 -django-crontab==0.7.1 +cron-descriptor==1.4.5 +cssselect2==0.8.0 +distro==1.9.0 +django==6.0.1 +django-celery-beat==2.9.0 +django-environ==0.12.0 django-extensions==4.1 +django-filter==25.1 +django-stubs==5.2.8 +django-stubs-ext==5.2.8 django-timezone-field==7.2.1 +djangorestframework==3.16.1 +djangorestframework-simplejwt==5.5.1 +djangorestframework-stubs==3.16.6 +drf-spectacular==0.29.0 +et-xmlfile==2.0.0 +fastuuid==0.14.0 +filelock==3.20.2 +fonttools==4.61.1 +frozenlist==1.8.0 +fsspec==2025.12.0 google-api-core==2.29.0 -google-api-python-client==2.188.0 -google-auth==2.47.0 +google-api-python-client==2.187.0 +google-auth==2.41.1 google-auth-httplib2==0.3.0 -google-auth-oauthlib==1.2.4 +google-auth-oauthlib==1.2.3 googleapis-common-protos==1.72.0 -httplib2==0.31.2 +grpcio==1.67.1 +gunicorn==23.0.0 +h11==0.16.0 +hf-xet==1.2.0 +httpcore==1.0.9 +httplib2==0.31.0 +httpx==0.28.1 +huggingface-hub==1.2.3 idna==3.11 +importlib-metadata==8.7.1 +inflection==0.5.1 +jinja2==3.1.6 +jiter==0.12.0 +jsonschema==4.25.1 +jsonschema-specifications==2025.9.1 kombu==5.6.2 +litellm==1.80.11 +markdown-it-py==4.0.0 +markupsafe==3.0.3 +mdurl==0.1.2 +multidict==6.7.0 +numpy==2.4.3 oauthlib==3.3.1 -packaging==26.0 -prompt_toolkit==3.0.52 +openai==2.14.0 +openpyxl==3.1.5 +packaging==25.0 +pandas==3.0.1 +pillow==12.1.0 +pip==24.0 +polib==1.2.0 +prompt-toolkit==3.0.52 +propcache==0.4.1 proto-plus==1.27.0 -protobuf==6.33.4 -pyasn1==0.6.2 -pyasn1_modules==0.4.2 -pyparsing==3.3.2 +protobuf==6.33.3 +psycopg2-binary==2.9.11 +-e file:///home/ismail/projects/HH +pyasn1==0.6.1 +pyasn1-modules==0.4.2 +pycparser==2.23 +pydantic==2.12.5 +pydantic-core==2.41.5 +pydyf==0.12.1 +pygments==2.19.2 +pyjwt==2.10.1 +pyparsing==3.3.1 +pyphen==0.17.2 python-crontab==3.3.0 python-dateutil==2.9.0.post0 +python-dotenv==1.2.1 +pytz==2025.2 +pyyaml==6.0.3 redis==7.1.0 +referencing==0.37.0 +regex==2025.11.3 +reportlab==4.4.7 requests==2.32.5 requests-oauthlib==2.0.0 +rich==14.2.0 +rpds-py==0.30.0 rsa==4.9.1 +shellingham==1.5.4 six==1.17.0 +sniffio==1.3.1 sqlparse==0.5.5 -stack-data==0.6.3 -traitlets==5.14.3 -trio==0.32.0 -trio-websocket==0.12.2 +tiktoken==0.12.0 +tinycss2==1.5.1 +tinyhtml5==2.0.0 +tokenizers==0.22.2 +tqdm==4.67.1 tweepy==4.16.0 twilio==9.10.3 -types-PyYAML==6.0.12.20250915 +typer-slim==0.21.0 +types-pyyaml==6.0.12.20250915 types-requests==2.32.4.20250913 -typing_extensions==4.15.0 +typing-extensions==4.15.0 +typing-inspection==0.4.2 tzdata==2025.3 tzlocal==5.3.1 +ua-parser==1.0.1 +ua-parser-builtins==202601 +unidecode==1.4.0 uritemplate==4.2.0 -urllib3==2.6.3 +urllib3==2.6.2 +user-agents==2.2.0 vine==5.1.0 -wcwidth==0.3.1 -rich==13.9.4 +watchdog==6.0.0 +wcwidth==0.2.14 +weasyprint==67.0 +webencodings==0.5.1 +whitenoise==6.11.0 +xlrd==2.0.2 +yarl==1.22.0 +zipp==3.23.0 +zopfli==0.4.0 diff --git a/templates/accounts/onboarding/invitation_email.html b/templates/accounts/onboarding/invitation_email.html index 57596bb..ddc49d4 100644 --- a/templates/accounts/onboarding/invitation_email.html +++ b/templates/accounts/onboarding/invitation_email.html @@ -2,137 +2,48 @@ {% load i18n %} {% block title %}{% trans "Welcome to PX360 - Al Hammadi Hospital" %}{% endblock %} - {% block preheader %}{% trans "You have been invited to join PX360. Complete your account setup." %}{% endblock %} -{% block hero_title %}{% trans "Welcome to PX360!" %}{% endblock %} - -{% block hero_subtitle %}{% trans "Your comprehensive Patient Experience management platform" %}{% endblock %} - {% block content %} - - - - - -
-

- {% blocktrans with name=user.first_name|default:user.email %}Hello {{ name }},{% endblocktrans %} -

-

- {% trans "You have been invited to join PX360, our comprehensive Patient Experience management platform. To complete your account setup, please click the button below." %} -

-
+

{% trans "Welcome to PX360!" %}

- - - - - - - - -
-

- {% trans "During the onboarding process, you will:" %} -

-
- - - - - - -
- - -

- {% trans "Learn about PX360 features and your role responsibilities" %} -

-
- - - - - - - -
- - -

- {% trans "Set up your profile and preferences" %} -

-
- - - - - - - -
- - -

- {% trans "Complete required training materials" %} -

-
- - - - - - - -
- - -

- {% trans "Activate your account and start using PX360" %} -

-
-
+

+ {% blocktrans with name=user.first_name|default:user.email %}Hello {{ name }},{% endblocktrans %} +

+

+ {% trans "You have been invited to join PX360, our comprehensive Patient Experience management platform. To complete your account setup, please click the button below." %} +

+ + +
+

{% trans "During onboarding, you will:" %}

+ + + + + +
✓ {% trans "Learn about PX360 features and your role" %}
✓ {% trans "Set up your profile and preferences" %}
✓ {% trans "Complete required training" %}
✓ {% trans "Activate your account" %}
+
- +
- - - - -
- - {% trans "Complete Account Setup" %} - -
+ + {% trans "Complete Account Setup" %} +
- - - - - -
-

- {% trans "Important:" %} {% trans "This invitation link will expire in 7 days. If you don't complete the setup within this period, you'll need to request a new invitation." %} -

-
+
+

+ {% trans "Important:" %} {% trans "This invitation link will expire in 7 days." %} +

+
- - - - - -
-

- {% trans "If you have any questions or need assistance, please contact our support team at" %} - support@alhammadi.com - {% trans "or call us at" %} +966 11 123 4567. -

-
+

+ {% trans "Need help? Contact support@alhammadi.com or call +966 11 123 4567." %} +

{% endblock %} diff --git a/templates/accounts/onboarding/step_content.html b/templates/accounts/onboarding/step_content.html index 6b48336..dcc02f3 100644 --- a/templates/accounts/onboarding/step_content.html +++ b/templates/accounts/onboarding/step_content.html @@ -12,64 +12,99 @@
+ {% if current_content.icon %} + + {% else %} + {% endif %}

- {% trans "Review Onboarding Material" %} + {{ current_content.get_localized_title }}

+ {% if current_content.get_localized_description %}

- {% trans "Please review the following important information" %} + {{ current_content.get_localized_description }}

+ {% endif %}
- {% trans "Step 3 of 3" %} - 100% + {% trans "Step" %} {{ step }} {% trans "of" %} {{ content|length }} + {{ progress_percentage }}%
-
-
+
+
- -
- {% for content in content_items %} - @@ -78,6 +113,16 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/analytics/dashboard.html b/templates/analytics/dashboard.html index 92f77c9..852eb2d 100644 --- a/templates/analytics/dashboard.html +++ b/templates/analytics/dashboard.html @@ -826,9 +826,32 @@ function refreshDashboard() { const icon = document.querySelector('[data-lucide="refresh-cw"]'); icon.classList.add('animate-spin'); - setTimeout(() => { - window.location.reload(); - }, 500); + + // Call API to trigger cache refresh + fetch('{% url "analytics:refresh_dashboard_cache" %}', { + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}', + 'Content-Type': 'application/json', + }, + }) + .then(r => r.json()) + .then(data => { + showToast(data.message || 'Dashboard cache refresh triggered'); + // Reload after a short delay to get fresh data + setTimeout(() => { + window.location.reload(); + }, 2000); + }) + .catch(err => { + console.error('Failed to trigger dashboard refresh:', err); + showToast('Failed to trigger dashboard refresh'); + icon.classList.remove('animate-spin'); + // Fallback: just reload the page + setTimeout(() => { + window.location.reload(); + }, 500); + }); } function refreshAiAnalytics() { diff --git a/templates/callcenter/complaint_form.html b/templates/callcenter/complaint_form.html index 91f1e4d..f7aa1ee 100644 --- a/templates/callcenter/complaint_form.html +++ b/templates/callcenter/complaint_form.html @@ -357,7 +357,7 @@ document.addEventListener('DOMContentLoaded', function() { MRN: ${patient.mrn} | Phone: ${patient.phone || 'N/A'} | - ID: ${patient.national_id || 'N/A'} + ID: ${patient.national_id_masked || 'N/A'}
diff --git a/templates/callcenter/inquiry_form.html b/templates/callcenter/inquiry_form.html index 762a7ae..2d8df4c 100644 --- a/templates/callcenter/inquiry_form.html +++ b/templates/callcenter/inquiry_form.html @@ -302,7 +302,7 @@ document.addEventListener('DOMContentLoaded', function() { MRN: ${patient.mrn} | Phone: ${patient.phone || 'N/A'} | - ID: ${patient.national_id || 'N/A'} + ID: ${patient.national_id_masked || 'N/A'}
diff --git a/templates/complaints/complaint_form.html b/templates/complaints/complaint_form.html index dc838ee..7e3e91f 100644 --- a/templates/complaints/complaint_form.html +++ b/templates/complaints/complaint_form.html @@ -799,6 +799,74 @@ document.addEventListener('DOMContentLoaded', function() { }); } + // Location hierarchy cascading dropdowns + if (locationSelect) { + locationSelect.addEventListener('change', function () { + const locationId = this.value; + if (!locationId) { + if (mainSectionSelect) mainSectionSelect.innerHTML = ''; + if (subsectionSelect) subsectionSelect.innerHTML = ''; + return; + } + if (mainSectionSelect) { + mainSectionSelect.innerHTML = ''; + } + fetch('{% url "organizations:ajax_main_sections" %}?location_id=' + locationId) + .then(r => r.json()) + .then(data => { + const sections = data.sections || []; + if (mainSectionSelect) { + mainSectionSelect.innerHTML = ''; + sections.forEach(section => { + const opt = document.createElement('option'); + opt.value = section.id; + opt.textContent = {% if LANG == 'ar' %}section.name_ar || section.name{% else %}section.name{% endif %}; + mainSectionSelect.appendChild(opt); + }); + } + if (subsectionSelect) { + subsectionSelect.innerHTML = ''; + } + }) + .catch(err => { + console.error('Failed to load main sections:', err); + if (mainSectionSelect) mainSectionSelect.innerHTML = ''; + }); + }); + } + + if (mainSectionSelect) { + mainSectionSelect.addEventListener('change', function () { + const locationId = locationSelect ? locationSelect.value : ''; + const mainSectionId = this.value; + if (!locationId || !mainSectionId) { + if (subsectionSelect) subsectionSelect.innerHTML = ''; + return; + } + if (subsectionSelect) { + subsectionSelect.innerHTML = ''; + } + fetch('{% url "organizations:ajax_subsections" %}?location_id=' + locationId + '&main_section_id=' + mainSectionId) + .then(r => r.json()) + .then(data => { + const subsections = data.subsections || []; + if (subsectionSelect) { + subsectionSelect.innerHTML = ''; + subsections.forEach(sub => { + const opt = document.createElement('option'); + opt.value = sub.internal_id || sub.id; + opt.textContent = {% if LANG == 'ar' %}sub.name_ar || sub.name{% else %}sub.name{% endif %}; + subsectionSelect.appendChild(opt); + }); + } + }) + .catch(err => { + console.error('Failed to load subsections:', err); + if (subsectionSelect) subsectionSelect.innerHTML = ''; + }); + }); + } + // Complaint type selection complaintTypeCards.forEach(card => { card.addEventListener('click', function() { @@ -807,6 +875,7 @@ document.addEventListener('DOMContentLoaded', function() { }); this.classList.add('active'); complaintTypeInput.value = this.dataset.value; + }); }); // Patient auto-lookup by national_id diff --git a/templates/complaints/oncall/admin_form.html b/templates/complaints/oncall/admin_form.html index ccb7b38..8e44d7c 100644 --- a/templates/complaints/oncall/admin_form.html +++ b/templates/complaints/oncall/admin_form.html @@ -138,22 +138,45 @@
{% if not available_admins %}

- {% trans "All PX Admins are already assigned to this schedule." %} + {% trans "All eligible users are already assigned to this schedule." %}

{% endif %}
diff --git a/templates/complaints/partials/ai_panel.html b/templates/complaints/partials/ai_panel.html index 7e7b03c..1f07559 100644 --- a/templates/complaints/partials/ai_panel.html +++ b/templates/complaints/partials/ai_panel.html @@ -5,14 +5,16 @@

{% trans "AI Analysis" %}

- {% if not complaint.emotion and not complaint.short_description_en and not complaint.suggested_actions %} {% if user.is_px_admin or user.is_hospital_admin %} {% endif %} - {% endif %}
{% if complaint.emotion %} @@ -118,12 +120,9 @@ {% if not complaint.emotion and not complaint.short_description_en and not complaint.suggested_actions %}
-

{% trans "No AI analysis available for this complaint" %}

+

{% trans "No AI analysis available for this complaint" %}

{% if user.is_px_admin or user.is_hospital_admin %} - +

{% trans "Click \"Analyze\" above to run AI analysis" %}

{% endif %}
{% endif %} @@ -189,7 +188,7 @@ function reanalyzeComplaintAI() { }) .then(data => { if (data.success) { - let html = '

{% trans "AI Analysis" %}

{% trans "Analysis complete" %}
'; + let html = '

{% trans "AI Analysis" %}

{% trans "Analysis complete" %}
'; if (data.emotion && data.emotion !== 'neutral') { html += '
'; diff --git a/templates/complaints/public_complaint_success.html b/templates/complaints/public_complaint_success.html index cfa1c9d..fcc1e09 100644 --- a/templates/complaints/public_complaint_success.html +++ b/templates/complaints/public_complaint_success.html @@ -59,7 +59,7 @@ {% endblock %} {% block content %} -
+
@@ -184,7 +184,7 @@
-

+

{% trans "Your feedback helps us improve our services" %}

diff --git a/templates/complaints/public_complaint_track.html b/templates/complaints/public_complaint_track.html index 49f63b1..182179f 100644 --- a/templates/complaints/public_complaint_track.html +++ b/templates/complaints/public_complaint_track.html @@ -9,11 +9,6 @@ header.glass-card { display: none !important; } -body.public-bg { - background: linear-gradient(to bottom right, #005696, #007bbd, #eef6fb) !important; - min-height: 100vh; -} - .lang-switcher { position: fixed; top: 1rem; @@ -166,21 +161,22 @@ body.public-bg {
- {{ complaint.status }} + {{ public_status.label }}
+ style="width: {{ public_status.progress }}%">
diff --git a/templates/config/dashboard.html b/templates/config/dashboard.html index 4a0f589..552fb62 100644 --- a/templates/config/dashboard.html +++ b/templates/config/dashboard.html @@ -39,7 +39,7 @@
-
+ {% comment %}
@@ -62,7 +62,7 @@ {% trans "Manage Thresholds" %} -
+
{% endcomment %}
@@ -80,7 +80,7 @@ -
+{% comment %}

{% trans "PX Actions" %} @@ -112,7 +112,7 @@

- + {% endcomment %}
@@ -127,8 +127,8 @@

{% trans "Users" %}

-

{% trans "Manage system users and permissions" %}

- +

{{ active_users_count }} {% trans "active users" %}

+
{% trans "Manage Users" %} @@ -170,7 +170,7 @@
-
+ {% comment %}
@@ -180,7 +180,7 @@ {% trans "Configure" %} -
+
{% endcomment %}
diff --git a/templates/config/emails/reset_password_email.html b/templates/config/emails/reset_password_email.html new file mode 100644 index 0000000..8bc553b --- /dev/null +++ b/templates/config/emails/reset_password_email.html @@ -0,0 +1,79 @@ +{% extends 'emails/base_email_template.html' %} +{% load i18n %} + +{% block title %}{% trans "Your PX360 Password Has Been Reset - Al Hammadi Hospital" %}{% endblock %} + +{% block preheader %}{% trans "Your password has been reset by an administrator. Find your new credentials below." %}{% endblock %} + +{% block hero_title %}{% trans "Password Reset" %}{% endblock %} + +{% block hero_subtitle %}{% trans "Your PX360 account password has been reset" %}{% endblock %} + +{% block content %} + + + + +
+

+ {% trans "Dear" %} {{ user.get_full_name }}, +

+

+ {% trans "Your PX360 account password has been reset by an administrator. Please use the new credentials below to login." %} +

+
+ + + + + +
+

+ {% trans "Your New Credentials" %} +

+ + + + + + + + + +
+ {% trans "Email:" %} + + {{ user.email }} +
+ {% trans "Password:" %} + + {{ password }} +
+
+ + + + + +
+

+ {% trans "Security Notice:" %} +

+

+ {% trans "Please change your password immediately after logging in for security purposes." %} +

+
+{% endblock %} + +{% block cta_url %}{{ login_url }}{% endblock %} +{% block cta_text %}{% trans "Login to PX360" %}{% endblock %} + +{% block info_title %}{% trans "Need Assistance?" %}{% endblock %} +{% block info_content %} +{% trans "If you did not expect this password reset, please contact your system administrator immediately." %} +{% endblock %} + +{% block footer_address %} +PX360 Patient Experience Management System
+Al Hammadi Hospital +{% endblock %} diff --git a/templates/config/hospital_users.html b/templates/config/hospital_users.html new file mode 100644 index 0000000..bb465c1 --- /dev/null +++ b/templates/config/hospital_users.html @@ -0,0 +1,509 @@ +{% extends "layouts/base.html" %} +{% load i18n %} + +{% block title %}{% trans "Hospital Users" %} - PX360{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+
+
+

{% trans "Hospital Users" %}

+

{% trans "Manage hospital user accounts and reset passwords" %}

+
+ + {% trans "Back to Config" %} + +
+
+ + +
+
+
+
+
+ +
+
+
+
{% trans "Total Users" %}
+

{{ total_users }}

+
+
+
+
+
+
+
+ +
+
+
+
{% trans "Active" %}
+

{{ active_count }}

+
+
+
+
+
+
+
+ +
+
+
+
{% trans "Inactive" %}
+

{{ inactive_count }}

+
+
+
+
+ + +
+
+
+ +
+
{% trans "Filters" %}
+
+
+
+ {% if user.is_px_admin %} +
+ + +
+ {% endif %} +
+ + +
+
+ + +
+
+ + +
+
+ + {% trans "Clear" %} +
+
+
+
+ + +
+
+
+
+ +
+
{% trans "User Accounts" %}
+
+
+ {% trans "Showing" %} {{ page_obj.start_index|default:0 }}-{{ page_obj.end_index|default:0 }} {% trans "of" %} {{ page_obj.paginator.count|default:0 }} +
+
+
+ + + + + + + + + + + + + + {% for u in users %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Name" %}{% trans "Email" %}{% trans "Hospital" %}{% trans "Department" %}{% trans "Role(s)" %}{% trans "Status" %}{% trans "Actions" %}
+
+
+ {{ u.first_name.0 }}{{ u.last_name.0 }} +
+
+
{{ u.get_full_name }}
+ {% if u.employee_id %} +
{{ u.employee_id }}
+ {% endif %} +
+
+
{{ u.email }} + {% if u.hospital %} + {{ u.hospital.name }} + {% else %} + - + {% endif %} + + {% if u.department %} + {{ u.department.get_localized_name }} + {% else %} + - + {% endif %} + + {% for role_name in u.get_role_names %} + {{ role_name }} + {% empty %} + - + {% endfor %} + + {% if u.is_active %} + {% trans "Active" %} + {% else %} + {% trans "Inactive" %} + {% endif %} + + +
+
+
+ +
+

{% trans "No users found" %}

+

{% trans "Try adjusting your filters" %}

+
+
+
+ + + {% if page_obj.has_other_pages %} +
+
+ + {% trans "Showing" %} {{ page_obj.start_index }}-{{ page_obj.end_index }} {% trans "of" %} {{ page_obj.paginator.count }} {% trans "entries" %} + +
+ {% for key, value in request.GET.items %} + {% if key != 'page_size' and key != 'page' %} + + {% endif %} + {% endfor %} + + +
+
+
+ {% if page_obj.has_previous %} + + + + {% else %} + + + + {% endif %} + + {% for num in page_obj.paginator.page_range %} + {% if num == page_obj.number %} + {{ num }} + {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %} + + {{ num }} + + {% elif num == 1 or num == page_obj.paginator.num_pages %} + + {{ num }} + + {% elif num == page_obj.number|add:'-3' or num == page_obj.number|add:'3' %} + ... + {% endif %} + {% endfor %} + + {% if page_obj.has_next %} + + + + {% else %} + + + + {% endif %} +
+
+ {% endif %} +
+
+ + + + + + + +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/templates/core/public_submit.html b/templates/core/public_submit.html index 5d035b1..160562b 100644 --- a/templates/core/public_submit.html +++ b/templates/core/public_submit.html @@ -10,12 +10,6 @@ header.glass-card { display: none !important; } -/* Match login page background */ -body.public-bg { - background: linear-gradient(to bottom right, #005696, #007bbd, #eef6fb) !important; - min-height: 100vh; -} - /* Language switcher styles */ .lang-switcher { position: fixed; diff --git a/templates/emails/base_email_template.html b/templates/emails/base_email_template.html index 130d16b..a7c91ff 100644 --- a/templates/emails/base_email_template.html +++ b/templates/emails/base_email_template.html @@ -12,198 +12,69 @@ {% block title %}Al Hammadi Hospital{% endblock %} - - {% block extra_styles %}{% endblock %} - - - + + +
{% block preheader %}Al Hammadi Hospital - Patient Experience Management{% endblock %}
- - + +
- - - - - + + + - - - - {% block hero %} + + - - {% endblock %} - - + + - - - - - {% block cta_section %} - - - - {% endblock %} - - - {% block info_box %} - - - - {% endblock %} - - - - - - - - - - + - -
- - + diff --git a/templates/emails/explanation_request.html b/templates/emails/explanation_request.html index e97a439..5add275 100644 --- a/templates/emails/explanation_request.html +++ b/templates/emails/explanation_request.html @@ -2,162 +2,79 @@ {% load i18n %} {% block title %}{% trans "Explanation Request - Al Hammadi Hospital" %}{% endblock %} - {% block preheader %}{% trans "You have been assigned to provide an explanation for a patient complaint" %}{% endblock %} -{% block hero_title %}{% trans "Explanation Request" %}{% endblock %} - -{% block hero_subtitle %}{% trans "Please review the complaint details and submit your response" %}{% endblock %} - {% block content %} - - - - - -
-

- {% trans "Dear" %} {{ staff_name }}, -

-

- {% trans "You have been assigned to provide an explanation for the following patient complaint. Please review the details and submit your response using the button below." %} -

-
+

{% trans "Explanation Request" %}

+ +

+ {% trans "Dear" %} {{ staff_name }}, +

+

+ {% trans "You have been assigned to provide an explanation for the following patient complaint. Please review the details and submit your response using the button below." %} +

{% if custom_message %} - - - - - -
-

- {% trans "Note from PX Team:" %} -

-

- {{ custom_message }} -

-
+
+

+ {% trans "Note from PX Team:" %} {{ custom_message }} +

+
{% endif %} - - - - - -
-

- {% trans "Complaint Details" %} -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if description %} - - - - - {% endif %} -
- {% trans "Reference:" %} - - #{{ complaint_id }} -
- {% trans "Title:" %} - - {{ complaint_title }} -
- {% trans "Patient:" %} - - {{ patient_name }} -
- {% trans "Hospital:" %} - - {{ hospital_name }} -
- {% trans "Department:" %} - - {{ department_name }} -
- {% trans "Category:" %} - - {{ category }} -
- {% trans "Status:" %} - - {{ status }} -
- {% trans "Date:" %} - - {{ created_date }} -
- {% trans "Description:" %} - - {{ description }} -
-
+ +
+ + + + + + + + + + + + + + + + + {% if description %} + + + + {% endif %} +
+ {% trans "Reference:" %} #{{ complaint_id }} +
+ {% trans "Title:" %} {{ complaint_title }} +
+ {% trans "Patient:" %} {{ patient_name }} +
+ {% trans "Department:" %} {{ department_name }} +
+ {% trans "Deadline:" %} {{ created_date }} +
+ {% trans "Description:" %}
+ {{ description }} +
+
- + -
-

- {% trans "Important Information:" %} -

-
    -
  • {% trans "This link is unique and can only be used once" %}
  • -
  • {% trans "You can attach supporting documents to your explanation" %}
  • -
  • {% trans "Your response will be reviewed by the PX team" %}
  • -
  • {% trans "Please submit your explanation at your earliest convenience" %}
  • -
+
+ + {% trans "Submit Your Explanation" %} +
-{% endblock %} -{% block cta_url %}{{ explanation_url }}{% endblock %} -{% block cta_text %}{% trans "Submit Your Explanation" %}{% endblock %} - -{% block info_title %}{% trans "Need Assistance?" %}{% endblock %} -{% block info_content %} -{% trans "If you have any questions or concerns, please contact the PX team directly." %}
-{% trans "Note:" %} {% trans "This is an automated email. Please do not reply directly to this message." %} -{% endblock %} - -{% block footer_address %} -PX360 Complaint Management System
-Al Hammadi Hospital +

+ {% trans "If you have any questions, please contact the PX team." %}
+ {% trans "This is an automated email. Please do not reply." %} +

{% endblock %} diff --git a/templates/emails/new_observation_notification.html b/templates/emails/new_observation_notification.html index d8e3225..0e5703b 100644 --- a/templates/emails/new_observation_notification.html +++ b/templates/emails/new_observation_notification.html @@ -1,154 +1,56 @@ {% extends 'emails/base_email_template.html' %} {% load i18n %} -{% block title %}{% trans "New Observation Notification - Al Hammadi Hospital" %}{% endblock %} - -{% block preheader %}{% trans "A new observation has been submitted and requires review." %}{% endblock %} - -{% block hero_title %}{% trans "New Observation Submitted" %}{% endblock %} - -{% block hero_subtitle %}{% trans "A new observation requires your review and triage" %}{% endblock %} +{% block title %}{% trans "New Observation Submitted - Al Hammadi Hospital" %}{% endblock %} +{% block preheader %}{% trans "A new observation requires your review and triage" %}{% endblock %} {% block content %} - - - - -
-

- {% trans "Dear" %} {{ admin_name|default:'Admin' }}, -

-

- {% trans "A new observation has been submitted and requires your review. Please assess the details below and take appropriate action." %} -

-
+

{% trans "New Observation Submitted" %}

- - - - -
- - - - - {% if observation.title %} - - - - {% endif %} - - - - - - - - - - {% if observation.hospital %} - - - - {% endif %} - - - - - - -
-

- {% trans "Tracking Code" %}: {{ observation.tracking_code }} -

-
-

- {% trans "Title" %}: {{ observation.title }} -

-
-

- {% trans "Category" %}: - {% if observation.category %}{{ observation.category.name_en }}{% else %}N/A{% endif %} -

-
-

- {% trans "Severity" %}: - - {{ observation.get_severity_display }} - -

-
-

- {% trans "Location" %}: - {{ observation.location_text|default:"N/A" }} -

-
-

- {% trans "Hospital" %}: {{ observation.hospital.name }} -

-
-

- {% trans "Reporter" %}: {{ observation.reporter_display }} -

-
-

- {% trans "Submitted" %}: - {{ observation.created_at|date:"F d, Y H:i" }} -

-
-
+

+ {% trans "Dear" %} {{ recipient_name|default:'Colleague' }}, +

+

+ {% trans "A new observation has been submitted and requires your review. Please review the details below." %} +

-{% if observation.description %} - - - - -
-

- {% trans "Description" %} -

-

- {{ observation.description|truncatechars:1000 }} -

-
-{% endif %} + +
+ + + + + {% if observation.title %} + + + + {% endif %} + + + + + + + {% if observation.assigned_department %} + + + + {% endif %} +
+ {% trans "Tracking Code:" %} {{ observation.tracking_code }} +
+ {% trans "Title:" %} {{ observation.title }} +
+ {% trans "Category:" %} + {% if observation.category %}{{ observation.category.name_en }}{% else %}N/A{% endif %} +
+ {% trans "Status:" %} {{ observation.get_status_display }} +
+ {% trans "Department:" %} {{ observation.assigned_department.name }} +
+
- - - - -
-

- {% trans "Please review this observation and assign it to the appropriate team member for further action." %} -

-
- - - - - -
-

- تم إرسال ملاحظة جديدة - {{ observation.tracking_code }} -

-

- تم إرسال ملاحظة جديدة وتتطلب مراجعتكم. يرجى الاطلاع على التفاصيل أدناه واتخاذ الإجراء المناسب.
- {% if observation.category %}التصنيف: {{ observation.category.name_ar }}{% endif %} -

-
-{% endblock %} - -{% block cta_url %}{{ observation_url }}{% endblock %} -{% block cta_text %}{% trans "View Observation" %}{% endblock %} - -{% block info_title %}{% trans "Notification Details" %}{% endblock %} -{% block info_content %} -{% trans "Type:" %} New Observation
-{% trans "Time:" %} {{ current_time }}
-{% trans "This is an automated notification from the PX 360 system." %} -{% endblock %} - -{% block footer_address %} -PX360 Observation Management System
-Al Hammadi Hospital +

+ {% trans "This is an automated notification. Please log in to PX360 for full details." %} +

{% endblock %} diff --git a/templates/emails/simple_email.html b/templates/emails/simple_email.html new file mode 100644 index 0000000..7d486e8 --- /dev/null +++ b/templates/emails/simple_email.html @@ -0,0 +1,8 @@ +{% extends 'emails/base_email_template.html' %} +{% load i18n %} + +{% block title %}{{ subject|default:"Al Hammadi Hospital" }}{% endblock %} + +{% block content %} +{{ content_html|safe }} +{% endblock %} diff --git a/templates/emails/survey_invitation.html b/templates/emails/survey_invitation.html index 925660c..26f8133 100644 --- a/templates/emails/survey_invitation.html +++ b/templates/emails/survey_invitation.html @@ -2,94 +2,42 @@ {% load i18n %} {% block title %}{% trans "Patient Survey Invitation - Al Hammadi Hospital" %}{% endblock %} - {% block preheader %}{% trans "We value your feedback! Please share your experience with us." %}{% endblock %} -{% block hero_title %}{% trans "We Value Your Feedback" %}{% endblock %} - -{% block hero_subtitle %}{% trans "Help us improve our services by sharing your recent experience at Al Hammadi Hospital" %}{% endblock %} - {% block content %} +

{% trans "We Value Your Feedback" %}

+ +

+ {% trans "Dear" %} {{ patient_name|default:_("Valued Patient") }}, +

+

+ {% blocktrans with visit=visit_date|default:_("your recent visit") %}Thank you for choosing Al Hammadi Hospital for your healthcare needs. We hope your recent visit on {{ visit }} met your expectations.{% endblocktrans %} +

+

+ {% trans "We would greatly appreciate it if you could take a few minutes to complete our satisfaction survey. Your feedback helps us improve our services." %} +

+ + +
+

+ ⏱ {% trans "Takes only 3-5 minutes" %}
+ 🔒 {% trans "Your responses are confidential" %} +

+
+ + -
-

- {% trans "Dear" %} {{ patient_name|default:_("Valued Patient") }}, -

-

- {% blocktrans with visit=visit_date|default:_("your recent visit") %}Thank you for choosing Al Hammadi Hospital for your healthcare needs. We hope your recent visit on {{ visit }} met your expectations.{% endblocktrans %} -

-

- {% blocktrans with duration=survey_duration|default:_("3-5") %}Your feedback is invaluable in helping us maintain and improve the quality of care we provide. Would you mind taking {{ duration }} minutes to complete our patient experience survey?{% endblocktrans %} -

+
+ + {% trans "Take Survey" %} +
- - - - - - - -
-

- {% trans "Why Your Feedback Matters:" %} -

-
- - - - - -
- - -

- {% trans "Improve Patient Care:" %} {% trans "Your insights help us enhance our services" %} -

-
- - - - - - -
- - -

- {% trans "Better Experience:" %} {% trans "Help us create a better experience for all patients" %} -

-
- - - - - - -
- - -

- {% trans "Quality Standards:" %} {% trans "Contribute to our commitment to excellence" %} -

-
-
-{% endblock %} - -{% block cta_url %}{{ survey_link|default:'#' }}{% endblock %} -{% block cta_text %}{% trans "Start Survey" %}{% endblock %} - -{% block info_title %}{% trans "Survey Information" %}{% endblock %} -{% block info_content %} -{% trans "Duration:" %} {% trans "Approximately" %} {{ survey_duration|default:_("3-5") }} {% trans "minutes" %}
-{% trans "Confidentiality:" %} {% trans "Your responses are completely confidential" %}
-{% trans "Deadline:" %} {% trans "Please complete by" %} {{ deadline|default:_("the end of this week") }} -{% endblock %} - -{% block footer_address %} -{% trans "Patient Experience Management Department" %}
-{% trans "Al Hammadi Hospital" %} +

+ {% trans "Thank you for your time and feedback." %} +

{% endblock %} diff --git a/templates/layouts/partials/sidebar.html b/templates/layouts/partials/sidebar.html index d326570..194dbfe 100644 --- a/templates/layouts/partials/sidebar.html +++ b/templates/layouts/partials/sidebar.html @@ -353,6 +353,11 @@ {% trans "Leaderboard" %} {% if user.is_px_admin or user.is_hospital_admin %} + + + {% trans "Fetch Ratings" %} + diff --git a/templates/layouts/public_base.html b/templates/layouts/public_base.html index 6596399..d774698 100644 --- a/templates/layouts/public_base.html +++ b/templates/layouts/public_base.html @@ -55,7 +55,7 @@ /* Background Pattern */ .public-bg { - background: linear-gradient(135deg, #005696 0%, #007bbd 50%, #00a8e8 100%); + background: linear-gradient(135deg, #007bbd 0%, #005696 100%); min-height: 100vh; position: relative; overflow-x: hidden; @@ -63,15 +63,28 @@ .public-bg::before { content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: - radial-gradient(circle at 20% 50%, rgba(255,255,255,0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 80%, rgba(255,255,255,0.1) 0%, transparent 50%); + position: fixed; + top: -100px; + right: -100px; + width: 200px; + height: 200px; + background: rgba(255,255,255,0.05); + border-radius: 9999px; pointer-events: none; + z-index: 0; + } + + .public-bg::after { + content: ''; + position: fixed; + bottom: -75px; + left: -75px; + width: 150px; + height: 150px; + background: rgba(255,255,255,0.05); + border-radius: 9999px; + pointer-events: none; + z-index: 0; } /* Glass morphism effect */ @@ -206,16 +219,16 @@ -