diff --git a/public/mpm-logo-doc.png b/public/mpm-logo-doc.png new file mode 100644 index 0000000..50017d8 Binary files /dev/null and b/public/mpm-logo-doc.png differ diff --git a/public/mpm-logo.png b/public/mpm-logo.png new file mode 100644 index 0000000..b3dd38f Binary files /dev/null and b/public/mpm-logo.png differ diff --git a/src/app/globals.css b/src/app/globals.css index fce668a..11d2626 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,46 +1,81 @@ @import "tailwindcss"; +/* + * Message Point Media brand palette. + * Gold system (Middle / Light / Dark Gold) on a Dark Shade charcoal ground, + * with Light Shade text. Montserrat headings, Open Sans body. + */ :root { - --bg-dark: #0f172a; - --bg-card: rgba(30, 41, 59, 0.7); - --accent-primary: #38bdf8; - --accent-secondary: #818cf8; - --text-main: #f1f5f9; - --text-dim: #94a3b8; - --glass-border: rgba(255, 255, 255, 0.1); - --glass-highlight: rgba(255, 255, 255, 0.05); + /* Brand core */ + --mpm-gold-mid: #dcbb4f; /* Middle Gold — default accent */ + --mpm-gold-light: #f5cd15; /* Light Gold — accents on dark backgrounds */ + --mpm-gold-dark: #998643; /* Dark Gold — titles on light backgrounds */ + --mpm-shade-dark: #232022; /* Dark Shade — near-black ground */ + --mpm-shade-light: #f5f1ec;/* Light Shade — off-white text */ + --mpm-accent: #849698; /* Light Accent — steel gray, secondary text */ + + /* Semantic tokens used across the app */ + --bg-dark: var(--mpm-shade-dark); + --bg-card: rgba(40, 37, 39, 0.72); + --accent-primary: var(--mpm-gold-mid); + --accent-secondary: var(--mpm-gold-light); + --text-main: var(--mpm-shade-light); + --text-dim: var(--mpm-accent); + --glass-border: rgba(220, 187, 79, 0.18); + --glass-highlight: rgba(220, 187, 79, 0.08); +} + +/* Expose brand tokens as Tailwind v4 theme colors so utilities like + text-accent-primary / text-text-dim / from-accent-primary resolve. */ +@theme inline { + --color-bg-dark: var(--bg-dark); + --color-accent-primary: var(--accent-primary); + --color-accent-secondary: var(--accent-secondary); + --color-text-main: var(--text-main); + --color-text-dim: var(--text-dim); + --color-gold-dark: var(--mpm-gold-dark); + --font-sans: var(--font-open-sans), ui-sans-serif, system-ui, sans-serif; + --font-heading: var(--font-montserrat), var(--font-open-sans), sans-serif; } body { - background: radial-gradient(circle at top left, #1e293b, #0f172a); + background: radial-gradient(circle at top left, #2f2b2d, var(--mpm-shade-dark)); color: var(--text-main); min-height: 100vh; + font-family: var(--font-open-sans), ui-sans-serif, system-ui, sans-serif; +} + +/* Montserrat carries all headings — weight, not just size, sets hierarchy. */ +h1, h2, h3, h4 { + font-family: var(--font-montserrat), var(--font-open-sans), sans-serif; } .glass-card { background: var(--bg-card); backdrop-filter: blur(12px); border: 1px solid var(--glass-border); - box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); + box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.45); border-radius: 1rem; } .glass-input { - background: rgba(15, 23, 42, 0.5); + background: rgba(35, 32, 34, 0.5); border: 1px solid var(--glass-border); - color: white; + color: var(--text-main); transition: all 0.2s ease; } .glass-input:focus { border-color: var(--accent-primary); - box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.2); + box-shadow: 0 0 0 2px rgba(220, 187, 79, 0.25); outline: none; } +/* Gold gradient echoes the MPM logo mark (light gold → dark gold). + Dark Shade text keeps the label readable on the gold fill. */ .btn-primary { - background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); - color: white; + background: linear-gradient(135deg, var(--mpm-gold-light), var(--mpm-gold-dark)); + color: var(--mpm-shade-dark); font-weight: 600; padding: 0.75rem 1.5rem; border-radius: 0.75rem; @@ -49,20 +84,20 @@ body { .btn-primary:hover { transform: translateY(-2px); - opacity: 0.9; + opacity: 0.92; } .btn-secondary { background: var(--glass-highlight); border: 1px solid var(--glass-border); - color: white; + color: var(--text-main); padding: 0.75rem 1.5rem; border-radius: 0.75rem; transition: all 0.2s ease; } .btn-secondary:hover { - background: rgba(255, 255, 255, 0.1); + background: rgba(220, 187, 79, 0.14); } /* Animations */ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4e722e3..4e13506 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,13 +1,23 @@ import type { Metadata } from "next"; -import { Inter } from "next/font/google"; +import { Montserrat, Open_Sans } from "next/font/google"; import "./globals.css"; import { AuthProvider } from "@/components/providers/AuthProvider"; -const inter = Inter({ subsets: ["latin"] }); +// MPM brand typography: Montserrat for headings, Open Sans for body. +const montserrat = Montserrat({ + subsets: ["latin"], + weight: ["500", "600", "700", "800"], + variable: "--font-montserrat", +}); + +const openSans = Open_Sans({ + subsets: ["latin"], + variable: "--font-open-sans", +}); export const metadata: Metadata = { - title: "WFH Daily Report", - description: "Sleek and modern work from home reporting tool", + title: "WFH Daily Report | Message Point Media", + description: "Daily work-from-home reporting for Message Point Media. Born to Innovate. Built to Last.", }; export default function RootLayout({ @@ -17,7 +27,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/src/components/ReportForm.tsx b/src/components/ReportForm.tsx index 3242533..e0c8dad 100644 --- a/src/components/ReportForm.tsx +++ b/src/components/ReportForm.tsx @@ -193,13 +193,16 @@ export default function ReportForm() { return (
-

- WFH Tracker + {/* eslint-disable-next-line @next/next/no-img-element */} + Message Point Media +

+ WFH Daily Report

Please sign in with your company Google account to access your daily reports.

+

Born to Innovate. Built to Last.

); @@ -208,11 +211,15 @@ export default function ReportForm() { return (
-
-

{view === "REPORT" ? "Daily Report" : "Administration"}

-

- {new Date().toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })} -

