Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Deploy project site to GitHub Pages

on:
push:
branches: [main]
paths:
- "apps/marketing/**"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "package.json"
- ".github/workflows/pages.yml"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 11.24.0

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build static project site
run: pnpm --filter marketing build
env:
PAGES_BASE_PATH: /slotsyncro

- name: Configure Pages
uses: actions/configure-pages@v5

- name: Upload static site
uses: actions/upload-pages-artifact@v4
with:
path: apps/marketing/out

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@

The maintained product design, feature inventory, repository architecture, and documentation roadmap are available in the [design documentation](./docs/README.md).

[![Test Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen)](https://techaaroorian.github.io/slotsyncro/)
[View the SlotSyncro project site](https://techaaroorian.github.io/slotsyncro/)

---

## πŸš€ Live Interactive Test Coverage Report
## πŸ“Έ Product Preview

The unit test suite achieves full coverage using **Vitest** and **happy-dom**. The interactive HTML coverage report is automatically updated and deployed via GitHub Actions on every push to `main`:
### Connected scheduling dashboard

πŸ‘‰ **[View Live Coverage Report](https://techaaroorian.github.io/slotsyncro/)**
[![SlotSyncro dashboard showing scheduling totals and a completed readiness checklist](./apps/marketing/public/screenshots/dashboard.png)](./apps/marketing/public/screenshots/dashboard.png)

### Create and share a group poll

[![Create Poll page with poll details, quick time choices, and an explanation panel](./apps/marketing/public/screenshots/create-poll.png)](./apps/marketing/public/screenshots/create-poll.png)

### Collect availability publicly

[![Public SlotSyncro poll showing group availability and an accountless participant response form](./apps/marketing/public/screenshots/voting-poll.png)](./apps/marketing/public/screenshots/voting-poll.png)

---

Expand All @@ -26,14 +34,16 @@ This project is structured as a **Turborepo** workspace using `pnpm`:
```text
slotsyncro/
β”œβ”€β”€ apps/
β”‚ └── app/ # Main Next.js App Router application
β”‚ β”œβ”€β”€ app/ # Main Next.js App Router application
β”‚ └── marketing/ # Static project site deployed to GitHub Pages
β”œβ”€β”€ packages/
β”‚ β”œβ”€β”€ db/ # Prisma ORM schema, client export, and DB scripts (@repo/db)
β”‚ β”œβ”€β”€ eslint-config/ # Shared ESLint configuration
β”‚ └── typescript-config/ # Shared TypeScript configuration
β”œβ”€β”€ .github/
β”‚ └── workflows/
β”‚ └── coverage.yml # CI/CD test coverage & GitHub Pages deploy workflow
β”‚ β”œβ”€β”€ coverage.yml # Test coverage and PR reporting
β”‚ └── pages.yml # Marketing-site GitHub Pages deployment
β”œβ”€β”€ package.json # Root pnpm workspace scripts
└── pnpm-workspace.yaml

Expand All @@ -59,7 +69,7 @@ slotsyncro/
### Prerequisites

* **Node.js:** v20+
* **Package Manager:** `pnpm` v9+
* **Package Manager:** `pnpm` v11.24.0

### Environment & Setup

Expand Down Expand Up @@ -87,7 +97,7 @@ Set up your environment variables for local development:
1. **Generate Prisma Client:**

```bash
pnpm --filter @repo/db db:generate
pnpm --filter db db:generate

```

Expand All @@ -98,7 +108,7 @@ pnpm dev

```

Open [http://localhost:3000](http://localhost:3000) in your browser.
Open the marketing site at [http://localhost:3000](http://localhost:3000) and the product application at [http://localhost:3001](http://localhost:3001).

---

Expand All @@ -118,7 +128,7 @@ pnpm test
### CI/CD Workflow

* **Pull Requests:** GitHub Actions runs `vitest run --coverage`, generates Prisma client artifacts with a CI connection fallback, and posts a line-by-line coverage breakdown as a PR comment.
* **Main Branch:** Automatically builds and deploys the generated Vitest HTML report to **GitHub Pages**.
* **Main Branch:** Builds and deploys the static SlotSyncro project site to **GitHub Pages** when marketing files change.

---

Expand Down
4 changes: 2 additions & 2 deletions apps/app/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: "SlotSyncro πŸ—“οΈ",
title: "SlotSyncro",
description:
"Universal group scheduling & real-time availability heatmap builder",
};
Expand All @@ -42,7 +42,7 @@ export default async function LocaleLayout({
return (
<html lang={locale} suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased min-h-full flex flex-col bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100 transition-colors`}
className={`${geistSans.variable} ${geistMono.variable} min-h-full flex flex-col bg-background text-foreground transition-colors`}
>
<NextIntlClientProvider messages={messages}>
<ThemeProvider>
Expand Down
Binary file removed apps/app/app/favicon.ico
Binary file not shown.
96 changes: 55 additions & 41 deletions apps/app/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-brand: var(--brand);
--color-brand-accent: var(--brand-accent);

--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
Expand All @@ -52,77 +54,85 @@

/* Light Mode Variables */
:root {
--background: oklch(0.99 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--background: #f7f5ed;
--foreground: #17221d;
--card: #fffefa;
--card-foreground: #17221d;
--popover: #fffefa;
--popover-foreground: #17221d;
--primary: #1e6646;
--primary-foreground: #ffffff;
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--accent: #fbe9e3;
--accent-foreground: #6f2b1e;
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--ring: #cf5f3f;
--brand: #1e6646;
--brand-accent: #e97855;
--page-glow-primary: rgb(201 239 132 / 34%);
--page-glow-accent: rgb(233 120 85 / 12%);
--radius: 0.625rem;

--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-1: #e97855;
--chart-2: #1e6646;
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);

--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar: #f2f3ea;
--sidebar-foreground: #17221d;
--sidebar-primary: #1e6646;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #e6f2eb;
--sidebar-accent-foreground: #174c36;
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--sidebar-ring: #cf5f3f;
}

/* Dark Mode Variables */
.dark {
--background: oklch(0.12 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.16 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.16 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.145 0 0);
--background: #08110d;
--foreground: #f1f5f2;
--card: #101713;
--card-foreground: #f1f5f2;
--popover: #101713;
--popover-foreground: #f1f5f2;
--primary: #72d09d;
--primary-foreground: #10271d;
--secondary: oklch(0.22 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.22 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.22 0 0);
--accent-foreground: oklch(0.985 0 0);
--accent: #432a23;
--accent-foreground: #ffb49c;
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 12%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--ring: #ff997a;
--brand: #72d09d;
--brand-accent: #ff997a;
--page-glow-primary: rgb(30 102 70 / 24%);
--page-glow-accent: rgb(233 120 85 / 8%);

--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-1: #ff997a;
--chart-2: #72d09d;
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);

--sidebar: oklch(0.16 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.22 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar: #0b120e;
--sidebar-foreground: #f1f5f2;
--sidebar-primary: #72d09d;
--sidebar-primary-foreground: #10271d;
--sidebar-accent: #20382d;
--sidebar-accent-foreground: #d8f5e4;
--sidebar-border: oklch(1 0 0 / 12%);
--sidebar-ring: oklch(0.556 0 0);
--sidebar-ring: #ff997a;
}

/* Base resets */
Expand All @@ -132,5 +142,9 @@
}
body {
@apply bg-background text-foreground font-sans antialiased;
background-image:
radial-gradient(circle at 12% 4%, var(--page-glow-primary), transparent 30rem),
radial-gradient(circle at 88% 12%, var(--page-glow-accent), transparent 28rem);
background-attachment: fixed;
}
}
}
6 changes: 6 additions & 0 deletions apps/app/app/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions apps/app/components/auth-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { signIn, signOut } from "@/auth";

export function SignIn() {
return (
<div className="flex w-full flex-col gap-2 sm:flex-row">
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row">
<form
className="w-full"
className="w-full sm:w-auto"
action={async () => {
"use server";
await signIn("google");
}}
>
<button
type="submit"
className="flex w-full items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white px-5 py-3 text-sm font-medium text-gray-800 shadow-sm transition-all hover:bg-gray-50 active:scale-95 dark:border-gray-600 dark:bg-gray-900 dark:text-white dark:hover:bg-gray-800"
className="flex h-11 w-full items-center justify-center gap-2 whitespace-nowrap rounded-lg border border-gray-300 bg-white px-4 text-sm font-medium text-gray-800 shadow-sm transition-all hover:bg-gray-50 active:scale-95 sm:min-w-52 dark:border-gray-600 dark:bg-gray-900 dark:text-white dark:hover:bg-gray-800"
>
<svg
aria-hidden="true"
Expand Down Expand Up @@ -43,15 +43,15 @@ export function SignIn() {
</form>

<form
className="w-full"
className="w-full sm:w-auto"
action={async () => {
"use server";
await signIn("github");
}}
>
<button
type="submit"
className="flex w-full items-center justify-center gap-2 rounded-lg bg-gray-900 px-5 py-3 text-sm font-medium text-white shadow-md transition-all hover:bg-gray-800 active:scale-95 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100"
className="flex h-11 w-full items-center justify-center gap-2 whitespace-nowrap rounded-lg bg-gray-900 px-4 text-sm font-medium text-white shadow-md transition-all hover:bg-gray-800 active:scale-95 sm:min-w-52 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100"
>
<svg
aria-hidden="true"
Expand Down
2 changes: 1 addition & 1 deletion apps/app/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function Header() {
<Link href={`/${locale}`} className="flex items-center space-x-2 group">
<Logo className="w-7 h-7 group-hover:scale-105 transition-transform" />
<span className="text-xl font-black tracking-tight group-hover:text-primary transition-colors">
SlotSyncro πŸ—“οΈ
SlotSyncro
</span>
<Badge
variant="outline"
Expand Down
Loading
Loading