Skip to content

Repository files navigation

Org CMS - Company Management System

A modern company management dashboard for managing events, opportunities, and company profiles.

Features

  • Company Management - Add, claim, edit, and delete company profiles
  • User Authentication - Secure login via external auth system
  • Company Profile - Full editable profile (name, description, logo, location, website, email, industry, country, tags, SEO settings)
  • Breadcrumb Navigation - Navigation breadcrumbs on all pages for easy access to previous pages
  • Publish/Unpublish - Control company visibility (draft/published)
  • Events Management - Create, edit, delete events with full details
  • Opportunities Management - Create, edit, delete jobs, internships, grants, tenders
  • Applications - View and filter applications for opportunities
  • Admin Mode - Admins can view all companies in the system
  • Company Claim System - Users can request to claim companies with approval workflow
  • Responsive Design - Works on desktop and mobile devices

Tech Stack

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript
  • Styling: Tailwind CSS
  • Database: Supabase (PostgreSQL)
  • Image Storage: R2 via uploads worker (served from assets.techinika.com)
  • Icons: Lucide React

Project Structure

org-cms/
├── app/
│   ├── [slug]/
│   │   ├── page.tsx                     # Company main page with menu cards
│   │   ├── profile/page.tsx            # Edit company profile
│   │   ├── events/
│   │   │   ├── page.tsx               # List company events
│   │   │   ├── new/page.tsx             # Create new event
│   │   │   └── [event-id]/
│   │   │       ├── page.tsx              # Event menu hub
│   │   │       ├── edit/page.tsx        # Edit event details
│   │   │       ├── speakers/page.tsx     # Manage speakers
│   │   │       ├── agenda/page.tsx       # Manage sessions
│   │   │       ├── partners/page.tsx    # Manage partners/sponsors
│   │   │       ├── registrations/page.tsx # View registrations
│   │   │       └── stats/page.tsx       # View statistics
│   │   └── opportunities/
│   │       ├── page.tsx                # List opportunities
│   │       ├── new/page.tsx            # Create new opportunity
│   │       └── [opp-id]/page.tsx      # Opportunity detail with applications
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx                     # Homepage (company list)
├── components/
│   ├── pages/
│   │   └── Home.tsx                 # Main dashboard
│   ├── parts/
│   │   ├── Navbar.tsx               # Top navigation bar
│   │   ├── Breadcrumb.tsx           # Breadcrumb navigation
│   │   └── CreateCompanyModal.tsx  # Add/claim company modal
│   ├── ui/
│   │   ├── Toast.tsx                # Toast notifications
│   │   └── ConfirmationModal.tsx   # Confirmation dialogs
│   ├── events/
│   │   └── EventCard.tsx           # Event list item
│   └── opportunities/
│       ├── ApplicationList.tsx      # Paginated application list
│       ├── ApplicationDetailModal.tsx # Application detail view
│       ├── FeedbackModal.tsx          # Email feedback modal
│       └── AIScoreModal.tsx          # AI comparison results
├── lib/
│   ├── worker.ts                    # Main api-worker client (CRUD, session auth)
│   ├── ai.ts                        # AI applicant comparison (via /api/ai/compare proxy)
│   ├── auth-client.ts              # Client-side auth utilities
├── types/
│   └── company.ts                # TypeScript types
├── .env                           # Environment variables
├── package.json
└── tsconfig.json

Getting Started

Prerequisites

  • Node.js 18+
  • npm or yarn

Installation

npm install

Environment Variables

Create a .env.local file with:

# Backend Workers (email, uploads, AI are proxied to shared Cloudflare Workers)
NEXT_PUBLIC_WORKER_API_URL=http://localhost:8787
NEXT_PUBLIC_AI_WORKER_URL=http://localhost:8788
NEXT_PUBLIC_COMMS_WORKER_URL=http://localhost:8789
NEXT_PUBLIC_UPLOADS_WORKER_URL=http://localhost:8790
WORKER_API_KEY=

# Auth (external auth app — auth-platform)
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_BASE_URL=http://localhost:3002

Note: org-cms has no direct Supabase access — all data flows through the api-worker. No Supabase keys are required here.

Development

npm run dev

Open http://localhost:3001 in your browser.

Database Schema

The schema is owned by the api-worker (migrations in techinika-workers/api-worker). The supabase/migrations folder in this repo mirrors it for reference.

featured_startups

  • id (uuid, primary key)
  • name (text, required)
  • logo_url (text)
  • image_ref (uuid, references assets.id)
  • description (text)
  • industry (text)
  • location (text)
  • website (text)
  • email (text)
  • country (text)
  • slug (varchar)
  • claimed (boolean) - Set to true when someone requests to claim the company
  • is_featured (boolean)
  • is_published (boolean)
  • seo_title (text)
  • seo_description (text)
  • tags (text)
  • lang (text)
  • created_at, updated_at (timestamps)

assets

  • id (uuid, primary key)
  • url (text, required)
  • file_name (text)
  • file_type (text)
  • file_size (numeric)
  • width (numeric)
  • height (numeric)
  • created_at, updated_at (timestamps)

user_company

  • id (uuid, primary key)
  • user_id (uuid, references auth.users & authors)
  • company_id (uuid, references featured_startups)
  • role (creator | manager | employee)
  • status (confirmation_pending | accepted | rejected | active)
  • added_by (uuid)
  • created_at (timestamp)