+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + Message Point Media +
+

{view === "REPORT" ? "Daily Report" : "Administration"}

+

+ {new Date().toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })} +

+
{session.user.role === "ADMIN" && ( @@ -337,9 +344,9 @@ export default function ReportForm() { onChange={(e) => updateTask(task.id, { status: e.target.value as TaskStatus, type: 'COMPLETED' })} className="bg-transparent text-sm text-text-dim border-none p-0 focus:ring-0 outline-none appearance-none cursor-pointer" > - - - + + +
diff --git a/src/lib/google-drive.ts b/src/lib/google-drive.ts index 7cbe0a8..932c2f3 100644 --- a/src/lib/google-drive.ts +++ b/src/lib/google-drive.ts @@ -1,8 +1,28 @@ import { google, Auth } from 'googleapis'; import { Readable } from 'stream'; +import { readFileSync } from 'fs'; +import path from 'path'; import { prisma } from './prisma'; import type { Prisma } from '@prisma/client'; +// The MPM logo is embedded directly into the report HTML as a base64 data URI +// so it renders regardless of network reachability (e.g. a LAN-only deployment +// where Google's importer can't fetch an external URL). Read from disk once and +// cache; `null` means the asset was missing and the report renders without it. +let cachedLogoDataUri: string | null | undefined; +function getLogoDataUri(): string | null { + if (cachedLogoDataUri !== undefined) return cachedLogoDataUri; + try { + const logoPath = path.join(process.cwd(), 'public', 'mpm-logo-doc.png'); + const b64 = readFileSync(logoPath).toString('base64'); + cachedLogoDataUri = `data:image/png;base64,${b64}`; + } catch (error) { + console.error('MPM report logo could not be loaded; rendering without it:', error); + cachedLogoDataUri = null; + } + return cachedLogoDataUri; +} + type ReportWithRelations = Prisma.ReportGetPayload<{ include: { tasks: true; user: true }; }>; @@ -127,21 +147,41 @@ export function generateReportHTML(report: ReportWithRelations) { const plannedTasks = report.tasks.filter((t) => t.type === 'PLANNED'); const completedTasks = report.tasks.filter((t) => t.type === 'COMPLETED'); - const cellStyle = "padding: 10px; border-bottom: 1px solid #e2e8f0; font-family: Arial, sans-serif; font-size: 11pt;"; - const headerStyle = "padding: 12px 10px; background-color: #f1f5f9; border-bottom: 2px solid #cbd5e1; font-family: Arial, sans-serif; font-size: 11pt; font-weight: bold; text-align: left; color: #334155;"; + // MPM brand palette + const GOLD_DARK = '#998643'; // titles on light backgrounds + const GOLD_MID = '#DCBB4F'; // accent lines, dividers + const SHADE_DARK = '#232022'; // body text / table header background + const SHADE_LIGHT = '#F5F1EC'; // section panels / table header text + const OFF_WHITE = '#FAF7F2'; // alternating table rows + const ACCENT = '#849698'; // secondary / muted text + + const bodyFont = "'Open Sans', Arial, sans-serif"; + const headingFont = "'Montserrat', 'Open Sans', Arial, sans-serif"; + + const cellStyle = `padding: 10px; border-bottom: 1px solid #E7E0D5; font-family: ${bodyFont}; font-size: 11pt; color: ${SHADE_DARK};`; + const headerStyle = `padding: 12px 10px; background-color: ${SHADE_DARK}; font-family: ${headingFont}; font-size: 11pt; font-weight: 600; text-align: left; color: ${SHADE_LIGHT};`; + + // Embed the logo inline so it survives conversion without a reachable host. + const logoDataUri = getLogoDataUri(); + const logoHtml = logoDataUri + ? `Message Point Media` + : ''; + + const rowBg = (i: number) => (i % 2 === 1 ? ` background-color: ${OFF_WHITE};` : ''); return ` - -

WFH Daily Report

- -
+ + ${logoHtml} +

WFH Daily Report

+ +

Date: ${dateStr}

Employee: ${escapeHtml(report.user.name)}

Manager: ${escapeHtml(report.managerName || 'N/A')}

-

Planned Tasks

+

Planned Tasks

${plannedTasks.length > 0 ? ` @@ -149,17 +189,17 @@ export function generateReportHTML(report: ReportWithRelations) { - ${plannedTasks.map((t) => ` + ${plannedTasks.map((t, i) => ` - - - + + + `).join('')}
Estimate Notes
${escapeHtml(t.description)}${escapeHtml(t.timeEstimate || '-')}${escapeHtml(t.notes || '-')}${escapeHtml(t.description)}${escapeHtml(t.timeEstimate || '-')}${escapeHtml(t.notes || '-')}
- ` : `

No planned tasks for today.

`} + ` : `

No planned tasks for today.

`} -

Completed Tasks

+

Completed Tasks

${completedTasks.length > 0 ? ` @@ -167,21 +207,22 @@ export function generateReportHTML(report: ReportWithRelations) { - ${completedTasks.map((t) => { + ${completedTasks.map((t, i) => { const link = safeLink(t.link); return ` - - - + + + `; }).join('')}
Status Work Link
${escapeHtml(t.description)}${escapeHtml(t.status || 'Done')}${link ? `${escapeHtml(link)}` : '-'}${escapeHtml(t.description)}${escapeHtml(t.status || 'Done')}${link ? `${escapeHtml(link)}` : '-'}
- ` : `

No completed tasks reported today.

`} - -
- Generated automatically by WFH App + ` : `

No completed tasks reported today.

`} + +
+

Born to Innovate. Built to Last.

+

Message Point Media · Generated automatically by the WFH Daily Report app