fix home page and add career page
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
# Job Postings Feature — Implementation Plan
|
||||
|
||||
## 1. Overview
|
||||
|
||||
This feature has two sides:
|
||||
|
||||
- **Admin side** — create/manage job position templates, publish open positions, review applicant submissions.
|
||||
- **Client side** — browse open positions, view details, and submit an application (with resume upload) that triggers a confirmation email.
|
||||
|
||||
---
|
||||
|
||||
## 2. Data Model
|
||||
|
||||
### JobTemplate
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `template_id` | UUID | Primary key |
|
||||
| `title` | string | |
|
||||
| `description` | text | |
|
||||
| `department` | string | |
|
||||
| `location` | string | |
|
||||
| `employment_type` | string | e.g. full-time, part-time, contract |
|
||||
| `requirements` | text | |
|
||||
| `salary_range` | string | optional |
|
||||
| `version` | int | increments on each edit |
|
||||
| `is_active` | bool | soft-archive instead of delete |
|
||||
| `created_by` | user ref | |
|
||||
| `created_at` / `updated_at` | timestamp | |
|
||||
|
||||
### JobPosition
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `position_id` | UUID | **Unique ID per job, required** |
|
||||
| `template_id` | UUID (nullable) | set if created from a template |
|
||||
| `title`, `description`, `department`, `location`, `employment_type`, `requirements`, `salary_range` | — | copied from template at creation time (not a live reference) |
|
||||
| `status` | enum | draft / open / closed / filled |
|
||||
| `created_at` / `updated_at` | timestamp | |
|
||||
| `posted_by` | user ref | |
|
||||
|
||||
### Application
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `application_id` | UUID | |
|
||||
| `position_id` | FK | which job they applied to |
|
||||
| `first_name`, `last_name` | string | required |
|
||||
| `email` | string | required, validated |
|
||||
| `phone` | string | required |
|
||||
| `resume_file_url` | string | pointer to stored file |
|
||||
| `submitted_at` | timestamp | |
|
||||
| `status` | enum | new / reviewed / contacted / rejected / hired |
|
||||
| `admin_notes` | text | for admin follow-up tracking |
|
||||
|
||||
---
|
||||
|
||||
## 3. Admin Side
|
||||
|
||||
### 3.1 Create Open Position
|
||||
- Form fields: title, description, department, location, employment type, requirements, salary range.
|
||||
- Option to start **blank** or **from a template**.
|
||||
- On save: generate unique `position_id`, default status `draft` → publish sets `open`.
|
||||
|
||||
### 3.2 Job Templates (CRUD)
|
||||
- List / create / edit / archive templates.
|
||||
- **Editing behavior**: support both
|
||||
- **Overwrite** current version, or
|
||||
- **Save as new version** (keeps history)
|
||||
- Version history viewable/revertible.
|
||||
- Archiving a template never affects positions already created from it (fields are copied, not linked live).
|
||||
|
||||
### 3.3 Create Position from Template
|
||||
- Admin selects a template → fields pre-fill a new position form → admin edits as needed → save generates a new `position_id`.
|
||||
|
||||
### 3.4 Review Applications
|
||||
- Dashboard listing all submissions, filterable by position, status, or date.
|
||||
- Detail view: applicant info, resume preview/download, position applied to.
|
||||
- Status + notes fields so admin can track follow-up (contacted, rejected, hired).
|
||||
- Nice-to-have: CSV export, direct email link to applicant.
|
||||
|
||||
---
|
||||
|
||||
## 4. Client Side
|
||||
|
||||
### 4.1 Open Positions Listing
|
||||
- Public page listing all positions where `status = open`.
|
||||
- Card/list view: title, department, location, short summary.
|
||||
|
||||
### 4.2 Position Detail Page
|
||||
- Full description, requirements, etc.
|
||||
- "Apply" call-to-action.
|
||||
|
||||
### 4.3 Application Form
|
||||
- Fields: First Name, Last Name, Email, Phone, Resume upload (PDF/DOC).
|
||||
- Client-side validation: required fields, email format, file type/size limits.
|
||||
- Submits to create an `Application` tied to `position_id`.
|
||||
|
||||
### 4.4 Confirmation Email
|
||||
- Sent automatically to the applicant's email on successful submission.
|
||||
- References the position title and sets expectation of review/follow-up.
|
||||
- Optional: parallel notification email to admin/HR inbox.
|
||||
|
||||
---
|
||||
|
||||
## 5. API Endpoints (suggested)
|
||||
|
||||
**Admin**
|
||||
```
|
||||
POST /admin/templates
|
||||
PUT /admin/templates/:id
|
||||
GET /admin/templates
|
||||
|
||||
POST /admin/positions (optional template_id)
|
||||
PUT /admin/positions/:id
|
||||
GET /admin/positions
|
||||
|
||||
GET /admin/applications (filter by position, status)
|
||||
PATCH /admin/applications/:id (update status/notes)
|
||||
```
|
||||
|
||||
**Client (public)**
|
||||
```
|
||||
GET /positions (open only)
|
||||
GET /positions/:id
|
||||
POST /positions/:id/apply (multipart form, includes resume)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Infrastructure Notes
|
||||
|
||||
- **File storage**: resumes go to object storage (S3 or equivalent); store the file URL/key on the Application record, not the binary in the database.
|
||||
- **Email**: use a transactional email provider (SendGrid, SES, Postmark) for confirmation + admin notification emails, driven by templates.
|
||||
- **Auth**: admin routes require authenticated/role-gated access; client routes remain public.
|
||||
|
||||
---
|
||||
|
||||
## 7. Database Migrations
|
||||
|
||||
Assumes PostgreSQL syntax (adjust types for MySQL/SQLite as needed). Each migration is additive and ordered so foreign keys resolve correctly.
|
||||
|
||||
### Migration 001 — create `job_templates`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TABLE job_templates (
|
||||
template_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_job_templates_active ON job_templates (is_active);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_templates;
|
||||
```
|
||||
|
||||
### Migration 002 — create `job_template_versions` (version history)
|
||||
```sql
|
||||
-- up
|
||||
CREATE TABLE job_template_versions (
|
||||
version_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_id UUID NOT NULL REFERENCES job_templates(template_id) ON DELETE CASCADE,
|
||||
version INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
saved_by UUID REFERENCES users(id),
|
||||
saved_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_template_versions_template_id ON job_template_versions (template_id);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_template_versions;
|
||||
```
|
||||
|
||||
### Migration 003 — create `job_positions`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TYPE position_status AS ENUM ('draft', 'open', 'closed', 'filled');
|
||||
|
||||
CREATE TABLE job_positions (
|
||||
position_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_id UUID REFERENCES job_templates(template_id) ON DELETE SET NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
status position_status NOT NULL DEFAULT 'draft',
|
||||
posted_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_job_positions_status ON job_positions (status);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_positions;
|
||||
DROP TYPE IF EXISTS position_status;
|
||||
```
|
||||
|
||||
### Migration 004 — create `applications`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TYPE application_status AS ENUM ('new', 'reviewed', 'contacted', 'rejected', 'hired');
|
||||
|
||||
CREATE TABLE applications (
|
||||
application_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
position_id UUID NOT NULL REFERENCES job_positions(position_id) ON DELETE CASCADE,
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(30) NOT NULL,
|
||||
resume_file_url TEXT NOT NULL,
|
||||
status application_status NOT NULL DEFAULT 'new',
|
||||
admin_notes TEXT,
|
||||
submitted_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_applications_position_id ON applications (position_id);
|
||||
CREATE INDEX idx_applications_status ON applications (status);
|
||||
CREATE INDEX idx_applications_email ON applications (email);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS applications;
|
||||
DROP TYPE IF EXISTS application_status;
|
||||
```
|
||||
|
||||
### Migration 005 — updated_at auto-touch triggers (optional, Postgres)
|
||||
```sql
|
||||
-- up
|
||||
CREATE OR REPLACE FUNCTION set_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_job_templates_updated_at
|
||||
BEFORE UPDATE ON job_templates
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_job_positions_updated_at
|
||||
BEFORE UPDATE ON job_positions
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- down
|
||||
DROP TRIGGER IF EXISTS trg_job_templates_updated_at ON job_templates;
|
||||
DROP TRIGGER IF EXISTS trg_job_positions_updated_at ON job_positions;
|
||||
DROP FUNCTION IF EXISTS set_updated_at();
|
||||
```
|
||||
|
||||
### Migration order & notes
|
||||
1. `job_templates` → 2. `job_template_versions` → 3. `job_positions` → 4. `applications` → 5. triggers.
|
||||
2. `users` table is assumed to already exist (for `created_by` / `posted_by`); drop those FK constraints if no auth/user table exists yet.
|
||||
3. Run each migration's `up` in order; `down` scripts reverse in the opposite order for rollback.
|
||||
4. If using a migration tool (Knex, Prisma, Sequelize, Alembic, Rails ActiveRecord, etc.), split each numbered migration above into that tool's file format/naming convention — the SQL logic stays the same.
|
||||
|
||||
---
|
||||
|
||||
## 8. Build Phases
|
||||
|
||||
| Phase | Scope |
|
||||
|---|---|
|
||||
| 1 | Data models + admin CRUD for Positions (no templates yet) |
|
||||
| 2 | Template CRUD + versioning + "create position from template" |
|
||||
| 3 | Client-facing listing + detail pages |
|
||||
| 4 | Application form + file upload + submission handling |
|
||||
| 5 | Confirmation email + admin notification email |
|
||||
| 6 | Admin review dashboard (applications list, status, notes) |
|
||||
| 7 | Polish: validation, admin auth/permissions, testing |
|
||||
|
||||
---
|
||||
|
||||
## 9. Open Questions
|
||||
|
||||
- How is admin access authenticated/restricted (login system, roles)?
|
||||
- Should duplicate applications (same email + position) be blocked or allowed?
|
||||
- What resume file types/size limits are acceptable?
|
||||
- Should closed positions remain visible (marked "closed") or disappear from the client list entirely?
|
||||
@@ -0,0 +1,52 @@
|
||||
# Scope Discipline Rules
|
||||
|
||||
These rules override any general instinct to "improve while I'm in there." Follow them on every task, no exceptions.
|
||||
|
||||
## Before touching any code
|
||||
|
||||
1. Restate the task in one sentence: what behavior must change, and what the expected outcome is.
|
||||
2. Identify the smallest set of files/functions responsible for that behavior. This is your **allowed scope**. Everything else is **protected**.
|
||||
3. Read the relevant code before editing it. Do not edit based on assumptions about how it probably works.
|
||||
|
||||
## The hard rule: ask before expanding scope
|
||||
|
||||
If, while working, you find that:
|
||||
- a file outside your allowed scope needs to change,
|
||||
- a dependency needs to be added/updated,
|
||||
- a test needs modification,
|
||||
- an unrelated bug is blocking you,
|
||||
- or a "cleaner" implementation would touch more than the minimum,
|
||||
|
||||
**stop and ask me before making that change.** Explain:
|
||||
- what you were trying to do,
|
||||
- why the fix requires going outside the original scope,
|
||||
- exactly what you want to change and where.
|
||||
|
||||
Wait for my answer. Do not proceed on your own judgment, even if you're confident it's correct or trivial.
|
||||
|
||||
This applies even to small things (renaming a variable for clarity, fixing a typo in an unrelated comment, reformatting a block you had to scroll past). If it's not required to satisfy the request, ask first.
|
||||
|
||||
## While editing
|
||||
|
||||
- Make the fewest-line, fewest-file change that correctly satisfies the request.
|
||||
- Preserve existing naming, structure, patterns, and formatting. Match the codebase's existing style, don't impose your own.
|
||||
- Never run project-wide formatters/linters-with-autofix/import-organizers as a side effect of a small change.
|
||||
- Never touch tests except to add new ones that validate the requested behavior — and only after confirming that's in scope.
|
||||
- Treat any uncommitted/staged changes already in the working tree as off-limits. Don't revert, reset, or absorb them into your edit.
|
||||
|
||||
## Before reporting done
|
||||
|
||||
Review your own diff, file by file, line by line. For anything you can't justify with "this was required by the explicit request," revert it.
|
||||
|
||||
Then report:
|
||||
- **Files changed** — list, with a one-line reason each tied directly to the request.
|
||||
- **Scope confirmation** — explicitly state: "No files, dependencies, tests, or config outside this list were modified."
|
||||
- **Anything you noticed but didn't touch** — unrelated bugs, tech debt, cleanup opportunities. Mention them, don't fix them.
|
||||
|
||||
## If the task genuinely can't be done without expanding scope
|
||||
|
||||
Say so plainly, explain what would need to change and why, and wait for confirmation. Don't silently do the bigger version, and don't pretend a partial/incorrect fix is complete.
|
||||
|
||||
---
|
||||
|
||||
**Default when uncertain: don't make the change, ask instead.**
|
||||
@@ -222,6 +222,7 @@ $routes->post('/user/store', 'View\UserController::store');
|
||||
$routes->get('/thankyou', 'View\UserController::thankyou'); // Thank you page route
|
||||
$routes->get('/', 'View\UserController::home'); // Home page route
|
||||
$routes->get('/about', 'View\UserController::about'); // About page route
|
||||
$routes->get('/careers', 'View\UserController::careers'); // Careers page route
|
||||
$routes->get('/classes', 'View\UserController::classes'); // Classes page route
|
||||
$routes->get('/contact', 'View\UserController::contact'); // Contact Us page route
|
||||
$routes->post('/user/login', 'AuthController::login');
|
||||
|
||||
@@ -117,6 +117,12 @@ class UserController extends BaseController
|
||||
return view('/about');
|
||||
}
|
||||
|
||||
// Method to show the careers page
|
||||
public function careers()
|
||||
{
|
||||
return view('/careers');
|
||||
}
|
||||
|
||||
// Method to show the classes page
|
||||
public function classes()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Careers | Al Rahma Sunday School</title>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<meta content="Volunteer careers and openings at Al Rahma Sunday School" name="description">
|
||||
|
||||
<link href="<?= base_url('assets/images/favicon.ico') ?>" rel="icon">
|
||||
<link href="<?= base_url('assets/boot_css/bootstrap.min.css') ?>" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="<?= base_url('css/style.css') ?>" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--ink: #16262B;
|
||||
--ink-soft: #3E4E51;
|
||||
--paper: #F3EFE3;
|
||||
--paper-deep: #E9E3D2;
|
||||
--sage: #E7EEE6;
|
||||
--primary: #0B5D52;
|
||||
--primary-dark: #073F38;
|
||||
--accent: #C6963C;
|
||||
--accent-soft: #EFE1BC;
|
||||
--line: rgba(22, 38, 43, 0.12);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
color: var(--ink);
|
||||
background-color: var(--paper);
|
||||
overflow-x: hidden;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Amiri', serif;
|
||||
font-weight: 700;
|
||||
color: var(--ink);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.rule {
|
||||
width: 56px;
|
||||
height: 3px;
|
||||
background-color: var(--accent);
|
||||
border: none;
|
||||
margin: 0 0 1.25rem;
|
||||
}
|
||||
|
||||
.btn-brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
background-color: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
padding: 0.85rem 1.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-brand:hover {
|
||||
background-color: var(--primary-dark);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-brand-sm {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background-color: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
padding: 0.6rem 1.25rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-brand-sm:hover {
|
||||
background-color: var(--primary-dark);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-brand-outline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border: 1.5px solid var(--ink);
|
||||
color: var(--ink);
|
||||
border-radius: 999px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-brand-outline:hover {
|
||||
background-color: var(--ink);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
.btn-brand-danger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border: 1.5px solid #a33;
|
||||
color: #a33;
|
||||
border-radius: 999px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-brand-danger:hover {
|
||||
background-color: #a33;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
background-color: var(--paper) !important;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding-top: 0.6rem;
|
||||
padding-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.navbar .nav-link {
|
||||
color: var(--ink-soft);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.navbar .nav-link:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.navbar-text {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.careers-hero {
|
||||
position: relative;
|
||||
background: linear-gradient(rgba(7, 63, 56, .88), rgba(7, 63, 56, .88)), url("<?= base_url('images/call-to-action.jpg') ?>") center center / cover no-repeat;
|
||||
min-height: 320px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--paper);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.careers-hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.14;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='84' height='84' viewBox='0 0 84 84'%3E%3Cg fill='none' stroke='%23C6963C' stroke-width='1'%3E%3Cpath d='M42 2 L62 22 L42 42 L22 22 Z'/%3E%3Cpath d='M0 42 L20 22 L40 42 L20 62 Z'/%3E%3Cpath d='M42 42 L62 22 L84 42 L62 62 Z'/%3E%3Cpath d='M42 42 L62 62 L42 84 L22 62 Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 84px 84px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.careers-hero .container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.careers-hero h1 {
|
||||
color: var(--paper);
|
||||
font-size: 2.6rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.careers-hero p.lead {
|
||||
color: #DCE6DD;
|
||||
font-size: 1.1rem;
|
||||
max-width: 56ch;
|
||||
}
|
||||
|
||||
/* Culture strip */
|
||||
.culture-item {
|
||||
background: #ffffff;
|
||||
border-top: 3px solid var(--accent);
|
||||
padding: 1.75rem 1.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.culture-item h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.culture-item p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Openings */
|
||||
.openings-section {
|
||||
background-color: var(--sage);
|
||||
}
|
||||
|
||||
.openings-intro h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.opening-card {
|
||||
border: 1px solid var(--line);
|
||||
background: #ffffff;
|
||||
height: 100%;
|
||||
padding: 1.75rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.opening-card h3 {
|
||||
color: var(--ink);
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.opening-meta {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.01em;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.opening-list {
|
||||
padding-left: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.opening-list li {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.opening-list li::marker {
|
||||
color: var(--accent);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light sticky-top px-4 px-lg-5">
|
||||
<a href="<?= base_url('/') ?>" class="navbar-brand d-flex align-items-center">
|
||||
<img src="<?= base_url('images/logo.png') ?>" alt="Al Rahma Sunday School" style="height: 56px; width: 56px; border-radius: 50%; object-fit: contain; background-color: #fff;">
|
||||
</a>
|
||||
<button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<div class="navbar-nav mx-auto">
|
||||
<a href="<?= base_url('/') ?>" class="nav-item nav-link">Home Page</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<?php if (session()->get('is_logged_in')): ?>
|
||||
<span class="navbar-text me-3">Welcome, <?= esc(session()->get('user_name')) ?></span>
|
||||
<a href="<?= base_url('/dashboard') ?>" class="btn-brand-outline me-2">Dashboard <i class="fa fa-tachometer-alt"></i></a>
|
||||
<a href="<?= base_url('/logout') ?>" class="btn-brand-danger">Logout <i class="fa fa-sign-out-alt"></i></a>
|
||||
<?php else: ?>
|
||||
<a href="<?= base_url('/login') ?>" class="btn-brand-outline me-2">Login <i class="fa fa-arrow-right"></i></a>
|
||||
<a href="<?= base_url('/register') ?>" class="btn-brand-sm">Register <i class="fa fa-arrow-right"></i></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="careers-hero">
|
||||
<div class="container py-5">
|
||||
<div class="col-lg-8">
|
||||
<h1>Open Positions</h1>
|
||||
<p class="lead mb-4">Serve with Al Rahma Sunday School and help students grow in Quran, Arabic, Islamic Studies and character.</p>
|
||||
<a class="btn-brand" href="#openings">View Openings <i class="fa fa-arrow-down"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="container-xxl py-5">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="culture-item">
|
||||
<h3>Faith-Centered Work</h3>
|
||||
<p>Support a school community focused on Islamic learning, strong character and service.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="culture-item">
|
||||
<h3>Supportive Team</h3>
|
||||
<p>Collaborate with teachers, assistants and administrators committed to student success.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="culture-item">
|
||||
<h3>Sunday Schedule</h3>
|
||||
<p>Volunteer in a structured weekend program serving families in the Greater Lowell community.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="openings" class="openings-section py-5">
|
||||
<div class="container">
|
||||
<div class="text-center openings-intro mb-5">
|
||||
<hr class="rule mx-auto">
|
||||
<h2>Current Open Positions</h2>
|
||||
<p class="mb-0">Review the available roles below and apply by creating an account.</p>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<article class="opening-card">
|
||||
<h3>Volunteer Teacher</h3>
|
||||
<div class="opening-meta">Instruction · Chelmsford, MA · Part-time volunteer</div>
|
||||
<p>Lead Quran, Arabic or Islamic Studies lessons in a warm classroom environment.</p>
|
||||
<ul class="opening-list">
|
||||
<li>Prepare weekly lessons and classroom activities.</li>
|
||||
<li>Guide students with patience and clear communication.</li>
|
||||
<li>Partner with school administration and families when needed.</li>
|
||||
</ul>
|
||||
<a class="btn-brand-sm" href="<?= base_url('/register') ?>">Apply Now <i class="fa fa-arrow-right"></i></a>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<article class="opening-card">
|
||||
<h3>Teacher Assistant</h3>
|
||||
<div class="opening-meta">Classroom Support · Chelmsford, MA · Part-time volunteer</div>
|
||||
<p>Support teachers and help students stay engaged throughout Sunday classes.</p>
|
||||
<ul class="opening-list">
|
||||
<li>Assist with activities, materials and classroom routines.</li>
|
||||
<li>Provide individual support to students during lessons.</li>
|
||||
<li>Help maintain a respectful and focused learning environment.</li>
|
||||
</ul>
|
||||
<a class="btn-brand-sm" href="<?= base_url('/register') ?>">Apply Now <i class="fa fa-arrow-right"></i></a>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<article class="opening-card">
|
||||
<h3>Administrative Volunteer</h3>
|
||||
<div class="opening-meta">Operations · Chelmsford, MA · Part-time volunteer</div>
|
||||
<p>Help the school operate smoothly through weekly administrative and event support.</p>
|
||||
<ul class="opening-list">
|
||||
<li>Support communications, supplies and weekly coordination.</li>
|
||||
<li>Help organize school events and program logistics.</li>
|
||||
<li>Assist staff with structured tasks before or during Sunday school.</li>
|
||||
</ul>
|
||||
<a class="btn-brand-sm" href="<?= base_url('/register') ?>">Apply Now <i class="fa fa-arrow-right"></i></a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<?php include(__DIR__ . '/partials/footer.php'); ?>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+480
-637
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,3 @@
|
||||
<?php
|
||||
$userRole = session()->get('role'); // assuming you store it as 'role'
|
||||
$quickTourUrl = base_url('/help_center'); // default
|
||||
|
||||
switch ($userRole) {
|
||||
case 'parent':
|
||||
$quickTourUrl = base_url('/parent');
|
||||
break;
|
||||
case 'teacher':
|
||||
$quickTourUrl = base_url('/teacher');
|
||||
break;
|
||||
case 'teacher_assistant':
|
||||
$quickTourUrl = base_url('/teacher');
|
||||
break;
|
||||
}
|
||||
?>
|
||||
<link rel="stylesheet" href="<?= base_url('assets/css/landing_page.css') ?>">
|
||||
|
||||
<footer class="footer mt-auto custom-footer text-white py-4">
|
||||
<div class="container">
|
||||
<div class="row align-items-start justify-content-between">
|
||||
@@ -48,47 +30,50 @@ switch ($userRole) {
|
||||
<li>
|
||||
<a href="/account_creation_guide.pdf" target="_blank" rel="noopener noreferrer"
|
||||
class="pdf-link" data-filename="account_creation_guide.pdf">
|
||||
<i class="fas fa-file-pdf"></i>How To Create An Account
|
||||
<i class="fas fa-file-pdf me-1"></i>How To Create An Account
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<p class="text-center text-white">© 2026 Al Rahma Sunday School by ISGL. All Rights Reserved.</p>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="rights-line">© 2026 Al Rahma Sunday School by ISGL. All Rights Reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Check if PDF files exist and add error handling
|
||||
document.querySelectorAll('.pdf-link').forEach(link => {
|
||||
const pdfUrl = link.getAttribute('href');
|
||||
<style>
|
||||
.custom-footer {
|
||||
background-color: var(--ink, #16262B);
|
||||
color: #CFC9B7;
|
||||
}
|
||||
|
||||
// Test if the PDF exists
|
||||
fetch(pdfUrl, {
|
||||
method: 'HEAD'
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
// File doesn't exist or is inaccessible
|
||||
link.style.opacity = '0.7';
|
||||
link.title = 'File might be temporarily unavailable';
|
||||
console.warn('PDF might be missing:', pdfUrl);
|
||||
.custom-footer .info-list li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: #CFC9B7;
|
||||
}
|
||||
|
||||
// Modify click behavior to show helpful message
|
||||
link.addEventListener('click', function(e) {
|
||||
if (!confirm('The PDF file might be temporarily unavailable. Try to open it anyway?')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error checking PDF:', pdfUrl, error);
|
||||
link.style.opacity = '0.7';
|
||||
link.title = 'File check failed';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
.custom-footer .info-list i {
|
||||
color: var(--accent, #C6963C);
|
||||
}
|
||||
|
||||
.custom-footer .pdf-link {
|
||||
color: #CFC9B7;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.custom-footer .pdf-link:hover {
|
||||
color: var(--accent, #C6963C);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.custom-footer .rights-line {
|
||||
text-align: center;
|
||||
color: #7FBFA0;
|
||||
margin: 1.5rem auto 0;
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
# Careers Section (Home Page) — Implementation Plan
|
||||
|
||||
## 1. Goals & Scope
|
||||
- Attract candidates and showcase company culture directly from the home page
|
||||
- Careers lives as a **section on the home page**, not a standalone `/careers` page
|
||||
- Section should link out to a full job detail view or an ATS for the actual application step
|
||||
- Decide: in-house application handling vs. linking out to an ATS (Greenhouse, Lever, Workable, etc.)
|
||||
|
||||
## 2. Content Structure (Home Page Section)
|
||||
- **Section heading/hero** — tagline + short pitch on why to work here
|
||||
- **Culture/values snippet** — a few photos or highlights, benefits teaser (health, remote work, PTO, equity, etc.)
|
||||
- **Open positions preview** — short list (e.g., top 3–5) with title, department, location, type
|
||||
- **"View all openings" CTA** — links to full listing (could be a modal, an anchor-expanded list, or an external ATS board)
|
||||
- **Application CTA** — "Apply Now" per role, linking to a form, email, or ATS
|
||||
- **Optional extras** — employee testimonials, office photos/video, perks grid
|
||||
|
||||
## 3. Data Model
|
||||
Fields per job posting:
|
||||
```
|
||||
title
|
||||
department
|
||||
location
|
||||
employment_type
|
||||
description
|
||||
requirements
|
||||
salary_range (optional)
|
||||
posted_date
|
||||
status (open/closed)
|
||||
apply_link
|
||||
```
|
||||
Store in a CMS (Sanity, Contentful, Strapi) or a simple JSON/database table if no CMS exists.
|
||||
|
||||
## 4. Technical Approach
|
||||
- **Static list** — hardcode JSON if roles change rarely
|
||||
- **CMS-driven** — non-technical team can add/remove postings without code
|
||||
- **ATS integration** — pull live listings via API (Greenhouse/Lever both offer public job board APIs) for least maintenance
|
||||
- **Application handling** — form → email service (e.g., SendGrid) or direct link to ATS application page
|
||||
|
||||
## 5. Pages / Routes
|
||||
- `/` (home page) — Careers section embedded, e.g. `#careers` anchor for nav linking
|
||||
- `/careers/[job-slug]` — individual job detail page (linked from the home section)
|
||||
- `/careers/apply/[job-slug]` — application form (optional)
|
||||
- Consider adding "Careers" to the main nav, scrolling/linking to the `#careers` section
|
||||
|
||||
## 6. Design Considerations
|
||||
- Keep the section concise — it's part of the home page, not the main focus, so limit to a handful of featured/open roles
|
||||
- Mobile-responsive job cards, and ensure the section doesn't overload home page load time
|
||||
- Clear, prominent "View openings" / "Apply" CTAs
|
||||
- SEO: `JobPosting` schema.org structured data on the linked job detail pages so roles surface in Google Jobs search
|
||||
- Make sure the section is reachable via nav (e.g., "Careers" nav item scrolls to the section or links to `/#careers`)
|
||||
|
||||
## 7. Build Order
|
||||
1. Design mockups/wireframe for the home page section (placement, hero, mini job list)
|
||||
2. Set up data source (CMS or ATS API) for job postings
|
||||
3. Build the home page Careers section + "view all/apply" links
|
||||
4. Build job detail page(s) linked from the section
|
||||
5. Wire up application flow
|
||||
6. Add SEO schema markup on job detail pages
|
||||
7. Test on mobile; submit a test application
|
||||
8. Launch and monitor applications
|
||||
|
||||
## Open Questions
|
||||
- What is the site built with (React/Next.js, WordPress, Webflow, plain HTML)?
|
||||
- Use an existing ATS (Greenhouse/Lever) or handle applications via email/form?
|
||||
Reference in New Issue
Block a user