diff --git a/.gitignore b/.gitignore index f765b7b..5617eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,6 @@ htmlcov/ # Media and Static files (if served locally and not meant for version control) media/ -static/ # Deployment files *.tar.gz diff --git a/recruitment/__pycache__/forms.cpython-313.pyc b/recruitment/__pycache__/forms.cpython-313.pyc index 4aa8345..55879c6 100644 Binary files a/recruitment/__pycache__/forms.cpython-313.pyc and b/recruitment/__pycache__/forms.cpython-313.pyc differ diff --git a/recruitment/forms.py b/recruitment/forms.py index 1a6ba6a..3929bed 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -728,7 +728,7 @@ class InterviewScheduleForm(forms.ModelForm): ) class Meta: - model = InterviewSchedule + model = InterviewSchedule fields = [ 'schedule_interview_type', "applications", @@ -1544,8 +1544,7 @@ class CandidateEmailForm(forms.Form): label=_('Select Candidates'), # Use a descriptive label required=False ) - - + subject = forms.CharField( max_length=200, widget=forms.TextInput(attrs={ @@ -1568,29 +1567,29 @@ class CandidateEmailForm(forms.Form): required=True ) - + def __init__(self, job, candidates, *args, **kwargs): super().__init__(*args, **kwargs) self.job = job self.candidates=candidates - + candidate_choices=[] for candidate in candidates: candidate_choices.append( (f'candidate_{candidate.id}', f'{candidate.email}') ) - + self.fields['to'].choices =candidate_choices - self.fields['to'].initial = [choice[0] for choice in candidate_choices] - - + self.fields['to'].initial = [choice[0] for choice in candidate_choices] + + # Set initial message with candidate and meeting info initial_message = self._get_initial_message() - + if initial_message: self.fields['message'].initial = initial_message @@ -1598,7 +1597,7 @@ class CandidateEmailForm(forms.Form): """Generate initial message with candidate and meeting information""" candidate=self.candidates.first() message_parts=[] - + if candidate and candidate.stage == 'Applied': message_parts = [ f"Than you, for your interest in the {self.job.title} role.", @@ -1618,7 +1617,7 @@ class CandidateEmailForm(forms.Form): f"We look forward to reviewing your results.", f"Best regards, The KAAUH Hiring team" ] - + elif candidate and candidate.stage == 'Interview': message_parts = [ f"Than you, for your interest in the {self.job.title} role.", @@ -1629,7 +1628,7 @@ class CandidateEmailForm(forms.Form): f"We look forward to reviewing your results.", f"Best regards, The KAAUH Hiring team" ] - + elif candidate and candidate.stage == 'Offer': message_parts = [ f"Congratulations, ! We are delighted to inform you that we are extending a formal offer of employment for the {self.job.title} role.", @@ -1648,9 +1647,9 @@ class CandidateEmailForm(forms.Form): f"If you have any questions before your start date, please contact [Onboarding Contact].", f"Best regards, The KAAUH Hiring team" ] - - - + + + # # Add candidate information # if self.candidate: @@ -1675,9 +1674,9 @@ class CandidateEmailForm(forms.Form): def get_email_addresses(self): """Extract email addresses from selected recipients""" email_addresses = [] - + candidates=self.cleaned_data.get('to',[]) - + if candidates: for candidate in candidates: if candidate.startswith('candidate_'): @@ -1691,7 +1690,7 @@ class CandidateEmailForm(forms.Form): return list(set(email_addresses)) # Remove duplicates - + def get_formatted_message(self): """Get the formatted message with optional additional information""" @@ -1871,7 +1870,7 @@ class InterviewEmailForm(forms.Form): location = meeting.interview_location # --- Data Preparation --- - + # Safely access details through the related InterviewLocation object if location and location.start_time: formatted_date = location.start_time.strftime('%Y-%m-%d') @@ -1884,7 +1883,7 @@ class InterviewEmailForm(forms.Form): formatted_time = "TBD" duration = "N/A" meeting_link = "Not Available" - + job_title = job.title agency_name = candidate.hiring_agency.name if candidate.belong_to_an_agency and candidate.hiring_agency else "Hiring Agency" @@ -1917,7 +1916,7 @@ Best regards, KAAUH Hiring Team """ # ... (Messages for agency and participants remain the same, using the updated safe variables) - + # --- 2. Agency Message (Professional and clear details) --- agency_message = f""" Dear {agency_name}, @@ -1969,44 +1968,44 @@ class OnsiteLocationForm(forms.ModelForm): class Meta: model = OnsiteLocationDetails # Include 'room_number' and update the field list - fields = ['topic', 'physical_address', 'room_number'] + fields = ['topic', 'physical_address', 'room_number'] widgets = { 'topic': forms.TextInput( attrs={'placeholder': 'Enter the Meeting Topic', 'class': 'form-control'} ), - + 'physical_address': forms.TextInput( attrs={'placeholder': 'Physical address (e.g., street address)', 'class': 'form-control'} ), - + 'room_number': forms.TextInput( attrs={'placeholder': 'Room Number/Name (Optional)', 'class': 'form-control'} ), - - + + } class OnsiteReshuduleForm(forms.ModelForm): class Meta: model = OnsiteLocationDetails - fields = ['topic', 'physical_address', 'room_number','start_time','duration','status'] + fields = ['topic', 'physical_address', 'room_number','start_time','duration','status'] widgets = { 'topic': forms.TextInput( attrs={'placeholder': 'Enter the Meeting Topic', 'class': 'form-control'} ), - + 'physical_address': forms.TextInput( attrs={'placeholder': 'Physical address (e.g., street address)', 'class': 'form-control'} ), - + 'room_number': forms.TextInput( attrs={'placeholder': 'Room Number/Name (Optional)', 'class': 'form-control'} ), - - + + } - + class OnsiteScheduleForm(forms.ModelForm): # Add fields for the foreign keys required by ScheduledInterview @@ -2024,8 +2023,8 @@ class OnsiteScheduleForm(forms.ModelForm): class Meta: model = OnsiteLocationDetails # Include all fields from OnsiteLocationDetails plus the new ones - fields = ['topic', 'physical_address', 'room_number', 'start_time', 'duration', 'status', 'application', 'job'] - + fields = ['topic', 'physical_address', 'room_number', 'start_time', 'duration', 'status', 'application', 'job'] + widgets = { 'topic': forms.TextInput( attrs={'placeholder': _('Enter the Meeting Topic'), 'class': 'form-control'} @@ -2036,7 +2035,7 @@ class OnsiteScheduleForm(forms.ModelForm): 'room_number': forms.TextInput( attrs={'placeholder': _('Room Number/Name (Optional)'), 'class': 'form-control'} ), - # You should explicitly set widgets for start_time, duration, and status here + # You should explicitly set widgets for start_time, duration, and status here # if they need Bootstrap classes, otherwise they will use default HTML inputs. # Example: 'start_time': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}), diff --git a/static/css/main.css b/static/css/main.css new file mode 100644 index 0000000..e53a1d0 --- /dev/null +++ b/static/css/main.css @@ -0,0 +1,721 @@ +/* + * KAAT-S Theme Styles (V2.0 - Consolidated Global, Nav, and Components) + * This file contains all variables, global layout styles, navigation, and component-specific styles. + */ + +/* ---------------------------------- */ +/* 1. UI Variables and Global Reset */ +/* ---------------------------------- */ +:root { + --kaauh-teal: #00636e; + --kaauh-teal-dark: #004a53; + --kaauh-light-bg: #f9fbfd; + --kaauh-border: #eaeff3; + --kaauh-primary-text: #343a40; + --kaauh-success: #28a745; + --kaauh-info: #17a2b8; + --kaauh-danger: #dc3545; + --kaauh-warning: #ffc107; +} + +/* Primary Color Overrides */ +.text-success { color: var(--kaauh-success) !important; } +.text-info { color: var(--kaauh-info) !important; } +.text-danger { color: var(--kaauh-danger) !important; } +.text-warning { color: var(--kaauh-warning) !important; } +.text-primary-theme { color: var(--kaauh-teal) !important; } +.bg-primary-theme { background-color: var(--kaauh-teal) !important; } + +/* Global Layout Control */ +.max-width-1600 { + max-width: 1600px; + margin-right: auto; + margin-left: auto; + padding-right: var(--bs-gutter-x, 0.75rem); + padding-left: var(--bs-gutter-x, 0.75rem); +} + +/* Global Container Padding for main content */ +.container-fluid.py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } + +/* Main content minimum height */ +main.container-fluid { + min-height: calc(100vh - 200px); + padding: 1.5rem 0; +} + +/* ---------------------------------- */ +/* 2. Navigation and Header */ +/* ---------------------------------- */ + +/* Top Bar (Contact/Social) */ +.top-bar { + background-color: white; + border-bottom: 1px solid var(--kaauh-border); + font-size: 0.825rem; + padding: 0.4rem 0; +} +.top-bar a { text-decoration: none; } +.top-bar .social-icons i { + color: var(--kaauh-teal); + transition: color 0.2s; +} +.top-bar .social-icons i:hover { + color: var(--kaauh-teal-dark); +} +.top-bar .contact-item { + display: flex; + align-items: center; + gap: 0.35rem; + padding: 0.25rem 0.5rem; +} +.top-bar .logo-container img { + height: 60px; + object-fit: contain; +} +@media (max-width: 767.98px) { + .top-bar { + display: none; + } +} + +/* Navbar */ +.navbar-brand { + font-weight: 700; + letter-spacing: -0.5px; + font-size: 1.25rem; + display: flex; + align-items: center; + gap: 0.5rem; +} +.navbar-dark { + background-color: var(--kaauh-teal) !important; + box-shadow: 0 2px 6px rgba(0,0,0,0.12); +} +/* Ensure the inner container of the navbar stretches to allow max-width-1600 */ +.navbar-dark > .container { + max-width: 100%; +} +.nav-link { + font-weight: 500; + transition: all 0.2s ease; + padding: 0.5rem 0.75rem; +} +.nav-link:hover, +.nav-link.active { + color: white !important; + background: rgba(255,255,255,0.12) !important; + border-radius: 4px; +} + +/* Dropdown */ +.dropdown-menu { + backdrop-filter: blur(4px); + background-color: rgba(255, 255, 255, 0.98); + border: 1px solid var(--kaauh-border); + box-shadow: 0 6px 20px rgba(0,0,0,0.12); + border-radius: 8px; + padding: 0.5rem 0; + min-width: 200px; + will-change: transform, opacity; + transition: transform 0.2s ease, opacity 0.2s ease; +} +.dropdown-item { + padding: 0.5rem 1.25rem; + transition: background-color 0.15s; +} +.dropdown-item:hover { + background-color: var(--kaauh-light-bg); + color: var(--kaauh-teal-dark); +} + +/* Language Toggle Button Style */ +.language-toggle-btn { + color: white !important; + background: none !important; + border: none !important; + display: flex; + align-items: center; + gap: 0.3rem; + padding: 0.5rem 0.75rem !important; + font-weight: 500; + transition: all 0.2s ease; +} +.language-toggle-btn:hover { + background: rgba(255,255,255,0.12) !important; + border-radius: 4px; +} + +/* Profile Avatar */ +.profile-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + background: var(--kaauh-teal); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: bold; + font-size: 0.85rem; + transition: transform 0.1s ease; +} +.navbar-nav .dropdown-toggle:hover .profile-avatar { + transform: scale(1.05); +} +.navbar-nav .dropdown-toggle.p-0:hover { + background: none !important; +} + + +/* ---------------------------------- */ +/* 3. Component Styles (Cards & Forms)*/ +/* ---------------------------------- */ +.kaauh-card { + border: 1px solid var(--kaauh-border); + border-radius: 0.75rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); + background-color: white; +} + +/* NEW: Filter Controls Container Style */ +.filter-controls { + border: 1px solid var(--kaauh-border); + border-radius: 0.75rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); + background-color: white; + padding: 1.5rem; /* Consistent internal padding */ + margin-bottom: 1.5rem; /* Space below filter */ +} + +/* Typography & Headers */ +.page-header { + color: var(--kaauh-teal-dark); + font-weight: 700; +} +.section-header { + color: var(--kaauh-primary-text); + font-weight: 600; + border-bottom: 2px solid var(--kaauh-border); + padding-bottom: 0.5rem; + margin-bottom: 1rem; +} +label { + font-weight: 500; + color: var(--kaauh-primary-text); + font-size: 0.9rem; + margin-bottom: 0.25rem; +} + +/* Forms - Default Size */ +.form-control, .form-select { + border-radius: 0.5rem; + border: 1px solid #ced4da; + padding: 0.5rem 0.75rem; + font-size: 0.9rem; +} + +/* Forms - Compact Size (for modals/tables) */ +.form-control-sm, +.form-select-sm, +.btn-sm { + padding: 0.25rem 0.5rem !important; /* Adjusted padding */ + font-size: 0.8rem !important; + line-height: 1.25 !important; /* Standard small line height */ + height: auto !important; +} + +.form-select-sm { + padding: 0.25rem 2rem 0.25rem 0.5rem !important; /* Increased right padding for arrow */ + font-size: 0.8rem !important; + line-height: 1.25 !important; + height: auto !important; /* Remove fixed height */ + border-radius: 0.5rem; + border: 1px solid #ced4da; +} + +/* Scrollable Multiple Select Fix */ +.form-group select[multiple] { + max-height: 450px; + overflow-y: auto; + min-height: 250px; + padding: 0; +} + +/* Break Times Section Styling (Schedule Interviews) */ +.break-time-form { + background-color: #f8f9fa; + padding: 0.75rem; + border-radius: 0.5rem; + border: 1px solid var(--kaauh-border); + align-items: flex-end; +} +.note-box { + background-color: #fff3cd; + border-left: 5px solid var(--kaauh-warning); + padding: 1rem; + border-radius: 0.25rem; + font-size: 0.9rem; + margin-bottom: 1rem; +} + +/* Tier Controls (Kept for consistency/future use) */ +.tier-controls { + background-color: var(--kaauh-border); + border-radius: 0.75rem; + padding: 1.25rem; + margin-bottom: 2rem; + border: 1px solid var(--kaauh-border); +} +.tier-controls .form-row { + display: flex; + align-items: end; + gap: 1rem; +} +.tier-controls .form-group { + flex: 1; + margin-bottom: 0; +} + + +/* ---------------------------------- */ +/* 4. Button Styles (Component Themed)*/ +/* ---------------------------------- */ +.btn-main-action { + background-color: var(--kaauh-teal); + border-color: var(--kaauh-teal); + color: white; + font-weight: 600; + transition: all 0.2s ease; +} +.btn-main-action:hover { + background-color: var(--kaauh-teal-dark); + border-color: var(--kaauh-teal-dark); + box-shadow: 0 4px 8px rgba(0,0,0,0.15); +} +.btn-main-action.btn-sm { font-weight: 600 !important; } + +.btn-outline-secondary { + color: var(--kaauh-teal-dark); + border-color: var(--kaauh-teal); +} +.btn-outline-secondary:hover { + background-color: var(--kaauh-teal-dark); + color: white; + border-color: var(--kaauh-teal-dark); +} + +.btn-bulk-pass { + background-color: var(--kaauh-success); + border-color: var(--kaauh-success); + color: white; + font-weight: 500; +} +.btn-bulk-pass:hover { + background-color: #1e7e34; + border-color: #1e7e34; +} + +.btn-bulk-fail { + background-color: var(--kaauh-danger); + border-color: var(--kaauh-danger); + color: white; + font-weight: 500; +} +.btn-bulk-fail:hover { + background-color: #bd2130; + border-color: #bd2130; +} + +.btn-apply { /* From Job Board table */ + background: var(--kaauh-teal); + border: none; + color: white; + padding: 0.45rem 1rem; + font-weight: 600; + border-radius: 6px; + transition: all 0.2s; + white-space: nowrap; +} +.btn-apply:hover { + background: var(--kaauh-teal-dark); + transform: translateY(-1px); + box-shadow: 0 2px 6px rgba(0,0,0,0.15); +} + +/* ---------------------------------- */ +/* 5. Table & Footer Styles */ +/* ---------------------------------- */ +.candidate-table { + table-layout: fixed; + width: 100%; + border-collapse: separate; + border-spacing: 0; + background-color: white; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); +} +.candidate-table thead { + background-color: var(--kaauh-border); +} +.candidate-table th { + padding: 0.75rem; + font-weight: 600; + color: var(--kaauh-teal-dark); + border-bottom: 2px solid var(--kaauh-teal); + font-size: 0.9rem; + vertical-align: middle; +} +.candidate-table td { + padding: 0.75rem; + border-bottom: 1px solid var(--kaauh-border); + vertical-align: middle; + font-size: 0.9rem; +} +.candidate-table tbody tr:hover { + background-color: #f1f3f4; +} +.candidate-name { + font-weight: 600; + color: var(--kaauh-primary-text); +} +.candidate-details { + font-size: 0.8rem; + color: #6c757d; +} + +/* Job Table Specific Styles */ +.job-table-wrapper { + background: white; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 4px 16px rgba(0,0,0,0.06); + margin-bottom: 2rem; +} +.job-table thead th { + background: var(--kaauh-teal); + color: white; + font-weight: 600; + padding: 1rem; + text-align: center; +} +.job-table td { + padding: 1rem; + vertical-align: middle; + text-align: center; +} +.job-table tr:hover td { + background-color: rgba(0, 99, 110, 0.03); +} + +/* Table Responsiveness */ +@media (max-width: 575.98px) { + .table-responsive { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .job-table th, + .job-table td { + white-space: nowrap; + font-size: 0.875rem; + } +} + +/* Bulk Action Bar (Interview Management) */ +.bulk-action-bar { + display: flex; + align-items: center; + gap: 0.5rem; + padding-bottom: 0.75rem; + margin-bottom: 1rem; + border-bottom: 1px solid var(--kaauh-border); +} +.action-group { + display: flex; + align-items: center; + gap: 0.5rem; +} + +/* Badges (Adapted for Interview Tiers/Status) */ +.ai-score-badge { /* Used as an all-purpose secondary badge */ + background-color: var(--kaauh-teal-dark) !important; + color: white; + font-weight: 700; + padding: 0.4em 0.8em; + border-radius: 0.4rem; + font-size: 0.8rem; +} +.tier-badge { /* Used for Tier labels */ + font-size: 0.75rem; + padding: 0.125rem 0.5rem; + border-radius: 0.5rem; + font-weight: 600; + margin-left: 0.5rem; + display: inline-block; +} +.tier-1-badge { background-color: var(--kaauh-success); color: white; } +.tier-2-badge { background-color: var(--kaauh-warning); color: #856404; } +.tier-3-badge { background-color: #d1ecf1; color: #0c5460; } +.cd_interview{ color: #00636e; } + +/* NEW: Application Stage Badges */ +.stage-badge { + font-size: 0.8rem; + padding: 0.2em 0.6em; + border-radius: 0.4rem; + font-weight: 600; + display: inline-block; + margin-top: 0.25rem; +} +.stage-Application { background-color: #e9ecef; color: #495057; } +.stage-Screening { background-color: #d1ecf1; color: #0c5460; } +.stage-Exam { background-color: #cce5ff; color: #004085; } +.stage-Interview { background-color: #fff3cd; color: #856404; } +.stage-Offer { background-color: #d4edda; color: #155724; } + +/* NEW: Applicant Status Badges */ +.status-badge { + font-size: 0.8rem; + padding: 0.2em 0.6em; + border-radius: 0.4rem; + font-weight: 500; + display: inline-block; +} +.bg-candidate { background-color: var(--kaauh-teal-dark); color: white; } +.bg-applicant { background-color: #f8f9fa; color: #495057; border: 1px solid #ced4da; } + +/* Table Column Width Fixes */ +.candidate-table th:nth-child(1) { width: 40px; } +.candidate-table th:nth-child(4) { width: 15%; } +.candidate-table th:nth-child(5) { width: 80px; } +.candidate-table th:nth-child(6) { width: 180px; } + +/* Footer & Alerts */ +.footer { + background: var(--kaauh-light-bg); + padding: 1.5rem 0; + border-top: 1px solid var(--kaauh-border); + font-size: 0.9rem; + color: #555; +} +.alert { + border: none; + border-radius: 8px; + box-shadow: 0 2px 6px rgba(0,0,0,0.05); +} + +/* ---------------------------------- */ +/* 6. RTL Support */ +/* ---------------------------------- */ +html[dir="rtl"] { + text-align: right; + direction: rtl; +} +html[dir="rtl"] .navbar-brand { + flex-direction: row-reverse; +} +html[dir="rtl"] .dropdown-menu { + right: auto; + left: 0; +} +/* RTL Spacing adjustments */ +html[dir="rtl"] .me-3 { margin-right: 0 !important; margin-left: 1rem !important; } +html[dir="rtl"] .ms-3 { margin-left: 0 !important; margin-right: 1rem !important; } +html[dir="rtl"] .me-2 { margin-right: 0 !important; margin-left: 0.5rem !important; } +html[dir="rtl"] .ms-2 { margin-left: 0 !important; margin-right: 0.5rem !important; } +html[dir="rtl"] .ms-auto { margin-left: 0 !important; margin-right: auto !important; } +html[dir="rtl"] .me-auto { margin-right: 0 !important; margin-left: auto !important; } + + +/* ================================================= */ +/* 1. THEME VARIABLES AND GLOBAL STYLES */ +/* ================================================= */ +:root { + --kaauh-teal: #00636e; + --kaauh-teal-dark: #004a53; + --kaauh-border: #eaeff3; + --kaauh-primary-text: #343a40; +} + +/* Primary Color Overrides */ +.text-primary { color: var(--kaauh-teal) !important; } +.text-info { color: #17a2b8 !important; } +.text-success { color: #28a745 !important; } +.text-secondary { color: #6c757d !important; } +.text-kaauh-primary { color: var(--kaauh-primary-text); } /* Custom class for primary text color if needed */ + + +/* ---------------------------------- */ +/* 2. Button Styles */ +/* ---------------------------------- */ + +/* Main Action Button Style (Used for Download Resume) */ +.btn-main-action { + background-color: var(--kaauh-teal); + border-color: var(--kaauh-teal); + color: white; + font-weight: 600; + padding: 0.6rem 1.2rem; + transition: all 0.2s ease; + display: inline-flex; + align-items: center; + gap: 0.5rem; +} +.btn-main-action:hover { + background-color: var(--kaauh-teal-dark); + border-color: var(--kaauh-teal-dark); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0,0,0,0.15); +} + +/* Outlined Button Styles */ +.btn-outline-primary { + color: var(--kaauh-teal); + border-color: var(--kaauh-teal); +} +.btn-outline-primary:hover { + background-color: var(--kaauh-teal); + color: white; +} +.btn-outline-secondary { + color: var(--kaauh-teal-dark); + border-color: var(--kaauh-teal); +} +.btn-outline-secondary:hover { + background-color: var(--kaauh-teal-dark); + color: white; + border-color: var(--kaauh-teal-dark); +} + +/* ---------------------------------- */ +/* 3. Card/Modal Styles */ +/* ---------------------------------- */ + +/* Card enhancements */ +.kaauh-card, .card { + border: 1px solid var(--kaauh-border); + border-radius: 0.75rem; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); + background-color: white; +} + +/* Candidate Header Card (The teal header) */ +.candidate-header-card { + background: linear-gradient(135deg, var(--kaauh-teal), #004d57); + color: white; + border-radius: 0.75rem 0.75rem 0 0; + padding: 1.5rem; + box-shadow: 0 4px 10px rgba(0,0,0,0.15); +} +.candidate-header-card h1 { + font-weight: 700; + margin: 0; + font-size: 1.8rem; +} +.candidate-header-card .badge { + font-size: 0.9rem; + padding: 0.4em 0.8em; + border-radius: 0.4rem; + font-weight: 700; +} + +/* ---------------------------------- */ +/* 4. Tab Navigation Styles (Candidate Detail View) */ +/* ---------------------------------- */ + +/* Left Column Tabs (Main Content Tabs) */ +.main-tabs { + border-bottom: 1px solid var(--kaauh-border); + background-color: #f8f9fa; + padding: 0 1.25rem; +} +.main-tabs .nav-link { + border: none; + border-bottom: 3px solid transparent; + color: var(--kaauh-primary-text); + font-weight: 500; + padding: 0.75rem 1rem; + margin-right: 0.5rem; + transition: all 0.2s; +} +.main-tabs .nav-link:hover { + color: var(--kaauh-teal); +} +.main-tabs .nav-link.active { + color: var(--kaauh-teal-dark) !important; + background-color: white !important; + border-bottom: 3px solid var(--kaauh-teal); + font-weight: 600; +} + +/* Right Column Card (General styling for tab content if needed) */ +.right-column-card .tab-content { + padding: 1.5rem 1.25rem; + background-color: white; +} + +/* ---------------------------------- */ +/* 5. Vertical Timeline Styling */ +/* ---------------------------------- */ + +/* Highlight box for the current stage */ +.current-stage { + border: 1px solid var(--kaauh-border); + background-color: #f0f8ff; /* Light, subtle blue background */ +} +.current-stage .text-primary { + color: var(--kaauh-teal) !important; +} + +.timeline { + position: relative; + padding-left: 2rem; +} +.timeline::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 1.25rem; + width: 2px; + background-color: var(--kaauh-border); +} +.timeline-item { + position: relative; + margin-bottom: 2rem; + padding-left: 1.5rem; +} +.timeline-icon { + position: absolute; + left: 0; + top: 0; + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 0.8rem; + z-index: 10; + border: 4px solid white; +} +.timeline-item:last-child { + margin-bottom: 0; +} + +/* Custom Timeline Background Classes for Stages (Using Bootstrap color palette) */ +.timeline-bg-applied { background-color: var(--kaauh-teal) !important; } +.timeline-bg-exam { background-color: #17a2b8 !important; } +.timeline-bg-interview { background-color: #ffc107 !important; } +.timeline-bg-offer { background-color: #28a745 !important; } +.timeline-bg-rejected { background-color: #dc3545 !important; } +.loading { + background: linear-gradient(135deg, var(--kaauh-teal), #004d57); + color: white; + border-radius: 0.75rem 0.75rem 0 0; + padding: 1.5rem; + box-shadow: 0 4px 10px rgba(0,0,0,0.15); +} diff --git a/static/css/messages.css b/static/css/messages.css new file mode 100644 index 0000000..d47ff89 --- /dev/null +++ b/static/css/messages.css @@ -0,0 +1,786 @@ +/* Unified Messaging System Styles */ + +:root { + --primary-color: #00636e; + --secondary-color: #f8f9fa; + --light-bg: #f8f9fa; + --border-color: #dee2e6; + --text-color: #333; + --text-muted: #6c757d; + --success-color: #28a745; + --warning-color: #ffc107; + --danger-color: #dc3545; + --info-color: #17a2b8; +} + +/* Main Layout */ +.unified-messages { + display: flex; + height: calc(100vh - 120px); + background: white; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); +} + +/* Sidebar */ +.messages-sidebar { + width: 350px; + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + background: var(--light-bg); +} + +.sidebar-header { + padding: 1rem; + border-bottom: 1px solid var(--border-color); + background: white; +} + +.sidebar-tabs { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.tab-btn { + padding: 0.5rem 1rem; + border: none; + background: transparent; + color: var(--text-muted); + border-radius: 0.5rem; + cursor: pointer; + transition: all 0.2s ease; + font-size: 0.875rem; + font-weight: 500; +} + +.tab-btn.active { + background: var(--primary-color); + color: white; +} + +.tab-btn:hover:not(.active) { + background: var(--border-color); +} + +.search-box { + position: relative; +} + +.search-input { + width: 100%; + padding: 0.75rem 1rem 0.75rem 2.5rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + font-size: 0.875rem; + transition: border-color 0.2s ease; +} + +.search-input:focus { + outline: none; + border-color: var(--primary-color); +} + +.search-icon { + position: absolute; + left: 0.75rem; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); +} + +.compose-btn { + width: 100%; + padding: 0.75rem; + background: var(--primary-color); + color: white; + border: none; + border-radius: 0.5rem; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.compose-btn:hover { + background: #004d57; +} + +/* Conversation List */ +.conversation-list { + flex: 1; + overflow-y: auto; + padding: 0.5rem; +} + +.conversation-item { + display: flex; + align-items: center; + padding: 0.75rem; + border-radius: 0.5rem; + cursor: pointer; + transition: background-color 0.2s ease; + margin-bottom: 0.25rem; + border: 1px solid transparent; +} + +.conversation-item:hover { + background: white; + border-color: var(--border-color); +} + +.conversation-item.active { + background: white; + border-color: var(--primary-color); + box-shadow: 0 2px 8px rgba(0, 99, 110, 0.1); +} + +.conversation-avatar { + width: 48px; + height: 48px; + border-radius: 50%; + background: var(--primary-color); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 1rem; + margin-right: 0.75rem; + flex-shrink: 0; + position: relative; +} + +.online-indicator { + position: absolute; + bottom: 2px; + right: 2px; + width: 12px; + height: 12px; + background: var(--success-color); + border: 2px solid white; + border-radius: 50%; +} + +.conversation-info { + flex: 1; + min-width: 0; +} + +.conversation-name { + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.25rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.conversation-preview { + font-size: 0.875rem; + color: var(--text-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.conversation-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.25rem; +} + +.conversation-time { + font-size: 0.75rem; + color: var(--text-muted); +} + +.unread-badge { + background: var(--primary-color); + color: white; + border-radius: 50%; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + font-weight: 600; +} + +/* Conversation Detail */ +.conversation-detail { + flex: 1; + display: flex; + flex-direction: column; +} + +.conversation-detail-header { + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border-color); + background: white; + display: flex; + align-items: center; + justify-content: space-between; +} + +.conversation-detail-info { + display: flex; + align-items: center; +} + +.conversation-detail-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--primary-color); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + margin-right: 0.75rem; +} + +.conversation-detail-name { + font-weight: 600; + color: var(--text-color); + margin-bottom: 0.25rem; +} + +.conversation-detail-status { + font-size: 0.875rem; + color: var(--text-muted); +} + +.conversation-detail-actions { + display: flex; + gap: 0.5rem; +} + +.detail-action-btn { + width: 36px; + height: 36px; + border: none; + background: transparent; + color: var(--text-muted); + border-radius: 50%; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.detail-action-btn:hover { + background: var(--light-bg); + color: var(--primary-color); +} + +/* Messages Container */ +.messages-container { + flex: 1; + overflow-y: auto; + padding: 1rem; + background: var(--light-bg); +} + +.message { + display: flex; + align-items: flex-start; + margin-bottom: 1rem; + padding: 0 1rem; +} + +.message.sent { + flex-direction: row-reverse; +} + +.message.received { + flex-direction: row; +} + +.message.reply { + margin-left: 2rem; + border-left: 2px solid var(--border-color); + padding-left: 1rem; + background: rgba(0, 99, 110, 0.02); + border-radius: 0.5rem; +} + +.message-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--primary-color); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 0.875rem; + margin: 0 0.75rem; + flex-shrink: 0; +} + +.message-avatar.reply-avatar { + width: 32px; + height: 32px; + font-size: 0.75rem; + margin: 0 0.5rem; +} + +.message-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + max-width: 70%; +} + +.message.sent .message-content { + align-items: flex-end; +} + +.message.received .message-content { + align-items: flex-start; +} + +.message-bubble { + background: var(--light-bg); + border: 1px solid var(--border-color); + border-radius: 1rem; + padding: 0.75rem 1rem; + margin-bottom: 0.25rem; + position: relative; + word-wrap: break-word; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.message-bubble.reply-bubble { + background: white; + border-radius: 0.75rem; + padding: 0.5rem 0.75rem; + font-size: 0.875rem; +} + +.message.sent .message-bubble { + background: var(--primary-color); + color: white; + border-bottom-right-radius: 0.25rem; +} + +.message.received .message-bubble { + background: white; + color: var(--text-color); + border-bottom-left-radius: 0.25rem; +} + +.thread-indicator { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; + padding: 0.25rem 0.5rem; + background: rgba(0, 99, 110, 0.1); + border-radius: 0.25rem; + font-size: 0.75rem; + color: var(--primary-color); +} + +.reply-info { + display: flex; + align-items: center; + gap: 0.25rem; + margin-bottom: 0.25rem; + font-size: 0.75rem; + color: var(--text-muted); +} + +.message-text { + line-height: 1.5; + margin-bottom: 0.25rem; +} + +.message-time { + font-size: 0.75rem; + color: var(--text-muted); + display: flex; + align-items: center; + gap: 0.5rem; +} + +/* Reply Area */ +.message-reply-area { + padding: 1rem 1.5rem; + border-top: 1px solid var(--border-color); + background: white; +} + +.reply-form { + display: flex; + align-items: flex-end; + gap: 0.75rem; +} + +.reply-input { + flex: 1; + padding: 0.75rem 1rem; + border: 1px solid var(--border-color); + border-radius: 1.5rem; + resize: none; + font-family: inherit; + font-size: 0.875rem; + line-height: 1.5; + max-height: 120px; + min-height: 44px; + transition: border-color 0.2s ease; +} + +.reply-input:focus { + outline: none; + border-color: var(--primary-color); +} + +.reply-tools { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.tool-btn { + width: 36px; + height: 36px; + border: none; + background: transparent; + color: var(--text-muted); + border-radius: 50%; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.tool-btn:hover { + background: var(--light-bg); + color: var(--primary-color); +} + +.send-btn { + width: 36px; + height: 36px; + border: none; + background: var(--primary-color); + color: white; + border-radius: 50%; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.send-btn:hover:not(:disabled) { + background: #004d57; +} + +.send-btn:disabled { + background: var(--text-muted); + cursor: not-allowed; +} + +/* Empty States */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-muted); + text-align: center; + padding: 2rem; +} + +.empty-state i { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.5; +} + +.empty-state h3 { + margin-bottom: 0.5rem; + color: var(--text-color); +} + +.empty-state p { + font-size: 0.875rem; +} + +/* Compose Modal */ +.compose-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.compose-modal-content { + background: white; + border-radius: 0.5rem; + width: 90%; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.compose-modal-header { + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border-color); + display: flex; + align-items: center; + justify-content: space-between; +} + +.compose-modal-body { + padding: 1.5rem; +} + +.compose-form-group { + margin-bottom: 1rem; +} + +.compose-form-label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; + color: var(--text-color); +} + +.compose-form-control { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + font-size: 0.875rem; + transition: border-color 0.2s ease; +} + +.compose-form-control:focus { + outline: none; + border-color: var(--primary-color); +} + +.compose-form-textarea { + resize: vertical; + min-height: 120px; + font-family: inherit; + line-height: 1.5; +} + +.compose-modal-footer { + padding: 1rem 1.5rem; + border-top: 1px solid var(--border-color); + display: flex; + justify-content: flex-end; + gap: 0.75rem; +} + +.btn { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 0.5rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + font-size: 0.875rem; +} + +.btn-primary { + background: var(--primary-color); + color: white; +} + +.btn-primary:hover { + background: #004d57; +} + +.btn-secondary { + background: var(--light-bg); + color: var(--text-color); + border: 1px solid var(--border-color); +} + +.btn-secondary:hover { + background: var(--border-color); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .unified-messages { + height: calc(100vh - 60px); + } + + .messages-sidebar { + width: 100%; + position: absolute; + z-index: 10; + transform: translateX(-100%); + transition: transform 0.3s ease; + } + + .messages-sidebar.show { + transform: translateX(0); + } + + .conversation-detail { + width: 100%; + } + + .message-content { + max-width: 85%; + } + + .compose-modal-content { + width: 95%; + margin: 1rem; + } +} + +@media (max-width: 480px) { + .conversation-item { + padding: 0.5rem; + } + + .conversation-avatar { + width: 40px; + height: 40px; + font-size: 0.875rem; + } + + .message { + padding: 0 0.5rem; + } + + .message-avatar { + width: 32px; + height: 32px; + font-size: 0.75rem; + margin: 0 0.5rem; + } + + .message-content { + max-width: 90%; + } + + .message-bubble { + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + } +} + +/* Animations */ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.message { + animation: slideIn 0.3s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.conversation-item { + animation: fadeIn 0.2s ease; +} + +/* Loading States */ +.loading { + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; + color: var(--text-muted); +} + +.spinner { + width: 20px; + height: 20px; + border: 2px solid var(--border-color); + border-top: 2px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Notification Styles */ +.notification { + position: fixed; + top: 20px; + right: 20px; + padding: 1rem 1.5rem; + border-radius: 0.5rem; + color: white; + font-weight: 500; + z-index: 1001; + animation: slideInRight 0.3s ease; + max-width: 300px; +} + +.notification.success { + background: var(--success-color); +} + +.notification.error { + background: var(--danger-color); +} + +.notification.info { + background: var(--info-color); +} + +.notification.warning { + background: var(--warning-color); +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(100%); + } + to { + opacity: 1; + transform: translateX(0); + } +} diff --git a/static/css/update_meeting.css b/static/css/update_meeting.css new file mode 100644 index 0000000..00e0d01 --- /dev/null +++ b/static/css/update_meeting.css @@ -0,0 +1,162 @@ + +/* UI Variables for the KAAT-S Theme */ +:root { + --kaauh-teal: #00636e; + --kaauh-teal-dark: #004a53; + --kaauh-border: #eaeff3; + --kaauh-primary-text: #343a40; + --kaauh-gray: #6c757d; + + /* Status Colors for alerts/messages */ + --kaauh-success: var(--kaauh-teal); + --kaauh-danger: #dc3545; + --kaauh-info: #17a2b8; +} + +/* CONTAINER AND CARD STYLING */ +.container { + padding: 2rem 1rem; +} +.card { + border: 1px solid var(--kaauh-border); + border-radius: 0.75rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); + max-width: 600px; + margin: 0 auto; /* Center the card */ + padding: 1.5rem; +} + +/* HEADER STYLING (The section outside the card) */ +.header { + text-align: center; + margin-bottom: 2rem; +} +.header h1 { + font-size: 2rem; + color: var(--kaauh-teal-dark); + font-weight: 700; + margin-bottom: 0.25rem; +} +.header p { + color: var(--kaauh-gray); + font-size: 1rem; +} + +/* CARD TITLE STYLING */ +.card-title { + font-size: 1.25rem; + color: var(--kaauh-teal-dark); + font-weight: 600; + border-bottom: 1px solid var(--kaauh-border); + padding-bottom: 0.75rem; + margin-bottom: 1.5rem; +} + +/* FORM STYLING */ +.form-row { + margin-bottom: 1.5rem; +} +.form-label { + display: block; + font-weight: 600; + color: var(--kaauh-gray); + margin-bottom: 0.5rem; + font-size: 0.9rem; +} +.form-input { + display: block; + width: 100%; + padding: 0.75rem 1rem; + font-size: 1rem; + line-height: 1.5; + color: var(--kaauh-primary-text); + background-color: #fff; + background-clip: padding-box; + border: 1px solid var(--kaauh-border); + border-radius: 0.5rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +.form-input:focus { + border-color: var(--kaauh-teal); + outline: 0; + box-shadow: 0 0 0 0.1rem rgba(0, 99, 110, 0.25); +} +input[type="datetime-local"] { + font-family: inherit; +} + +/* MESSAGES/ALERTS STYLING */ +.messages { + max-width: 600px; + margin: 0 auto 1.5rem auto; +} +.alert { + padding: 1rem; + border-radius: 0.5rem; + margin-bottom: 0.5rem; + font-weight: 500; +} +.alert-success { + color: white; + background-color: var(--kaauh-success); + border-color: var(--kaauh-success); +} +.alert-danger { + color: white; + background-color: var(--kaauh-danger); + border-color: var(--kaauh-danger); +} +.alert-info { + color: white; + background-color: var(--kaauh-info); + border-color: var(--kaauh-info); +} + +/* BUTTON STYLING */ +.actions { + margin-top: 1.5rem; + display: flex; + gap: 1rem; +} +.btn-base { + display: inline-flex; + align-items: center; + gap: 0.5rem; + text-align: center; + vertical-align: middle; + cursor: pointer; + user-select: none; + padding: 0.5rem 1rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.5rem; + font-weight: 600; + border: 1px solid transparent; + transition: all 0.2s ease; + text-decoration: none; +} + +/* Primary Action Button (Update) */ +.btn-main-action { + background-color: var(--kaauh-teal); + border-color: var(--kaauh-teal); + color: white; +} +.btn-main-action:hover { + background-color: var(--kaauh-teal-dark); + border-color: var(--kaauh-teal-dark); + box-shadow: 0 4px 8px rgba(0,0,0,0.15); + color: white; +} + +/* Secondary Button (Cancel) */ +.btn-kaats-outline-secondary { + color: var(--kaauh-secondary); + border-color: var(--kaauh-secondary); + background-color: transparent; +} +.btn-kaats-outline-secondary:hover { + background-color: var(--kaauh-secondary); + color: white; + border-color: var(--kaauh-secondary); +} diff --git a/static/image/hospital_logo copy.png b/static/image/hospital_logo copy.png new file mode 100644 index 0000000..4250a35 Binary files /dev/null and b/static/image/hospital_logo copy.png differ diff --git a/static/image/hospital_logo_1 copy.png b/static/image/hospital_logo_1 copy.png new file mode 100644 index 0000000..ff44820 Binary files /dev/null and b/static/image/hospital_logo_1 copy.png differ diff --git a/static/image/hospital_logo_2 copy.png b/static/image/hospital_logo_2 copy.png new file mode 100644 index 0000000..a1698d4 Binary files /dev/null and b/static/image/hospital_logo_2 copy.png differ diff --git a/static/image/hospital_logo_3 copy.png b/static/image/hospital_logo_3 copy.png new file mode 100644 index 0000000..5a9e883 Binary files /dev/null and b/static/image/hospital_logo_3 copy.png differ diff --git a/static/image/kaauh.jpeg b/static/image/kaauh.jpeg new file mode 100644 index 0000000..f569ae6 Binary files /dev/null and b/static/image/kaauh.jpeg differ diff --git a/static/image/kaauh.png b/static/image/kaauh.png new file mode 100644 index 0000000..bd28a2f Binary files /dev/null and b/static/image/kaauh.png differ diff --git a/static/image/kaauh_banner.png b/static/image/kaauh_banner.png new file mode 100644 index 0000000..4065c35 Binary files /dev/null and b/static/image/kaauh_banner.png differ diff --git a/static/image/kaauh_green.png b/static/image/kaauh_green.png new file mode 100644 index 0000000..561e750 Binary files /dev/null and b/static/image/kaauh_green.png differ diff --git a/static/image/kaauh_green1.png b/static/image/kaauh_green1.png new file mode 100644 index 0000000..10dbccb Binary files /dev/null and b/static/image/kaauh_green1.png differ diff --git a/static/image/vision copy.svg b/static/image/vision copy.svg new file mode 100644 index 0000000..97124a3 --- /dev/null +++ b/static/image/vision copy.svg @@ -0,0 +1,120 @@ + diff --git a/static/image/vision2.svg b/static/image/vision2.svg new file mode 100644 index 0000000..43156d8 --- /dev/null +++ b/static/image/vision2.svg @@ -0,0 +1,8 @@ + diff --git a/static/js/messages.js b/static/js/messages.js new file mode 100644 index 0000000..29f73fd --- /dev/null +++ b/static/js/messages.js @@ -0,0 +1,949 @@ +/** + * Message System JavaScript + * Handles interactive features for the modern message interface + */ + +class MessageSystem { + constructor() { + this.init(); + } + + init() { + this.initSearch(); + this.initFolderNavigation(); + this.initMessageActions(); + this.initComposeFeatures(); + this.initKeyboardShortcuts(); + this.initAutoSave(); + this.initAttachments(); + this.initTooltips(); + } + + /** + * Initialize search functionality + */ + initSearch() { + const searchInputs = document.querySelectorAll('.search-input'); + searchInputs.forEach(input => { + let searchTimeout; + + input.addEventListener('input', (e) => { + clearTimeout(searchTimeout); + const searchTerm = e.target.value.toLowerCase(); + + searchTimeout = setTimeout(() => { + this.performSearch(searchTerm); + }, 300); + }); + + // Clear search on Escape key + input.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + e.target.value = ''; + this.performSearch(''); + } + }); + }); + } + + /** + * Perform search across message items + */ + performSearch(searchTerm) { + const messageItems = document.querySelectorAll('.message-item'); + let visibleCount = 0; + + messageItems.forEach(item => { + const text = item.textContent.toLowerCase(); + if (text.includes(searchTerm)) { + item.style.display = 'flex'; + visibleCount++; + } else { + item.style.display = 'none'; + } + }); + + // Show no results message if needed + this.updateSearchResults(visibleCount, searchTerm); + } + + /** + * Update search results display + */ + updateSearchResults(count, searchTerm) { + let noResults = document.querySelector('.search-no-results'); + + if (count === 0 && searchTerm) { + if (!noResults) { + noResults = document.createElement('div'); + noResults.className = 'search-no-results'; + noResults.innerHTML = ` +
+ {% trans "No messages match your search for" %} "${searchTerm}" +
+${replyData.reply_content}
+| Name | -Type | -Status | -API Key | -Created | -Actions | +{% trans "Name" %} | +{% trans "Type" %} | +{% trans "Status" %} | +{% trans "API Key" %} | +{% trans "Created" %} | +{% trans "Actions" %} | {{ source.source_type }} | {% if source.is_active %} - Active + {% trans "Active" %} {% else %} - Inactive + {% trans "Inactive" %} {% endif %} |
@@ -165,16 +165,16 @@
{% else %}
-
{% endif %}
No sources found+{% trans "No sources found" %}{% if search_query %} - No sources match your search criteria. + {% blocktrans with query=query %}No sources match your search criteria "{{ query }}".{% endblocktrans %} {% else %} - Get started by creating your first source. + {% trans "Get started by creating your first source." %} {% endif %} - Create Source + {% trans "Create Source" %} |
|---|