events

  • id (uuid, primary key)
  • title (text)
  • slug (text, unique)
  • location (text)
  • format (Online | In-Person | Hybrid)
  • status (Upcoming | Ongoing | Completed | Cancelled)
  • publish_status (draft | published)
  • start_date, end_date (timestamps)
  • organizer_id (uuid, references featured_startups)
  • seo_description, full_description (text)
  • tags, external_link (text)
  • views, is_featured (numeric/boolean)
  • lang (text)

opportunities

  • id (uuid, primary key)
  • title (text)
  • slug (text, unique)
  • type (Job | Tender | Grant | Internship | Other)
  • location (text)
  • salary, application_link, contact_email (text)
  • work_mode (Remote | On-Site | Hybrid)
  • employment_type (Full-Time | Part-Time | Contract | Internship)
  • status (Open | Closed | Draft)
  • company_id (uuid, references featured_startups)
  • expires_at (timestamp)
  • views, featured (numeric/boolean)

applications

  • id (uuid, primary key)
  • opportunity_id (uuid, references opportunities)
  • name, email, phone, location (text)
  • resume_url, cover_letter (text)
  • supporting_docs, portfolio_links (text[])
  • created_at (timestamp)

Key Features

Adding a Company

  1. Click "Add Company" button
  2. Search for existing company by name
  3. If found, click to claim (creates user_company record)
  4. If not found, create new with name, description, logo

Claiming a Company

  • When claiming, a user_company record is created with status "confirmation_pending"
  • The featured_startups.claimed field is set to true
  • Admin approval required - status changes to "accepted" after admin approval
  • If rejected, user sees "Request Rejected" notice
  • If pending confirmation, user sees "Awaiting Approval" notice

Company Claim Status Flow

  1. No claim requested: User sees "Claim Company" button
  2. Company already claimed (featured_startups.claimed = true, no user_company): User sees "Already Claimed" notice, cannot claim
  3. Pending confirmation (user_company.status = "confirmation_pending"): User sees "Awaiting Approval" notice
  4. Accepted/Active (user_company.status = "accepted" or "active"): User can access the company
  5. Rejected (user_company.status = "rejected"): User sees "Request Rejected" notice

Company Management

  • Edit company profile at /[slug]/profile (name, description, logo, location, website, email, industry, country, tags)
  • SEO settings (title, description)
  • Publish/Unpublish toggle with confirmation modal
  • Delete company with confirmation modal
  • Manage company at /[slug] - Three menu cards: Profile, Events, Opportunities

Events

  • List events with status badges
  • Create new event at /[slug]/events/new
  • Edit event at /[slug]/events/[event-id]
  • Delete event from menu

Opportunities

  • List opportunities with type badges
  • Create new opportunity at /[slug]/opportunities/new
  • Edit opportunity at /[slug]/opportunities/[opp-id]
  • Rich text editor for full description, requirements, and benefits (Tiptap WYSIWYG)
  • View applications with filters (All/Pending/In Review/Interview/Tech Exam/Contract/Hired/Rejected)
  • Pagination supports up to 1000+ applications
  • Individual application modal with full details (name, email, phone, location, cover letter, files)
  • File preview links for resume, supporting docs, and proposals (opens in browser)
  • Application progress workflow: In Review → Interview Pending → Technical Exam → Contract Signing → Hired
  • Rejection flow with customizable email message
  • AI Applicant Comparison: Click "AI Compare" button to rank applicants by compatibility score (0-100%)
    • Uses Gemini via the ai-worker (proxied through /api/ai/compare)
    • Shows ranked list with scores and reasoning
  • Email Notifications: When updating application status, personalized email is sent to the applicant via the comms worker (proxied through /api/send-email)
  • Delete opportunity from menu

Application Link

  • To use the built-in application form (instead of external link), set application_link to "apply"
  • This will make the opportunity accept applications through the CMS form

Security

  • Session-guarded proxies: /api/send-email, /api/upload-image, and /api/ai/compare verify the session server-side (checkAuthStatusServer() in lib/auth-server.ts) and return 401 when unauthenticated.
  • Sanitized HTML: FAQ answers are rendered through the whitelist sanitizer in lib/sanitize.ts (strips on* handlers and blocks javascript:/vbscript:/data: URIs).
  • Security headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection, plus poweredByHeader: false in next.config.ts.
  • No Supabase keys ship in this app — all data access is server-side via the api-worker.

Environment Variables

Email (sent via comms worker / Amazon SES)

Transactional email (application status updates) is sent through the comms worker from no-reply@techinika.com. support@techinika.com / editorial@techinika.com are user-facing contact addresses only.

NEXT_PUBLIC_COMMS_WORKER_URL=http://localhost:8789
WORKER_API_KEY=

Navigation Routes

  • Homepage: / - List of user's companies with claim status
  • Company page: /[slug] - Company with three menu cards (Profile, Events, Opportunities)
  • Profile page: /[slug]/profile - Edit company profile and delete company
  • Events page: /[slug]/events - List company events, create new event
  • Opportunities page: /[slug]/opportunities - List company opportunities, create new opportunity
  • Event detail: /[slug]/events/[event-id]
  • Opportunity detail: /[slug]/opportunities/[opp-id]
  • Create event: /[slug]/events/new
  • Create opportunity: /[slug]/opportunities/new
  • Sign out: Redirects to auth app

Code Conventions

Dynamic Route Params

In Next.js 16, dynamic route params are passed as Promises. Always unwrap with use():

import { use } from "react";

export default function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = use(params);
  // ...
}

License

MIT

About

CMS to manage company events and opportunities

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages