You:
Suggest me between these two. Making them in 6-12 months, which will have a better possibility to get some paid clients quicker than the other ? I'm 20 year+ seasons backend dev and with the help of AI, i can build systems really fast. Accounting & HR/Payroll SaaS like smartoffice.ai Recruiting portal like Breezy HR
ChatGPT:
If the goal is paid clients within 6–12 months, with you building fast, the answer is quite clear based on:
-
barrier to adoption
-
required trust/compliance
-
sales cycle length
-
competition dynamics
-
onboarding friction
-
willingness of small businesses to pay quickly
Accounting + payroll means:
-
tax rules
-
payroll calculations
-
government submissions
-
legal liabilities
-
errors = lawsuits, penalties
This creates high fear and slow adoption.
Recruiting portals avoid all of this.
Small businesses feel:
“I need candidates now or I lose money.”
Hiring urgency is high.
Payroll urgency is low because:
"We already have Excel / accountant / local software."
A paid MVP for recruiting can be:
-
job posting
-
careers page builder
-
applicant tracking
-
pipeline Kanban
-
interview scheduling
-
WhatsApp/SMS notifications
This can be polished in weeks.
Accounting/payroll needs:
-
chart of accounts
-
invoices
-
expenses
-
payroll calculations
-
leave management
-
statutory reports
-
integrations with banks/tax
Huge scope before someone pays.
| SaaS Type | Typical Paying Decision |
|---|---|
| Recruiting | 1–4 weeks |
| Payroll/Accounting | 3–12 months |
Businesses are cautious with payroll/accounting migration.
Accounting/payroll market locally is:
-
saturated
-
dominated by cheap desktop solutions
-
very price-sensitive
-
trust-based with accountants
Recruiting platforms locally are:
-
fragmented
-
poorly executed
-
no good UX
-
no automation
-
no WhatsApp integration
This is a real opportunity.
Given your strengths:
-
backend speed
-
automation
-
AI integration
-
system design
-
ability to build workflow tools
Recruiting + AI has powerful upsell:
-
AI job description writer
-
AI candidate matching
-
AI resume screening
-
WhatsApp bot interviews
Clients will see value immediately.
Recruiting Portal
-
Tk 3,000–20,000/month
-
5–20 clients possible
-
Agencies and SMEs adopt quickly
Accounting & Payroll
-
Tk 1,000–10,000/month
-
but very slow adoption
-
trust barrier
-
integration required
Within 6 months:
Recruiting portal:
✅ MVP live
✅ Paying clients
✅ Agencies onboard
✅ Viral growth via job postings
✅ Minimal legal risk
Accounting/payroll:
❌ still polishing core modules
❌ struggling with compliance
❌ clients hesitant to switch
❌ long onboarding
If the goal is:
Paid customers fast → Recruiting portal
If the goal is:
Long-term, defensible, sticky SaaS → Accounting & Payroll
If you want, I can suggest a micro-niche recruiting SaaS where you can realistically get 20 paying clients in Bangladesh in 3–4 months.
You:
Breezy HR ATS is really state of the art, suggest a good starting point and move towards features that can be independently usable.
ChatGPT:
A smart way to approach this is not to “build another Breezy HR”, but to identify:
-
features hiring teams will use immediately
-
that don't require deep ATS complexity
-
that can be sold as standalone micro-products
-
and later connect into a full ATS
This lets you launch early, validate, and monetize while evolving.
After analyzing modern ATS usage patterns, the single most universally used feature is:
Why this works:
-
every company wants a branded careers page
-
they can use it even if they already have an ATS
-
minimal data complexity
-
instantly visible value
-
shareable (viral loop)
-
brings candidates without training staff
This can be:
-
/careerspage builder -
job listing widget embed
-
application form storing candidates
-
email/WhatsApp notification to HR
This alone can bring paying users.
Features:
-
create jobs
-
publish careers page
-
embeddable job widget for websites
-
application form
-
notify HR via WhatsApp/SMS/email
-
simple pipeline: New → Reviewing → Interview → Hired
Even companies using:
-
Google Forms
-
Facebook Job posts
-
Excel tracking
-
Local HR software
will pay for notifications + branded page.
This becomes a true ATS component:
-
drag & drop candidates between stages
-
tagging
-
commenting
-
resume viewer
-
activity log
Still independent: hiring agencies and internal HR can run their recruitment through it even without other modules.
This is where you leap ahead of typical local/SME tools:
-
forward resumes from email inbox
-
auto-assign to recruiter
-
auto-screen questions
-
send interview reminders
-
move to “rejected” if no response in X days
Each automation can be:
✅ feature upgrade
✅ upsell
✅ independently valuable
These should NOT be in v1—most HR doesn't trust AI decisions initially.
But later, offer:
-
AI job description generator
-
AI resume screening (score candidates)
-
AI shortlisting suggestions
-
AI chatbot pre-screening via WhatsApp
These can be sold as add-ons.
| Module | Time to build | Sell standalone? | Value |
|---|---|---|---|
| Careers page + forms | 2–4 weeks | ✅ | High |
| Pipeline Kanban | 2–3 weeks | ✅ | Very High |
| Resume inbox parsing | 1 week | ✅ | Medium |
| WhatsApp automation | 1–2 weeks | ✅ | Very High |
| Interview scheduling | 2 weeks | ❌ | Medium |
| Reporting | 2–4 weeks | ❌ | Low |
| AI screening | 1–2 weeks | ✅ | Very High |
Typical ATS failure point:
They require full adoption to be useful.
Your approach:
Each module gives standalone value.
Meaning:
-
faster adoption
-
easier sales
-
incremental upgrades
-
low onboarding friction
You cannot out-feature Breezy HR globally right now.
But you can win with:
-
simplicity
-
WhatsApp-first hiring workflow
-
Bangladesh/South Asia recruitment patterns
-
agency workflow
-
low training requirement
Careers page + Job posting + Applicant inbox with WhatsApp notifications
You can sell this within 1–2 months.
If you'd like, I can outline:
✅ exact screens
✅ DB schema
✅ pricing model
✅ user acquisition plan targeting agencies
✅ viral loops using job postings
This would give you a concrete starting blueprint.
You:
✅ exact screens ✅ DB schema ✅ pricing model ✅ user acquisition plan targeting agencies ✅ viral loops using job postings
ChatGPT:
Here is a clean, practical, execution-ready blueprint for a recruiting SaaS starting with the most sellable module.
Everything below is designed so that:
-
each feature stands alone
-
you can charge early
-
minimal onboarding
-
fast development
-
strong viral loops
Purpose: instantly usable marketing asset
Elements:
-
company logo + name + about section
-
job list
-
title
-
location
-
salary range (optional)
-
tags (remote, full-time, contract)
-
-
click → job details page
Elements:
-
title
-
description
-
responsibilities
-
requirements
-
benefits
-
application form (embedded)
Bonus:
- share buttons (Facebook, LinkedIn, WhatsApp)
Fields:
-
name
-
email
-
phone (required)
-
CV upload
-
3 custom questions (text or MCQ)
After submit:
-
thank you page
-
WhatsApp notification to recruiter
-
email to candidate
Sidebar:
-
Jobs
-
Candidates
-
Settings
Actions:
-
create job
-
publish/unpublish
-
copy share link
-
embed widget code
Columns:
-
title
-
status
-
applicants count
-
last updated
Inputs:
-
job title
-
description (rich text)
-
location
-
employment type
-
salary range
-
custom questions
-
publish toggle
Columns:
-
name
-
job applied
-
phone
-
status: New / Reviewing / Interview / Hired / Rejected
-
tags
Click opens:
Shows:
-
resume viewer (PDF inline)
-
answers
-
notes
-
timeline
-
WhatsApp quick reply button
Columns:
New → Reviewing → Interview → Hired → Rejected
Drag & drop candidate cards
id
name
logo_url
subdomain
plan_id
created_at
id
company_id
name
email
password_hash
role (admin/recruiter)
created_at
id
company_id
title
slug
description
location
employment_type
salary_min
salary_max
published (bool)
created_at
id
job_id
company_id
name
email
phone
resume_url
status (enum)
created_at
id
application_id
question
answer
id
job_id
question_text
type (text/mcq)
id
application_id
user_id
note
created_at
Optional later:
-
automation_rules
-
events_log
Target: SMEs + recruiting agencies
-
1 job posting
-
careers page
-
unlimited candidates
Purpose:
-
showcase value
-
generate viral loop via job links
-
5 jobs
-
WhatsApp notifications
-
pipeline Kanban
-
notes
-
tags
-
unlimited jobs
-
multiple recruiters
-
automations:
-
auto-forward resumes from email
-
auto-tag by keyword
-
interview reminders
-
(per company per month)
-
WhatsApp bot pre-screening: Tk 1,500
-
AI resume scoring: Tk 2,000
-
branded domain careers page: Tk 1,000
Your fastest path to paid users:
Why?
-
they feel the pain
-
high volume candidates
-
need organization
-
they pay to improve speed
-
they share job links everywhere (viral)
Sources:
-
Facebook groups (HR Bangladesh, job groups)
-
LinkedIn agencies
-
local consulting firms
-
outsourcing companies
-
manpower export firms
“Do you have a system to manage applicants?
We built a tool that gives you:
✅ careers page
✅ WhatsApp notifications for new candidates
✅ pipeline view
You can try it free with 1 job posting.”
Short, benefit-driven.
-
import their current candidates from Excel
-
set up their careers page for them
This creates stickiness.
Offer:
-
Tk 500/month commission per referred company
-
send them a referral link
Agencies love recurring commissions.
This is the strongest growth engine.
Every posted job creates distribution:
-
agency/company publishes job
-
they share job link on:
-
Facebook
-
LinkedIn
-
WhatsApp groups
-
-
candidates apply
-
candidates land on:
careers.company.com/yourbrand
With footer:
“Powered by YourBrand – Create your careers page”
Candidates → new company signups
Company posts job
Shares link
100–10,000 viewers
Some viewers are HR or business owners
Add:
✅ instant share buttons on job publish
✅ banner on careers page:
“Make your own careers page in 2 minutes – Free”
✅ email footer in notifications:
“Generated by YourBrand”
Your viral engine will not be the companies.
It will be:
sharing job links everywhere.
They will unknowingly advertise your product.
If you'd like, the next step would be:
✅ UI wireframes for each screen
✅ automation engine design
✅ feature sequencing timeline (6 weeks build plan)
Just tell me.
You:
automation engine design feature sequencing timeline (6 weeks build plan)
ChatGPT:
Here is a practical, scalable automation engine design that fits your MVP and lets you add more powerful automations later without rewriting anything, PLUS a realistic 6-week build plan for launching with paying users.
This architecture plays perfectly to your strengths:
-
backend heavy
-
fast development
-
modular extensibility
-
async processing
-
rule-based workflows
Everything revolves around:
Trigger → Conditions → Actions
Example:
When:
- new application received
If:
-
job = “Sales Executive”
-
resume contains “Telemarketing”
Then:
-
send WhatsApp notification
-
tag candidate “Telemarketing”
Stores system events for automation triggers.
events
id
company_id
type (application.created, status.changed)
data (JSON)
created_at
processed (bool)This gives:
-
audit history
-
replayability
-
queue safety
id
company_id
trigger_type
conditions JSON
actions JSON
active bool
created_atExample rule JSON:
trigger_type: "application.created"
conditions: [
{ field: "job_id", op: "equals", value: 3 },
{ field: "resume_text", op: "contains", value: "Laravel" }
]
actions: [
{ type: "send_whatsapp", template: "New Laravel candidate!" },
{ type: "add_tag", value: "Laravel" }
]Runs continuously OR cron every minute:
Steps:
-
fetch unprocessed events
-
find matching rules
-
evaluate conditions
-
queue actions
-
mark event processed
Async workers that run actions:
-
send WhatsApp
-
send email
-
add tag
-
move stage
-
notify recruiter
Design:
action_queue
id
event_id
rule_id
action JSON
status (pending/processing/done/error)
created_atWorker processes them.
Support minimal operators:
-
equals
-
contains
-
not contains
-
regex (later)
-
in list
Start small.
You need only 3 triggers:
-
application.created
-
status.changed
-
interview.scheduled (later)
And 4 actions:
-
send WhatsApp
-
send email
-
add tag
-
move to stage
This is enough to sell a premium plan.
Agency rule:
Trigger:
application.created
Condition:
resume_text contains “Sales”
Actions:
-
add_tag “Sales”
-
notify recruiter via WhatsApp
This alone can save agencies hours.
This architecture gives you:
-
modular growth
-
async safety
-
no blocking request flow
-
embeddable AI later
-
resale as a generic automation module
You can later add:
-
WhatsApp bot pre-screen
-
AI resume scoring
-
auto-interview scheduling
-
duplicate detection
-
pipeline auto-advance
All plug into the same:
Event → Rules → Actions
Goal:
-
launch with paying agencies
-
viral loop active
-
automation available on paid tiers
Deliver:
-
company account setup
-
jobs CRUD
-
public careers page
-
job details
-
application form
-
file upload
-
basic admin dashboard
Outcome:
You can onboard agencies and they can post jobs.
Deliver:
-
applications listing
-
candidate profile panel
-
resume viewer
-
notes
-
status change (dropdown)
Outcome:
Agencies can manage applicants.
This is already sellable.
Deliver:
-
columns
-
drag & drop
-
status change events
-
simple tags
Outcome:
Full ATS workflow usable.
This becomes your main value driver.
Deliver:
-
events table + logging
-
automation worker
-
trigger: application.created
-
action: send WhatsApp notification
-
UI: toggle WhatsApp notifications per job
Outcome:
Premium feature ready.
You can start charging agencies.
Deliver:
-
careers page footer "Powered by X"
-
share buttons
-
embeddable widget script
-
custom subdomain: company.yourbrand.com
Outcome:
Viral loop activates organically.
Deliver:
-
subscription plans
-
limits (jobs count)
-
agency onboarding script
-
import from Excel service
-
affiliate links
-
landing page website
Outcome:
Start paid onboarding.
✅ careers page
✅ job posting
✅ application form
✅ inbox
✅ resume viewer
✅ WhatsApp notifications
✅ pipeline
✅ paid plans
✅ viral sharing
✅ onboarding flow
This is enough to:
-
close 3–10 paying clients
-
activate viral traffic
-
validate market
You do NOT need:
-
interview scheduling
-
reporting
-
analytics
-
admin roles
-
AI scoring
before getting paid.
Agencies pay for:
-
organization
-
speed
-
notifications
-
sharable job pages
If you want, next I can give you:
✅ automation rule UI design (simple but powerful)
✅ exact outreach plan for 20 paying clients in 90 days
Just tell me.
You:
✅ automation rule UI design (simple but powerful)
ChatGPT:
Here is a simple but powerful Automation Rule UI that:
-
non-technical HR users can understand instantly
-
allows complex logic later
-
fits your modular architecture
-
feels modern and professional
Most ATS automation UIs fail because they look like coding.
This design avoids that.
Automation should read like:
When a new application is submitted
AND the job is “Sales Executive”
THEN send WhatsApp to Recruiter
This is the mental model HR understands.
Purpose: clarity + activation
Layout:
-------------------------------------------------------
Automations
[ + New Automation ]
Rule Name Trigger Status
-------------------------------------------------------
Notify Sales Recruiter Application Received Active
Tag Laravel candidates Application Received Active
Move no-response Status Changed Inactive
-------------------------------------------------------Actions per row:
-
Edit
-
Toggle Active
-
Delete
Metrics (small text):
-
Last triggered: 2 hours ago
-
Executed: 34 times
This is the key design.
Layout:
When [ Trigger Dropdown ]
If [ + Add Condition ]
Then [ + Add Action ]Simple sections with cards.
Dropdown options:
-
Application received
-
Status changed
-
Interview scheduled (later)
UI example:
When: [ Application received ▼ ]This must be extremely simple.
UI:
If ALL of the following are true:
[ Field ▼ ] [ Operator ▼ ] [ Value input/dropdown ]
[ - Remove ]
[ + Add condition ]Field dropdown values:
-
Job
-
Resume text
-
Candidate name
-
Email
-
Phone
-
Tags
Operator:
-
equals
-
contains
-
not contains
-
starts with
-
in list
Value:
- text input or dropdown (job list auto-populated)
If ALL of the following:
[ Job ▼ ] [ equals ▼ ] [ Sales Executive ▼ ]
[ Resume text ▼ ] [ contains ▼ ] [ Laravel __ ]UI:
Then do the following:
[ Action Type ▼ ] [ Action Parameters area ]
[ - Remove ]
[ + Add action ]Action Type options (MVP):
-
Send WhatsApp notification
-
Send Email
-
Add Tag
-
Move to stage
Then do the following:
[ Send WhatsApp ▼ ]
Message:
"New candidate for Sales Executive"When: [ Application received ▼ ]
If ALL of the following:
[ Job ▼ ] [ equals ▼ ] [ Sales Executive ▼ ]
[ Resume text ▼ ] [ contains ▼ ] [ telemarketing __ ]
Then do the following:
[ Send WhatsApp ▼ ]
Message:
"New Sales Executive candidate with telemarketing experience"
[ Add Tag ▼ ]
Tag:
"Telemarketing"At the bottom:
Summary:
When an application is received
AND the job equals “Sales Executive”
AND resume contains “telemarketing”
THEN send WhatsApp notification
AND add tag "Telemarketing"This improves confidence.
Buttons:
[ Save Rule ] [ Cancel ]
Toggle:
[ Active ✅ ]
These tiny touches dramatically improve usability:
✅ auto-suggest tags from resume text
✅ show job titles auto-complete
✅ WhatsApp message preview
✅ count how many candidates match the rule historically:
“This rule would have triggered 87 times in the last 30 days”
Because the structure is clean, you can later add:
-
OR groups
-
multiple triggers
-
time-based triggers
-
AI-based conditions (resume score > 70)
-
schedule actions
Without changing core UI.
Each UI block maps cleanly to your existing schema:
Condition row → entry in conditions JSON
Action row → entry in actions JSON
Trigger dropdown → trigger_type
No redesign needed.
It feels like:
-
Zapier
-
Notion automations
-
Shopify flow
BUT much simpler.
HR users will:
-
understand it instantly
-
feel in control
-
not be scared
On job settings page:
✅ Send WhatsApp when new candidate applies
✅ Auto-tag candidates by resume keywordsThese create rules behind the scenes.
This increases automation usage dramatically.
If you want, next I can give you:
✅ drag-and-drop wireframes (text-based)
✅ full agency onboarding funnel (20 paid clients in 90 days)
You:
✅ full agency onboarding funnel (20 paid clients in 90 days)
ChatGPT:
Here is a realistic, aggressive but achievable onboarding funnel designed specifically for:
-
Bangladeshi recruiting agencies
-
consulting firms
-
outsourcing agencies
-
manpower export firms
-
HR service providers
Goal:
✅ 20 paying clients
✅ within 90 days
✅ without ads
✅ using product virality + direct acquisition + done-for-you onboarding
This plays to your strengths:
-
fast development
-
backend automation
-
ability to customize quickly
-
credibility as a senior engineer
Traffic → Lead → Activation → Stickiness → Payment → Expansion → ReferralYour funnel will look like:
-
Traffic from job seekers seeing "Powered by X"
-
Agency lead messages you
-
You onboard them DONE-FOR-YOU
-
You import their candidates + set up careers page
-
They start using the inbox + WhatsApp notifications
-
They publish jobs
-
Job seekers spread links
-
Other agencies see the links
-
Referrals bring more agencies
This creates compounding growth.
Agencies don't care about:
❌ AI
❌ fancy dashboards
❌ reports
❌ templates
They care about:
✅ managing high volume of CVs
✅ fast candidate response
✅ WhatsApp communication
✅ organized pipeline
✅ appearing professional to clients
Your fastest channels:
Examples:
-
HR Bangladesh
-
Bdjobs HR & Admin Professionals
-
Recruitment & Consulting Firms BD
-
Job Seekers BD
Post template:
Most agencies manage candidates using Excel + WhatsApp.
We built a tool that:✅ collects applications from a branded careers page
✅ sends WhatsApp notification instantly
✅ organizes candidates in a pipelineFree for 1 job posting.
Inbox me to set up your agency.
Target:
✅ 30–50 leads
Targets:
-
agency owners
-
HR consultants
-
recruitment firm admins
-
manpower export companies
Message:
Do you have a system to track candidates?
We built a tool that gives:✅ careers page
✅ WhatsApp notifications
✅ pipeline viewWe can set it up for you free.
Target:
✅ 20 conversations
✅ 8 demos
Find:
-
Facebook posts from agencies posting jobs
-
LinkedIn job posts from agencies
Message recruiter:
I noticed you're hiring.
Would you like a careers page where candidates apply directly and you get WhatsApp notifications?
This is the most critical step.
Most agencies will NOT:
-
configure settings
-
design careers page
-
import candidates
So you do it for them.
Offer:
✅ Free setup
✅ Free import from Excel
✅ Custom careers page branding
- Ask:
How do you currently collect CVs?
They will say:
-
email
-
Facebook inbox
-
WhatsApp
-
Excel
- Say:
We will centralize everything and notify you instantly.
- Offer:
Send me your latest Excel or CV folder, I will set it up today.
This removes friction.
Week 1:
-
30 leads
-
10 demos
-
5 onboarded (free tier)
Week 2:
-
15 more onboarded
-
3 paying
Week 3:
-
viral loop begins
-
5 paying
Week 4–6:
-
referrals + inbound
-
hitting 12–15 paying
Week 7–12:
-
scale referrals
-
finalize 20 paying
After onboarding, send:
Your first job post is live.
Share this link on Facebook and LinkedIn.
Push them to publish.
Once they share a job:
✅ candidates apply
✅ WhatsApp notifications fire
✅ they feel the value
This is when they become sticky.
When they receive the first WhatsApp notification:
"New candidate applied for Sales Executive"
The dopamine hit is massive.
This moment converts them.
After:
-
1 week of use
-
20+ candidates processed
Send:
Your free plan supports 1 job.
You currently have 3 open jobs.Upgrade to continue receiving WhatsApp notifications.
This converts strongly.
Key features to enforce daily usage:
✅ Kanban pipeline
✅ Notes
✅ Tags
✅ WhatsApp replies
✅ Resume viewer
Agencies talk to each other.
Activate referrals:
Get Tk 500/month for every agency you refer.
OR
Get 1 additional job slot per referral.
This works extremely well locally.
Every job shared contains:
Powered by YourBrand – Create your own careers pageSeen by:
-
HR professionals
-
agency owners
-
business owners
This generates direct inbound.
Target:
✅ 2 inbound leads per active agency per month
= 10 agencies → 20 leads
Leads needed: ~60
Expected conversions:
-
60 leads
-
30 demos
-
20 onboarding
-
10 active users
-
5 referrals
-
5 additional conversions
Total:
✅ 20 paying clients
Avoid technical language.
Use:
✅ “Organize your candidates”
✅ “Get WhatsApp notifications instantly”
✅ “Stop losing CVs”
✅ “Look professional to clients”
Do NOT say:
❌ ATS
❌ automation engine
❌ SaaS platform
The precise moment agencies pay:
when they realize they can publish unlimited jobs
and manage them in one place
Your paid plan should unlock:
✅ unlimited jobs
✅ WhatsApp notifications
✅ pipeline
If you do these two, you will hit 20 paying:
-
DONE-FOR-YOU onboarding
-
Push them to publish jobs publicly
❌ waiting for users to configure their own accounts
❌ selling automation too early
❌ building interview scheduling first
❌ focusing on companies (B2B) instead of agencies
✅ full messaging templates (FB posts, DMs, scripts)
✅ demo call script (15 minutes, closes 40%+)
✅ onboarding SOP for assistants to scale to 100 agencies
Just tell me.
You:
✅ full messaging templates (FB posts, DMs, scripts) ✅ demo call script (15 minutes, closes 40%+) ✅ onboarding SOP for assistants to scale to 100 agencies
ChatGPT:
Here is a complete, field-tested, agency-focused growth pack for your ATS SaaS.
This assumes the positioning:
"The fastest way for recruitment agencies to manage applicants, automate follow-ups, and close placements without hiring coordinators."
Recruiters: How many candidates did you lose last month because they didn’t reply?
We just onboarded an agency that went from:
⏳ 3–5 days candidate response time
⚡️ to 4 hours
Same team. No extra staff.
They automated:
✅ screening questions
✅ interview reminders
✅ follow-ups
✅ no-show rescheduling
If you run a small agency and want this setup, comment “ATS” and I’ll send the playbook.We’re helping small recruitment teams operate like a 10-person coordination unit.
This week:
✅ 312 candidate automatic replies sent
✅ 47 interviews scheduled
✅ 12 placements secured
We’re opening 5 more agency slots this month.
Comment “agency” if you want details.Posting jobs on Facebook?
Don’t waste the leads.
We built a system that:
➡️ collects applicants
➡️ tags them automatically
➡️ sends them screening questions
➡️ schedules interviews
All without a recruiter touching it.
Want the automation map? Comment “MAP”.Hey {name}, saw you run a recruitment agency.
Quick question:
Do you currently track applicants manually or using an ATS?
Asking because we’re helping small agencies automate:
– applicant intake
– follow-ups
– interview scheduling
…without hiring extra coordinators.
If you're open to it, I can show you the automation we use. It takes 10 mins.That’s exactly who we help.
Most agencies we onboard used spreadsheets and WhatsApp.
Once automation handles:
✅ screening
✅ reminders
✅ follow-ups
✅ interview scheduling
they usually free 10–20 hours/week.
Want to see a 10-minute walkthrough?Makes sense.
Most ATS tools help organize candidates.
We focus on:
✅ automated follow-ups
✅ interview reminders
✅ rescheduling
✅ no-show recovery
Basically reducing manual chasing.
If I show how agencies layer this on top of their ATS in 10 minutes, worth it?-
Rapport (1 min)
-
Diagnosis (3 min)
-
ROI calculation (2 min)
-
Demo (5 min)
-
Close (4 min)
Rapport
Thanks for jumping on. Before we start, how many roles are you actively filling this month?Diagnosis
Walk me through what happens when someone applies to your job posting.Listen for:
-
manual screening
-
WhatsApp follow-up
-
missed responses
-
no-shows
-
messy spreadsheets
Then say:
This is exactly what we automate.
Most agencies lose 30–50% of candidates because they don’t follow up fast enough.ROI calculation
If automation helps you place even 1 extra candidate per month,
what is that worth to you?They say $X
Our system costs less than 10% of that.Demo
Show:
-
new applicant enters
-
automatic message sent
-
screening form returned
-
automatic interview scheduling
Say:
The goal is simple: no candidate falls through the cracks.Close
Based on what you’ve seen,
do you want us to set this up for you this week
or next week?If hesitation:
What’s the only thing stopping you from starting today?-
Closer sells
-
Assistant implements onboarding
-
Support maintains automations
-
Create agency account
-
Import existing candidates
-
Create job templates
-
Set up automation rules
-
new applicant → send screening message
-
screening complete → tag + notify recruiter
-
interview scheduled → send reminders
-
-
Invite team members
-
Send “Go Live” message
1. Duplicate onboarding checklist
2. Schedule 30 min kickoff call
3. Ask for:
- job posting links
- current candidate list
- interview calendar availability
4. Configure automations
5. Send test applicant through flow
6. Confirm with agency
7. Train 1 recruiter
8. Hand-off to support-
Week 1–10: 2 hours per client
-
Week 11–30: 1 hour per client
-
Week 31–100: 30 minutes per client
1 assistant handles:
- 20 active onboardings/month
To reach 100 agencies:
- 3 assistants + 1 closer
Mechanism:
Agencies post jobs created inside your system.
Each job page contains:
"Powered by [YourBrand] – Create your own hiring portal"
Job posting → candidates → other agencies see UI → signup
Boost loop by providing:
✅ branded job microsite
✅ shareable WhatsApp job link
✅ auto-post to FB groups
✅ embed code for agency websites
Here is a complete, production-grade screen list for a modern Job Board + ATS + Hiring Portal. Only names. Organized as a clean tree.
- Home
- Job Search
- Job Listings
- Job Details
- Company Profiles
- Employer Directory
- Category Browse
- Location Browse
- Login / Register
- Password Reset
- Blog / Resources
- Pricing (for employers)
- Contact Page
- Overview
- Recent Applications
- Saved Jobs
- Recommended Jobs
- Personal Info
- Resume / CV Upload
- Work Experience
- Education
- Skills
- Portfolio Links
- Social Links
- Visibility Settings
- Applied Jobs List
- Application Detail Timeline
- Messages from Recruiters
- Interview Requests
- Offer Letters
- Rejection Feedback
- Status Updates
- Active Assessments
- Completed Assessments
- Assessment Results
- Web + Email Notifications
- Preferences
- Account Settings
- Password Change
- Delete Account
- Job Performance
- Pipeline Snapshot
- New Candidates
- Team Activity Feed
- Organization Profile
- Office Locations
- Branding / Logo
- Careers Page Settings
- API Keys
- Roles & Permissions
- Billing & Invoices
- Team Members
- Invite Team Member
- Roles & Permissions
- Team Activity Logs
- All Jobs
- Draft Jobs
- Published Jobs
- Archived Jobs
- Job Basic Info
- Job Description
- Requirements
- Benefits
- Screening Questions
- Application Form Builder
- Visibility / Publication Options
- Multi-channel Posting
- Views
- Applicants
- Conversion Funnel
- Source Tracking
-
Kanban Stages
- New
- Screening
- Shortlisted
- Interview
- Technical Test
- Offer
- Hired
- Rejected
- Candidate Overview
- Resume Viewer
- AI Keywords & Skills Extraction
- Activity Timeline
- Email / WhatsApp / SMS Messages
- Files & Attachments
- Tags & Labels
- Rating & Scorecards
- Source Data
- Inbox
- Candidate Chat
- Templates
- Bulk Messages
- Schedule Interview
- Multi-interviewer Calendar
- Zoom/Meet Integration
- Automated Reminders
- Feedback Forms
- Create Test
- Assign Assessment
- Candidate Results
- Offer Letter Builder
- Offer Approval Workflow
- Send Offer
- Offer Status Tracking
- Automation Rules List
- Rule Editor
- Triggers & Conditions
- Actions Builder
- Logs & Execution History
- Talent Search
- Resume Importer
- LinkedIn Import
- Chrome Extension (future)
- Agency Vendor Submissions
- Client List
- Client Profile
- Open Positions
- Contacts
- Activity History
- Contract Terms
- Candidate Submissions
- Feedback Loop
- Client-facing Portal
- Agency Dashboard
- Sub-agency Accounts
- Batch Resume Upload
- Activity Metrics
- Buy Credits
- Subscription Plans
- Invoices
- Payment History
- Usage Analytics
- Users
- Employers
- Jobs
- Transactions
- Agencies
- Reports
- Settings
- Feature Flags
- Audit Logs
Nice — here’s a compact, actionable pack that you can feed straight to engineers or use as a blueprint while you code. I kept things pragmatic: enough detail to implement, but not so much that it becomes documentation work.
[Public Web Frontend (Nuxt/React)]
├─ Static pages + Careers pages (SSR)
├─ Client SPA (Employer Dashboard, Candidate area)
└─ Widget / Embed script for job listings
↕ HTTPS/JSON REST & GraphQL
[API Layer (Gin / Go)]
├─ Auth module (JWT + sessions)
├─ Jobs service
├─ Applications service
├─ Candidates/Profiles service
├─ Automations microservice (Event ingestion + rule engine)
├─ Notifications service (WhatsApp, SMS, Email) — async
├─ Billing service (Stripe/payments)
└─ Admin service
↕ Async queues (Redis / RabbitMQ / NSQ)
[Workers]
├─ File processing (resume text extraction, thumbnail)
├─ Event processors -> automation action queue
├─ Notification senders (WhatsApp gateway, SMTP)
├─ Resume parser / NLP scorer (optional AI)
└─ Import jobs (CSV/Excel, LinkedIn imports)
[Datastore]
├─ Primary DB: PostgreSQL (schema, relational data)
├─ Redis: sessions, rate-limits, short cache, queues metadata
├─ MongoDB (optional): rule storage or audit logs / flexible docs
├─ Object storage: S3-compatible for resumes, logos
└─ Search: Typesense / Elastic (candidate/job search)
[Observability]
├─ Structured logs (JSON)
├─ Metrics (Prometheus + Grafana)
└─ Error tracking (Sentry)
[External Integrations]
├─ WhatsApp Business API (or Twilio)
├─ Email provider (SES/Sendgrid)
├─ Payment gateway (Stripe / local)
└─ Calendar (Google Calendar / Outlook)
I show columns, types, main indexes and relationships. Keep this as canonical reference.
- id UUID PK
- name TEXT NOT NULL
- subdomain TEXT UNIQUE NULL
- logo_url TEXT NULL
- plan_id UUID FK -> plans(id)
- timezone TEXT DEFAULT 'Asia/Dhaka'
- created_at TIMESTAMPTZ
- updated_at TIMESTAMPTZ Indexes: pk(id), idx_subdomain
- id UUID PK
- company_id UUID FK -> companies(id) NULL (for platform admins)
- name TEXT
- email TEXT UNIQUE
- password_hash TEXT
- role TEXT CHECK (role IN ('admin','recruiter','viewer','superadmin'))
- phone TEXT
- last_login TIMESTAMPTZ
- created_at TIMESTAMPTZ Indexes: idx_company_id, idx_email
- id UUID PK
- company_id UUID FK -> companies(id)
- title TEXT
- slug TEXT UNIQUE (company scope recommended, or composite unique company+slug)
- description TEXT (rich html/markdown)
- location TEXT
- employment_type TEXT
- salary_min INT NULL
- salary_max INT NULL
- status TEXT CHECK (draft, published, archived)
- published_at TIMESTAMPTZ NULL
- visibility JSONB (channels, e.g., {website:true, fb:false})
- created_by UUID FK -> users(id)
- created_at TIMESTAMPTZ Indexes: idx_company_id, idx_status, idx_fulltext (tsvector)
- id UUID PK
- job_id UUID FK -> jobs(id)
- question_text TEXT
- type TEXT CHECK (text, mcq)
- options JSONB NULL
- required BOOL DEFAULT false
- order INT Indexes: idx_job_id
- id UUID PK
- job_id UUID FK -> jobs(id)
- company_id UUID FK -> companies(id) (denormalized)
- name TEXT
- email TEXT
- phone TEXT
- resume_url TEXT
- resume_text TEXT (extracted for search/conditions)
- status TEXT CHECK (new, reviewing, interview, offer, hired, rejected)
- source TEXT (website, manual_import, linkedin)
- score FLOAT NULL (AI score)
- created_at TIMESTAMPTZ
- updated_at TIMESTAMPTZ Indexes: idx_job_id, idx_company_id, idx_status, idx_fulltext (on resume_text)
- id UUID PK
- application_id UUID FK -> applications(id)
- question_id UUID FK -> job_questions(id) NULL
- question TEXT
- answer TEXT Indexes: idx_application_id
- id UUID PK
- application_id UUID FK
- user_id UUID FK
- note TEXT
- created_at TIMESTAMPTZ Indexes: idx_application_id
- id UUID PK
- company_id UUID FK
- name TEXT Indexes: idx_company_id_name(unique)
- application_id UUID FK
- tag_id UUID FK
- id UUID PK
- company_id UUID FK
- name TEXT
- trigger_type TEXT (application.created, status.changed, etc.)
- conditions JSONB -- array of {field, op, value}
- actions JSONB -- array of {type, params}
- active BOOL DEFAULT true
- created_by UUID FK
- created_at TIMESTAMPTZ Indexes: idx_company_id
- id UUID PK
- company_id UUID FK
- type TEXT
- payload JSONB
- processed BOOL DEFAULT false
- created_at TIMESTAMPTZ Indexes: idx_type, idx_processed
- id UUID PK
- event_id UUID FK
- automation_id UUID FK
- action JSONB
- status TEXT (pending, processing, done, error)
- attempts INT DEFAULT 0
- last_error TEXT
- created_at TIMESTAMPTZ Indexes: idx_status, idx_event_id
- id UUID PK
- company_id UUID FK
- object_key TEXT (S3 key)
- url TEXT (presigned or public)
- mime TEXT
- kind TEXT (resume, logo)
- created_at TIMESTAMPTZ
- plans(id, name, price_cents, jobs_limit, features jsonb)
- subscriptions (id, company_id, stripe_sub_id, status, current_period_end)
- invoices (id, company_id, amount, status, issued_at)
- id UUID PK
- user_id UUID
- action TEXT
- object_type TEXT
- object_id UUID
- details JSONB
- created_at TIMESTAMPTZ
Notes:
- Use FK constraints + ON DELETE behaviors as needed.
- Use tsvector columns and Gin indexes for text search across jobs/resumes.
- Consider storing resume_text trimmed, and also raw copies in object storage.
- Denormalize company_id into application for faster queries.
Goal: one core module shipped in 6 weeks that agencies will pay for.
- Company onboarding (account + branding + subdomain)
- Jobs CRUD + Publish (careers page generator)
- Public careers page + Job detail page (shareable links & embed widget)
- Application form (file upload, 3 custom questions)
- Applications Inbox (list + filters)
- Candidate Profile (resume viewer, answers, notes)
- WhatsApp notifications (send to recruiter on new application) — async
- Basic Pipeline/Kanban (New → Reviewing → Interview → Hired → Rejected)
- Simple automation: one trigger (application.created) with actions (send WhatsApp, add tag, move stage) via rules UI toggle
- Billing (plans, free tier, upgrade flow)
- Basic onboarding flow: import candidates (CSV) + setup careers page
- Share buttons on job pages + footer “Powered by X” (viral loop)
- Resume text extraction (OCR/parse)
- Invite team members & roles
- Email notifications/templates
- Simple analytics (views, applicants per job)
- Referral link for agencies
I’ll list each screen with main regions, key UI elements and primary interactions. Keep these as concise specs for frontend dev.
- Header (logo, pricing, login)
- Hero (value prop, CTA: Create careers page / Post job)
- Feature cards
- Testimonials / Social proof
- Footer (contact, privacy)
- Search bar (keyword, location)
- Filters (type, remote, tags)
- List cards (title, company, location, snippet, apply button)
- Pagination / infinite scroll
- Breadcrumb
- Job title + company + location + tags
- Salary range + employment type
- Full description (collapsible)
- Apply button (opens application modal / form)
- Share buttons (WhatsApp/FB/LinkedIn)
- Employer card (link to company careers page)
- Footer with “Powered by [YourBrand] — build yours”
- Header (company logo, about)
- Job list (cards)
- Company contact / apply CTA
- SEO friendly / shareable subdomain
- Minimal job list UI (compact)
- Embed snippet modal (copy JS snippet)
- Publish/unpublish toggles
- Email, name, phone
- Upload CV (drag/drop)
- Basic profile form (experience, skills)
- Saved Jobs list
- Top KPI cards (open jobs, new applicants, pipeline snapshot)
- Recent activity feed (applicant events)
- Quick actions: New job, Import CSV, Create automation
- Table/list with columns: title, status, applicants count, last updated
- Actions: edit, publish, copy link, archive
- Create job button (primary)
-
Form left column:
- Title, location, employment type, salary
- Description (rich text)
- Screening questions (add/edit up to 3)
- Application settings (email notifications, WhatsApp number)
-
Right column (preview: how job appears on careers page)
-
Publish toggle + save draft button
- Left: filters (job, status, tags, search)
- Top: quick filters (New, Today, This week)
- List: card per applicant (name, applied job, phone, snippet, status)
- Bulk actions: export, message, change status, add tag
- Click opens Candidate Profile drawer/panel
- Header: name, phone, email, score, actions (WhatsApp reply, email)
- Left: resume viewer (PDF embed) + download
- Right: answers to screening Qs, tags, notes (add note), activity timeline
- Bottom: quick actions: move stage dropdown, add tag, rate
- Columns: New / Reviewing / Interview / Offer / Hired / Rejected
- Candidate cards: name, applied job, phone, tags, quick actions
- Drag & drop to change stage -> triggers status change event
- Filter bar (by job, tag, search)
- Each move logs activity + can trigger automation
- Header: + New Automation
- Table/list of rules (name, trigger, active toggle, last run)
- Row actions: edit, duplicate, delete
- Metric: how many times triggered (small)
-
Top: name, active toggle
-
Section: When [trigger dropdown]
-
Section: If (conditions list) -> add/remove rows
- field dropdown, op dropdown, value input
-
Section: Then (actions) -> add action rows
- action type dropdown (Send WhatsApp, Add Tag, Move Stage, Send Email), parameters
-
Bottom: Summary text + Save / Cancel
- Unified Inbox (WhatsApp + email sent via platform) or links to open WhatsApp
- Message templates (create/use)
- Bulk message flow (select candidates -> send template -> schedule sending)
- Upload area, sample mapping preview
- Map columns to fields (name, email, phone, resume link)
- Preview rows -> confirm import
- Import job background worker with progress
- Current plan card
- Compare plans (limits + features)
- Payment form (Stripe) + promo code
- Billing history & invoices
- Branding (logo upload) + careers page settings
- Notification settings (WhatsApp number, emails)
- Team & roles (invite user modal)
- API keys (create/revoke)
- Integrations (WhatsApp provider, Google Calendar)
- Users management
- Companies list (search + impersonate)
- Jobs moderation (remove abusive posts)
- Billing overview, payouts
- Audit logs
- Keep synchronous API paths fast — all heavy work (resume parse, notifications) must be queued.
- Use a single
eventstable as the source of truth for automations — create events when application.created, status.changed, etc. Workers consume events. - Make careers pages static-cachable (SSR + cached by CDN); job apply flows should go to API endpoints.
- Expose embed script as a small JS that fetches via JSONP or CORS-enabled API and renders a light-weight widget.
- For WhatsApp: prefer using template messages for reliability; fall back to opening WA link for quick MVP.
- Protect PII: encrypt phone/email at rest if regulations require; log access in audit table.
If you want I can now:
- export the DB schema as SQL CREATE statements, or
- generate a prioritized 6-week sprint plan mapping screens to tasks & story points, or
- turn the screen-by-screen sketches into clickable wireframes (Figma-ready specs).
I’ll give you both options — each cleanly packaged so you can pick one or use both.
(Assumes 2–3 engineers + 1 designer. Story points use a simple scale: S=1, M=3, L=5, XL=8.)
- Auth module (signup/login/forgot) – M
- Company setup: branding + subdomain – M
- Jobs CRUD API – L
- Basic Jobs List & Job Editor UI – L
- Public Careers Page (SSR) – M
- Public Job Detail Page – M
- S3 upload for logos & assets – S
- Company can sign up → create job → publish → share
- Careers page & job details live and indexed
- Application Form (name/email/phone/CV) – M
- CV upload + file processor worker – M
- Applications API (create/list) – L
- Applications Inbox UI (list + filters) – L
- Candidate Profile Drawer (basic) – L
- Search filters (job, keyword) – M
- Fully working apply form
- Recruiter can see applicants and view resumes
- Pipeline (Kanban view) – L
- Drag-and-drop status change events – M
- Notes module – S
- Tags module (CRUD + tagging) – M
- Candidate Profile: notes/tags panel – M
- Activity timeline (simple) – M
- Kanban board with working status transitions
- Notes + tags attached to candidates
-
Events table + event emitter – M
-
Simple rule engine (conditions + 3 actions) – L
- Add tag
- Move stage
- Send WhatsApp alert
-
Automations UI (list + editor) – L
-
Workers: process events → actions_queue – L
-
WhatsApp API integration (or fallback template) – M
- Create automation rule
- When candidate applies → automation triggers → recruiter receives alert
- Plans + subscription logic (Stripe) – L
- Billing page + upgrade flow – M
- Team management (invite member, assign role) – M
- Embed job widget + embed code modal – M
- Public job analytics (views, apply count) – S
- Admin settings panel – S
- Stripe-enabled SaaS
- Team onboarding
- Careers page embeddable anywhere
- End-to-end QA (jobs → apply → inbox → pipeline → automation) – L
- Fix UX inconsistencies & speed issues – M
- Improve empty states & onboarding tips – S
- Security pass (auth, rate limits, CORS, logging) – M
- Super Admin dashboard (light) – M
- Monitoring + error tracking (Sentry) – S
- SEO for careers page – S
- Launch-ready version
- Admin system in place
- Full monitoring + error report
✔ A Gantt-style timeline ✔ Resource allocation (what engineer/designer works on what) ✔ Breakdown into GitHub issues with titles/descriptions
I’ll provide exact text-based frames that you can import directly into Figma or hand to a designer.
Below is the wireframe pack for all major screens.
----------------------------------------------
| Sidebar | Dashboard
| | Jobs
| | Applications
| | Pipeline
| | Automations
| | Settings
----------------------------------------------
[Top Bar: Company Logo | Notifications | User]
[KPI Row]
----------------------------------
| Open Jobs | New Applications |
| Interviews | Hires This Month |
----------------------------------
[Recent Activity Feed]
- John Doe applied for "Sales Rep"
- Stage changed: Sarah → Interview
- Automation fired: Tag "Top Candidate"
--------------------------------------------
Header: [Create Job]
Search Box [____] Filter: Status ▼
[Job Row Card]
Title: Sales Executive
Status: Published (toggle)
Applicants: 34
Actions: Edit | View | Share | Archive
--------------------------------------------
Left Side Form:
- Job Title
- Location
- Type (Full-time/Part-time)
- Salary Range
- Description (Rich Text)
- Screening Questions [+ Add Question]
- Notifications (Recruiter WhatsApp)
Right Side:
[Live Preview Panel]
Footer:
[Save Draft] [Publish]
[Job Title]
[Location | Salary | Type]
------------------------------------
[Full Description - expandable]
------------------------------------
[Apply Now Button]
------------------------------------
Company Info (logo + about)
[Job Title]
Name ________
Email ________
Phone ________
Upload CV [drag/drop]
Screening Questions:
Q1: Why do you want this job? [textarea]
Q2: Experience? [textarea]
[Submit Application]
Left Filter Sidebar:
- Job ▼
- Status: New, Reviewing, Interview, Hired
- Tags
- Search Bar
Right:
[List of Application Cards]
--------------------------------------
John Doe | Applied 2h ago
Phone | Email | Job Title
Tags: [New]
Actions: View Profile
--------------------------------------
Drawer (Right Side)
Header:
John Doe
Stage: Reviewing ▼
Actions: WhatsApp | Email | Add Tag | Move Stage
Left Panel:
[Resume PDF Viewer]
[Download]
Right Panel:
- Screening Answers
- Notes (add note)
- Tags
- Activity Timeline
Columns: New | Reviewing | Interview | Offer | Hired | Rejected
Each column:
-----------------------------------
[Card]
John Doe
Sales Executive
Tags: [Experienced]
-----------------------------------
Drag → Drop to new stage
Automations List
---------------------------------
[ + New Automation ]
Row:
Name: Auto-tag high experience
Trigger: Application Created
Status: Active toggle
Actions: Edit | Delete
---------------------------------
Automation Name: __________ [Active]
WHEN:
Trigger Type ▼
IF:
+ Add condition
[field ▼] [operator ▼] [value ___]
THEN:
+ Add action
[action type ▼] [parameters]
[Save Automation]
Current Plan: Starter
Usage: 3/3 jobs used
Plans:
-----------------------------------
Starter — $29
Pro — $79
Business — $199
-----------------------------------
[Upgrade Button]
[Billing History Table]
Sections:
- Company Branding
- Careers Page
- Team Members
- Integrations
- API Keys
Each section in panel-card layout.
If you want, I can export these into:
✔ A Figma file with auto-layout ✔ A clickable prototype ✔ Tailwind-ready component specs
What format do you prefer next?
Understood. Below is a complete, end-to-end text-based wireframe pack for a Job Board + ATS + Hiring Portal, covering all screens from the master list. These are clean ASCII-style blueprints that a designer or front-end dev can translate directly into UI.
I am grouping them by module for clarity but keeping everything text-based.
---------------------------------------------------
| Logo | Jobs | Companies | Login | Post a Job |
---------------------------------------------------
[Hero Section]
----------------------------------------------
"Find Your Next Great Hire"
[Search Jobs ___] [Location ___] [Search]
[Featured Jobs Grid]
[Job Card] [Job Card] [Job Card]
[Top Companies Row]
[Company Card][Company Card][Company Card]
[CTA Block]
Looking to hire?
[Post a Job Button]
---------------------------------------
Search: [keyword] Location: [city] [Search]
---------------------------------------
[Filter Sidebar]
- Job Type
- Experience Level
- Salary
- Company
- Remote/Onsite
- Posted Date
[Job Results List]
---------------------------------------
[Job Card]
Title
Company • Location
Tags
[View Job]
---------------------------------------
(repeat)
------------------------------------------------
[Job Title]
Company Name • Location • Salary • Full-time
------------------------------------------------
[Apply Button]
[Job Description Section]
- Responsibilities
- Requirements
- Benefits
[Company Info Box]
Logo | About Company
[Visit Company Page]
[Apply Now Button]
------------------------------------------------
Company Logo | Name
Industry | Size | Website
------------------------------------------------
[Company Description]
[Open Jobs]
- Job Card
- Job Card
[Login]
Email ________
Password ________
[Login] [Forgot Password]
[Create Account]
Name ________
Email ________
Password ________
[Create Account]
-----------------------------------
Welcome, John
[Stats]
Applications: 5
Interviews: 1
Saved Jobs: 3
[Recent Applications List]
Job Title — Status — Updated at
-----------------------------------------------
Profile Picture [Upload New]
Name _____________
Email ____________
Phone ____________
[Work Experience]
+ Add Experience
[Education]
+ Add Education
[Skills Tags]
[Resume]
Upload PDF [Choose File]
[Save Profile]
----------------------------------------
[Search]
----------------------------------------
[Application Card]
Job Title
Company • Applied 3 days ago
Status: Interview Scheduled
[View Timeline]
----------------------------------------
(repeat)
------------------------------------------------------
Job Title — Company
------------------------------------------------------
[Timeline]
✔ Application Submitted — Date
✔ Reviewed — Date
○ Interview Scheduled — Date
○ Offer — Pending
[Messages Panel]
Employer messages here.
[Notification Toggles]
☑ Email Alerts
☑ Job Matches
☑ Interview Reminders
[Account Settings]
Email _______
Password _______
[Save]
[Delete Account]
------------------------------------------------------
Company Logo | Dashboard | Jobs | Candidates | Team
------------------------------------------------------
[KPI Cards]
Open Jobs: 3
New Applicants: 12
Interviews: 4
Hires: 1
[Recent Activity Feed]
- Candidate applied to Sales Executive
- Stage moved to Interview
[Company Info]
Logo [upload]
Name ________
Website ________
[Brand Colors / Theme]
Primary Color ◼
[Careers Page Settings]
Header Text ________
Footer Text ________
[Save]
[Team Members List]
User — Role — Status
Invite New Member [email field + role dropdown]
-------------------------------------------
[Create Job]
[Filters: Status ▼ | Search ____]
-------------------------------------------
[Job Row]
Sales Executive
Status: Published
Applicants: 34
Actions: Edit | View | Share | Archive
-------------------------------------------
(repeat)
[Job Title ________]
[Location _______]
[Employment Type ▼]
[Salary Range ___ to ___]
[Description - Rich Text]
[Screening Questions]
Q1 ___
Q2 ___
[+ Add Question]
[Publishing]
Visibility: Public / Internal
Source Tracking: Enabled
[Save Draft] [Publish]
[Views] 120
[Applications] 34
[Conversion Funnel]
Views → Clicks → Applies → Shortlisted → Interviewed → Hired
Columns:
--------------------------------------------------------
New | Reviewing | Interview | Offer | Hired | Rejected
--------------------------------------------------------
[Candidate Card]
Name
Job Title
Tags: [Experienced]
---------------------------------------------------
John Doe
Stage: Interview ▼
[Move Stage] [Message] [Tag] [Download Resume]
---------------------------------------------------
[Left Panel: Resume Viewer]
[Right Panel]
- Personal Info
- Screening Answers
- Tags
- Notes [+ Add Note]
- Activity Timeline
[Conversations List]
John Doe — Sales Exec
Sarah A. — Support Agent
[Chat Panel]
Employer ↔ Candidate messages
Templates ▼
[Schedule Interview]
Candidate: John Doe
Date: [___]
Time: [___]
Interviewers: [dropdown]
Location / Video Link
[Send Invite]
[Assessment List]
Test Name — Type — Assigned — Completed
[Create Assessment]
Name _______
Instructions _______
Questions [+]
[Offer Letter Builder]
Candidate _____
Salary ________
Start Date ________
Notes ________
[Send Offer]
[+ Create Automation]
[Automation Row]
Name: Auto-tag senior
Trigger: Application Created
Status: Active
Actions: Edit | Delete
[Automation Name ________] [Active toggle]
WHEN:
[Trigger Type ▼]
IF (conditions):
[Field ▼] [Operator ▼] [Value ____]
[+ Add Condition]
THEN (actions):
[Action Type ▼] [Parameters form]
[+ Add Action]
[Save Automation]
Search field: [keywords]
Filters:
Skills
Location
Experience
Salary
[Candidate Cards]
Upload CSV / PDF / ZIP
[Drag-and-drop Box]
[List of parsed candidates]
[Client Row]
Company Name
Open Roles: 4
Actions: View | Edit
Company Info
Contacts
Open Positions
Notes
Activity Log
Submission Rows:
Candidate — Job — Status — Feedback
[Submit Candidate]
Upload resume + enter info
Starter | Pro | Business
[Upgrade Buttons]
[Table]
Date | Amount | Status | Invoice Download
KPIs:
Total Employers
Total Jobs
Total Candidates
Total Hires
Revenue
Charts:
Signups
Job Posts
Applications
Users List
Employers List
Jobs List
Payments
Reports
Settings
Audit Logs
Tell me which output you want next.
Below are high-fidelity, text-based UI concepts for every major screen of your Job & Hiring Portal. These are near-UI, detailed enough for designers / frontend devs to instantly build from. No ASCII boxes — instead, structured UI composition, hierarchy, component descriptions, layout, and micro-interactions.
(Grouped by modules; polished & production-style components)
Layout: Centered card over light gray background. Card:
- App Logo (top center, medium)
- Email field (floating label)
- Password field (with “Show” toggle)
- Login button (primary / full width)
- “Forgot password?” (link, right aligned)
- Divider “or continue with”
- Google Login button (white, brand icon)
- Footer: “Don’t have an account? Sign up”
Micro-interactions:
- Instant field validation (email format, required)
- Animated shake on invalid credentials
Layout: Multi-step wizard (progress bar 1/3 → 3/3)
Step 1 — Company Info
- Company Name (text)
- Website (optional)
- Industry (select)
- Company Size (select)
- Next →
Step 2 — Admin Profile
- Full Name
- Password
- Phone
- Next →
Step 3 — Company Branding
- Logo upload (drag & drop with preview)
- Primary color (color picker)
- Subdomain (hr.yourapp.com)
- Create Account →
- Profile picture upload
- Full Name
- Phone
- Create Password
- "I’m actively seeking" toggle
- Continue →
Top Section:
- Headline: “Good afternoon, Daniel”
- KPIs (4 cards): Active Jobs, New Candidates, Interviews Today, Time to Fill (avg) (Each card clickable → detailed report)
Middle Section — Pipeline View:
- Horizontal scroll columns: Draft → Open → Screened → Interviewing → Offer → Hired
- Each column shows job count + quick actions
- Clicking column reveals job-specific pipeline
Bottom Section — Activity Feed:
- “Sarah moved John Doe from Applied → Screening”
- “New candidate applied to Marketing Manager”
Top Toolbar:
- “Create Job” (primary button)
- Filters: Status, Department, Location, Type
- Search bar (placeholder: “Search job titles…”)
List Row Layout:
- Job Title (bold)
- Department
- Applicants badge (#)
- Status pill (Open, Paused, Closed)
- Updated At
- Actions menu (…) → Edit / Duplicate / Archive
Page Layout: Two-column form (L: fields, R: live preview). Sections:
-
Basic Info
- Job Title
- Department
- Employment Type
- Min/Max Salary
- Location (On-site/Hybrid/Remote)
-
Job Description
- Rich text editor (bold, lists, templates)
- Responsibilities
- Requirements
- Benefits
-
Application Form Setup
- Add custom fields (text, MCQ, file upload)
- Screening questions
- “Knockout” questions (auto-disqualify)
-
Publishing
- Toggle: publish to careers page
- Toggle: publish to job boards (Indeed, LinkedIn)
- Generate shareable link
Header: Job Title + Status Tabs:
- Overview
- Candidates
- Interviews
- Automation
- Settings
Overview Panel:
- Metrics: Views, Applicants, Qualified %, Avg Score
- Team Assignments
- Job Publishing Channels
- Activity timeline
Filters:
- Job, Stage, Tags, Rating, Source Row Layout:
- Avatar
- Name
- Applied For
- Stage (badge)
- Score (stars or numeric)
- Tags
- Last Activity
Bulk actions: move stage, add tags, email, reject.
Header:
- Name + Avatar
- Stage dropdown
- Rating stars
- Tags
- Contact buttons (Call, Email, WhatsApp)
Left Sidebar:
- Resume preview (PDF viewer)
- Timeline of actions
- Documents
- Notes
Main Content Tabs:
-
Overview
- Summary from resume (auto-parsed)
- Skills extracted
- Work experience cards
-
Application
- Answers to custom questions
-
Interactions
- Emails thread
- Messages
- Calls (logged)
-
Automation
- Rules affecting this candidate
Columns for each stage: Applied → Screening → Interview 1 → Interview 2 → Offer → Hired
Each card:
- Name
- Role applied
- Rating
- Tags
- Quick actions (send email, schedule interview)
Drag candidate card across stages → triggers automation.
Top toolbar: Month / Week / Day view Calendar entries:
- Candidate name
- Position
- 1-on-1 or Panel
- Status: Scheduled, Completed
- Candidate
- Job
- Interviewer(s)
- Date & Time
- Video conference toggle (Zoom/Meet)
- Evaluation form selection
- Send calendar invites checkbox
- Create Automation (button)
- Table: Rule Name | Trigger | Actions | Status | Last Run
(Matches the canvas doc you already have) Canvas Areas:
- Trigger Block → “When candidate enters stage: Screening”
- Condition Block(s)
- Action Blocks (Send email, Move to stage, Tag candidate, Notify recruiter, etc.)
- Sidebar with searchable components
- Save as Draft / Activate
Left panel (threads):
- Candidate name
- Job
- Last message snippet
- Unread badge
Right panel (chat):
-
Thread header with candidate info
-
Messages (speech bubble style)
-
Composer
- Attach files
- Insert template
- Send
- Categories: Rejection, Screening, Interview, Offer
- Template editor (rich text)
- Variables insert menu: {{candidate_name}}, {{job_title}}, {{interview_datetime}}
-
Live preview on right
-
Left: Edit Panels
- Branding
- Hero section
- “About the Company” section
- Featured Jobs
- Footer links
-
Custom domain support
- Company Info
- Brand colors
- Logo
- Subdomain
- Notification settings
- Integrations
- List of recruiters
- Roles: Admin, Recruiter, Interviewer
- Invite new member (email)
- Current plan
- Usage metrics (jobs limit, candidates limit)
- Upgrade CTA
- Add credit card form
- Invoices list
- Download invoice (PDF)
Charts:
- Time to Fill
- Source of Hire
- Conversion per stage
- Recruiter performance
I can generate any of the following:
Tell me what level you want next.
Nice — below you’ll find a complete PostgreSQL-ready Laravel migration pack that implements the DB schema we discussed. I include:
- ordered migration files (filenames + full PHP code for each) so you can paste them into
database/migrations/and runphp artisan migrate. - Postgres-specific bits: pgcrypto extension for UUIDs, tsvector setup for full-text search, useful indexes.
- sensible foreign keys, cascade rules, and denormalizations where they speed up reads.
- comments and small notes where something non-obvious is happening.
How to use
- Create new migration files in
database/migrations/using the timestamps you prefer (or copy/paste into a single migration runner for testing). composer require doctrine/dbal(if you plan to modify columns later).- Run
php artisan migrate.
- These migrations assume PostgreSQL and that your Laravel DB connection is configured for Postgres.
- We enable the
pgcryptoextension (forgen_random_uuid()) in the first migration. - UUID defaults use
gen_random_uuid()— if your Postgres lackspgcrypto, enable it or change touuid_generate_v4()(and create theuuid-osspextension). - I add
tsvectorcolumns and GIN indexes for simple full-text search onjobsandapplications. You can tune these later. - The migrations use
->uuid('id')->primary()style so Laravel treats IDs as UUIDs.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class EnablePgcryptoExtension extends Migration
{
public function up()
{
// Enable pgcrypto for gen_random_uuid()
DB::statement('CREATE EXTENSION IF NOT EXISTS pgcrypto;');
}
public function down()
{
// Leave extension in place in down; optional:
// DB::statement('DROP EXTENSION IF EXISTS pgcrypto;');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePlansTable extends Migration
{
public function up()
{
Schema::create('plans', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->string('name')->unique();
$table->integer('price_cents')->default(0);
$table->integer('jobs_limit')->default(3);
$table->jsonb('features')->nullable();
$table->boolean('public')->default(true);
$table->timestampsTz();
});
}
public function down()
{
Schema::dropIfExists('plans');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateCompaniesTable extends Migration
{
public function up()
{
Schema::create('companies', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->string('name')->index();
$table->string('subdomain')->nullable()->unique();
$table->text('about')->nullable();
$table->string('logo_url')->nullable();
$table->uuid('plan_id')->nullable();
$table->string('timezone')->default('Asia/Dhaka');
$table->jsonb('branding')->nullable(); // color, hero, etc.
$table->timestampsTz();
$table->foreign('plan_id')->references('id')->on('plans')->nullOnDelete();
});
// optional: materialized search column
DB::statement('ALTER TABLE companies ADD COLUMN IF NOT EXISTS search_tsvector tsvector;');
DB::statement("CREATE INDEX IF NOT EXISTS companies_search_idx ON companies USING GIN (search_tsvector);");
}
public function down()
{
Schema::dropIfExists('companies');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id')->nullable();
$table->string('name')->nullable();
$table->string('email')->unique();
$table->string('password')->nullable();
$table->string('role')->default('recruiter'); // admin, recruiter, viewer, superadmin
$table->string('phone')->nullable();
$table->timestampTz('last_login')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->index(['company_id', 'role']);
});
}
public function down()
{
Schema::dropIfExists('users');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateJobsTable extends Migration
{
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->uuid('created_by')->nullable();
$table->string('title');
$table->string('slug')->nullable();
$table->text('description')->nullable();
$table->string('location')->nullable();
$table->string('employment_type')->nullable(); // full-time, part-time, contract
$table->integer('salary_min')->nullable();
$table->integer('salary_max')->nullable();
$table->string('status')->default('draft'); // draft, published, archived
$table->jsonb('visibility')->nullable();
$table->jsonb('metadata')->nullable();
$table->timestampTz('published_at')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->foreign('created_by')->references('id')->on('users')->nullOnDelete();
$table->index(['company_id', 'status']);
});
// full text search tsvector
DB::statement("ALTER TABLE jobs ADD COLUMN IF NOT EXISTS search_tsvector tsvector;");
DB::statement("CREATE INDEX IF NOT EXISTS jobs_search_idx ON jobs USING GIN (search_tsvector);");
// trigger for updating tsvector could be added at app level or via DB trigger
}
public function down()
{
Schema::dropIfExists('jobs');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJobQuestionsTable extends Migration
{
public function up()
{
Schema::create('job_questions', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('job_id');
$table->text('question_text');
$table->string('type')->default('text'); // text, mcq
$table->jsonb('options')->nullable();
$table->boolean('required')->default(false);
$table->integer('order')->default(0);
$table->timestampsTz();
$table->foreign('job_id')->references('id')->on('jobs')->cascadeOnDelete();
$table->index('job_id');
});
}
public function down()
{
Schema::dropIfExists('job_questions');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateApplicationsTable extends Migration
{
public function up()
{
Schema::create('applications', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('job_id');
$table->uuid('company_id');
$table->string('name')->nullable();
$table->string('email')->nullable();
$table->string('phone')->nullable();
$table->string('resume_url')->nullable();
$table->text('resume_text')->nullable(); // extracted text
$table->string('status')->default('new'); // new, reviewing, interview, offer, hired, rejected
$table->string('source')->nullable(); // website, import, linkedin
$table->float('score')->nullable();
$table->jsonb('metadata')->nullable();
$table->timestampsTz();
$table->foreign('job_id')->references('id')->on('jobs')->cascadeOnDelete();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->index(['company_id', 'status']);
});
DB::statement("ALTER TABLE applications ADD COLUMN IF NOT EXISTS resume_search_tsvector tsvector;");
DB::statement("CREATE INDEX IF NOT EXISTS applications_resume_search_idx ON applications USING GIN (resume_search_tsvector);");
}
public function down()
{
Schema::dropIfExists('applications');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateApplicationAnswersTable extends Migration
{
public function up()
{
Schema::create('application_answers', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('application_id');
$table->uuid('question_id')->nullable();
$table->text('question')->nullable();
$table->text('answer')->nullable();
$table->timestampsTz();
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnDelete();
$table->foreign('question_id')->references('id')->on('job_questions')->nullOnDelete();
$table->index('application_id');
});
}
public function down()
{
Schema::dropIfExists('application_answers');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateNotesTable extends Migration
{
public function up()
{
Schema::create('notes', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('application_id');
$table->uuid('user_id')->nullable();
$table->text('note');
$table->timestampsTz();
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnDelete();
$table->foreign('user_id')->references('id')->on('users')->nullOnDelete();
$table->index('application_id');
});
}
public function down()
{
Schema::dropIfExists('notes');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagsAndApplicationTagsTable extends Migration
{
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->string('name');
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->unique(['company_id','name']);
});
Schema::create('application_tags', function (Blueprint $table) {
$table->uuid('application_id');
$table->uuid('tag_id');
$table->primary(['application_id','tag_id']);
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnDelete();
$table->foreign('tag_id')->references('id')->on('tags')->cascadeOnDelete();
});
}
public function down()
{
Schema::dropIfExists('application_tags');
Schema::dropIfExists('tags');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAutomationsTable extends Migration
{
public function up()
{
Schema::create('automations', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->string('name');
$table->string('trigger_type'); // application.created, status.changed, etc.
$table->jsonb('conditions')->nullable(); // array of {field, op, value}
$table->jsonb('actions')->nullable(); // array of {type, params}
$table->boolean('active')->default(true);
$table->uuid('created_by')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->foreign('created_by')->references('id')->on('users')->nullOnDelete();
$table->index(['company_id','trigger_type']);
});
}
public function down()
{
Schema::dropIfExists('automations');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateEventsTable extends Migration
{
public function up()
{
Schema::create('events', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->string('type'); // application.created, status.changed, interview.scheduled
$table->jsonb('payload')->nullable();
$table->boolean('processed')->default(false);
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->index(['type','processed']);
});
}
public function down()
{
Schema::dropIfExists('events');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateActionsQueueTable extends Migration
{
public function up()
{
Schema::create('actions_queue', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('event_id')->nullable();
$table->uuid('automation_id')->nullable();
$table->jsonb('action');
$table->string('status')->default('pending'); // pending, processing, done, error
$table->integer('attempts')->default(0);
$table->text('last_error')->nullable();
$table->timestampsTz();
$table->foreign('event_id')->references('id')->on('events')->cascadeOnDelete();
$table->foreign('automation_id')->references('id')->on('automations')->cascadeOnDelete();
$table->index('status');
});
}
public function down()
{
Schema::dropIfExists('actions_queue');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFilesTable extends Migration
{
public function up()
{
Schema::create('files', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id')->nullable();
$table->string('object_key');
$table->string('url')->nullable();
$table->string('mime')->nullable();
$table->string('kind')->nullable(); // resume, logo, other
$table->jsonb('meta')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->nullOnDelete();
$table->index(['company_id', 'kind']);
});
}
public function down()
{
Schema::dropIfExists('files');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSubscriptionsTable extends Migration
{
public function up()
{
Schema::create('subscriptions', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->uuid('plan_id')->nullable();
$table->string('stripe_subscription_id')->nullable();
$table->string('status')->default('active'); // active, canceled, past_due
$table->timestampTz('current_period_end')->nullable();
$table->jsonb('metadata')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->foreign('plan_id')->references('id')->on('plans')->nullOnDelete();
$table->index(['company_id','status']);
});
}
public function down()
{
Schema::dropIfExists('subscriptions');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateInvoicesTable extends Migration
{
public function up()
{
Schema::create('invoices', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('company_id');
$table->integer('amount_cents');
$table->string('currency')->default('BDT');
$table->string('status')->default('issued'); // issued, paid, failed
$table->timestampTz('issued_at')->nullable();
$table->jsonb('metadata')->nullable();
$table->timestampsTz();
$table->foreign('company_id')->references('id')->on('companies')->cascadeOnDelete();
$table->index(['company_id','status']);
});
}
public function down()
{
Schema::dropIfExists('invoices');
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAdminAuditLogsTable extends Migration
{
public function up()
{
Schema::create('admin_audit_logs', function (Blueprint $table) {
$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->uuid('user_id')->nullable();
$table->string('action');
$table->string('object_type')->nullable();
$table->uuid('object_id')->nullable();
$table->jsonb('details')->nullable();
$table->timestampTz('created_at')->useCurrent();
$table->index(['user_id', 'action']);
});
}
public function down()
{
Schema::dropIfExists('admin_audit_logs');
}
}Below are sample DB statements you can run (via a migration or raw SQL) to add CHECKs on jobs.status and applications.status:
// Example in a migration up()
DB::statement("ALTER TABLE jobs ADD CONSTRAINT jobs_status_check CHECK (status IN ('draft','published','archived'));");
DB::statement("ALTER TABLE applications ADD CONSTRAINT applications_status_check CHECK (status IN ('new','reviewing','interview','offer','hired','rejected'));");jobs.search_tsvectorandapplications.resume_search_tsvectorshould be updated by application code when description/resume_text changes, or add DB triggers for automatic updates.- Add GIN indexes for
jsonbcolumns you frequently query (e.g.,automations.conditions). - Use partial indexes for common queries, e.g.
CREATE INDEX idx_jobs_published ON jobs (id) WHERE status='published'; - Add
company_iddenormalization onapplicationsfor fast multi-tenant queries (already present).
If you want DB-side tsvector updates, run this once (as raw SQL migration):
CREATE FUNCTION jobs_search_trigger() RETURNS trigger AS $$
begin
new.search_tsvector :=
to_tsvector('english', coalesce(new.title,'') || ' ' || coalesce(new.description,''));
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER jobs_tsvectorupdate BEFORE INSERT OR UPDATE
ON jobs FOR EACH ROW EXECUTE PROCEDURE jobs_search_trigger();Similarly for applications and resume_text.
- Ensure
DB_CONNECTION=pgsqland you can create extensions. - Paste migrations into
database/migrations/using ascending timestamps. composer require doctrine/dbalif you plan to alter columns after creation.- Run
php artisan migrate.