Generate a fully functional, multi-page Next.js MVP application for 'Salary Shield'. This application will help users understand and control how their personal data might be used by employers to influence salary offers. The goal is to empower users with information and control during salary negotiations.
PROJECT OVERVIEW:
Salary Shield aims to solve the problem of employers exploiting users' publicly available personal data to determine the lowest possible salary they will accept. The application will allow users to create a profile of their digital footprint, analyze the potential impact of this data on salary negotiations, control the visibility of certain data points, and get an estimate of a fair salary range based on their profile and market data. The core value proposition is to level the playing field in salary negotiations by demystifying data usage and empowering individuals with knowledge and control.
TECH STACK:
- Frontend Framework: Next.js (App Router)
- Styling: Tailwind CSS
- ORM: Drizzle ORM (PostgreSQL compatible)
- Database: PostgreSQL (or a compatible cloud-based solution like Supabase/Neon)
- Authentication: NextAuth.js (with credentials and OAuth providers like Google/GitHub)
- UI Component Library: shadcn/ui (for clean, accessible, and customizable components)
- Form Handling: React Hook Form with Zod for validation
- State Management: Zustand or React Context API for global state, component-level state for local UI interactions.
- Data Fetching: Server Actions and Route Handlers for API interactions.
- Deployment: Vercel
- Additional Libraries: `react-charts` or `chart.js` for data visualization, `axios` or `fetch` for client-side API calls if needed, `clsx` for conditional class names.
DATABASE SCHEMA:
```sql
-- Users Table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255),
email VARCHAR(255) UNIQUE NOT NULL,
emailVerified TIMESTAMP(3),
image TEXT, -- URL to profile picture
hashedPassword TEXT, -- For credentials-based auth
createdAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP
);
-- Accounts Table (for OAuth)
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,
userId UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(255) NOT NULL, -- 'oauth' or 'credentials'
provider VARCHAR(255) NOT NULL,
providerAccountId VARCHAR(255) NOT NULL,
refresh_token TEXT,
access_token TEXT,
expires_at TIMESTAMP(3),
token_type VARCHAR(255),
scope VARCHAR(255),
id_token TEXT,
session_state VARCHAR(255),
UNIQUE(provider, providerAccountId)
);
-- Sessions Table (for NextAuth.js)
CREATE TABLE "session" (
"sid" BIGSERIAL PRIMARY KEY,
"userId" UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
"expires" TIMESTAMP(3) NOT NULL,
"data" JSONB NOT NULL DEFAULT '{}'
);
-- Verification Tokens Table (for NextAuth.js)
CREATE TABLE "verificationToken" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
PRIMARY KEY ("identifier", "token")
);
-- UserDataProfile Table
CREATE TABLE userDataProfiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
userId UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
socialMediaLinks JSONB, -- e.g., { 'linkedin': 'url', 'twitter': 'url', 'github': 'url' }
onlineActivitySummary TEXT, -- e.g., 'Active blogger on tech', 'Frequent contributor to open source'
professionalCertifications TEXT,
educationLevel VARCHAR(100),
pastRoles TEXT,
createdAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP
);
-- DataPrivacySettings Table
CREATE TABLE dataPrivacySettings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
userId UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
linkedinVisibility INT DEFAULT 1, -- 0: Private, 1: Limited, 2: Public
twitterVisibility INT DEFAULT 1,
githubVisibility INT DEFAULT 1,
otherPlatforms JSONB, -- Store visibility for other custom platforms
dataUsageConsent BOOLEAN DEFAULT FALSE, -- Consent for app to analyze data
createdAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP
);
-- SalaryAnalysis Table
CREATE TABLE salaryAnalyses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
userId UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
targetJobTitle VARCHAR(255),
targetLocation VARCHAR(255),
calculatedSalaryRange JSONB, -- e.g., { 'min': 80000, 'max': 120000, 'currency': 'USD' }
riskScore INT, -- 0-100, based on data profile
analysisTimestamp TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP
);
-- AuditLog Table (Optional for MVP, useful for tracking data access)
CREATE TABLE auditLogs (
id BIGSERIAL PRIMARY KEY,
userId UUID REFERENCES users(id) ON DELETE SET NULL,
action TEXT NOT NULL,
details JSONB,
timestamp TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP
);
```
CORE FEATURES & USER FLOW:
1. **User Authentication (NextAuth.js)**
* **Flow:** User lands on the homepage. Clicks 'Sign Up' or 'Login'. Presented with options: Email/Password, Google OAuth, GitHub OAuth.
* **Sign Up (Email/Pass):** Enters email, password, confirms password. Validation runs. Password hashed. User created in `users` table. Email verification email sent (future enhancement, MVP assumes valid email). Redirected to profile setup.
* **Sign Up (OAuth):** Clicks Google/GitHub. Redirected to provider. Authorizes app. Redirected back. User created/found in `users` table, `accounts` table updated. Redirected to profile setup.
* **Login (Email/Pass):** Enters email, password. Credentials verified against `users` table. Session created. Redirected to dashboard.
* **Login (OAuth):** Clicks Google/GitHub. Verifies user via provider. Redirected to dashboard.
* **Edge Cases:** Invalid credentials, duplicate email, OAuth callback errors, session expiry.
2. **User Data Profile Setup**
* **Flow:** After successful auth, user is guided through profile setup. A multi-step form or a single page with sections.
* **Sections:**
* **Basic Info:** Name (pre-filled), potentially desired job title/location for salary analysis.
* **Social Media Links:** Input fields for LinkedIn, Twitter, GitHub, personal website. Validation for URL format.
* **Professional Summary:** Text area for a brief professional summary or key achievements.
* **Education & Experience:** Input fields for education level, past roles, key certifications.
* **Data Submission:** User fills the form. On submit, data is validated using Zod and React Hook Form. `userDataProfiles` and `dataPrivacySettings` tables are updated/created for the user via Server Actions.
* **Edge Cases:** Incomplete form, invalid URL formats, large text inputs.
3. **Data Risk Analysis & Visualization**
* **Flow:** On the Dashboard or a dedicated 'Analysis' page, the user sees a summary of their data risk.
* **Process:** A Server Action triggers an analysis based on the `userDataProfiles` and `dataPrivacySettings`. The risk score is calculated (e.g., more public data = higher risk, sensitive keywords in summary = higher risk). The results are stored in `salaryAnalyses`.
* **Visualization:** A prominent risk score (e.g., a gauge chart or a simple numerical score 0-100) is displayed. A brief explanation of *why* the score is high/low is provided (e.g., "Your LinkedIn profile is very detailed, which might give employers leverage.").
* **Edge Cases:** No profile data available, analysis calculation errors.
4. **Salary Range Estimation**
* **Flow:** Within the 'Analysis' page or a 'Salary Tools' section.
* **Input:** User confirms or inputs target job title, location, years of experience (can be inferred from profile but explicit input is better for MVP).
* **Process:** A Server Action queries an external (mocked for MVP) or internal API/database for salary benchmarks based on job title, location, and experience. It then adjusts this benchmark based on the user's `riskScore` and potentially profile details (e.g., high-demand certifications might slightly increase the range).
* **Output:** Displays a suggested fair salary range (`min`, `max`, `currency`).
* **Edge Cases:** Insufficient data for target job/location, API errors, nonsensical user inputs.
5. **Privacy Control Panel**
* **Flow:** Accessible from 'Settings' or 'Profile' page.
* **Functionality:** User can adjust visibility sliders/dropdowns for different platforms (LinkedIn, Twitter, GitHub, etc.). Toggles for data usage consent.
* **Update:** Changes are saved via Server Actions, updating the `dataPrivacySettings` table.
* **Edge Cases:** User revokes consent, attempts to save invalid settings.
API & DATA FETCHING:
- **Server Actions:** Primary method for data mutations (creating/updating profile, settings, triggering analysis) and authenticated data fetching for protected pages.
- **Route Handlers (API Routes):** For any external API integrations or if Server Actions become too complex for a specific task.
- **Data Fetching Pattern:** Fetch data server-side in Server Components where possible. For client-side interactions requiring dynamic data (e.g., form validation feedback), use Server Actions called from client components or dedicated API routes.
- **Example Server Action (Update Profile):
```javascript
// app/actions.ts
'use server';
import { db } from '@/lib/db'; // Drizzle connection
import { userDataProfiles } from '@/lib/schema'; // Zod schema
import { eq } from 'drizzle-orm';
import { getCurrentUser } from '@/lib/auth'; // Function to get current user from session
import { z } from 'zod';
const profileSchema = z.object({
linkedin: z.string().url().optional(),
twitter: z.string().url().optional(),
// ... other fields
});
export async function updateProfile(formData: FormData) {
const user = await getCurrentUser();
if (!user) {
throw new Error('Authentication required');
}
const linkedin = formData.get('linkedin') as string | null;
const twitter = formData.get('twitter') as string | null;
// ... get other fields
// Basic validation example (use Zod for robust validation)
if (linkedin && !linkedin.startsWith('http')) {
return { error: 'Invalid LinkedIn URL' };
}
try {
await db.insert(userDataProfiles).values({
userId: user.id,
socialMediaLinks: { linkedin, twitter } as any, // Cast to 'any' or use proper types
}).onConflictDoUpdate({
target: userDataProfiles.userId,
set: {
socialMediaLinks: { linkedin, twitter } as any,
// ... update other fields
updatedAt: new Date()
}
});
revalidatePath('/profile'); // Clear cache for profile page
return { success: true };
} catch (error) {
console.error('Failed to update profile:', error);
return { error: 'Failed to update profile' };
}
}
```
UI/UX DESIGN & VISUAL IDENTITY:
- **Style:** Minimalist Clean with subtle data-visualization elements.
- **Color Palette:**
* Primary (Dark Blue/Purple): `#2C3E50` (for backgrounds, deep text)
* Secondary (Teal/Cyan): `#1ABC9C` (for accents, CTAs, highlights)
* Accent (Light Gray/Off-white): `#ECF0F1` (for text on dark backgrounds, borders)
* Warning/Risk (Orange/Red): `#E74C3C` (for risk indicators)
* Success (Green): `#2ECC71` (for success messages)
- **Typography:** Use a clean sans-serif font like Inter or Roboto. Headings should be distinct but not overly decorative. Body text should be highly readable.
- **Layout:** Use a consistent grid system (e.g., 12-column). Maintain ample whitespace. Key information (like risk score) should be prominent on the dashboard.
- **Sections:** Header (Logo, Nav links, Auth button), Sidebar/Main Content Area (for pages), Footer.
- **Responsive Rules:** Mobile-first approach. Ensure usability on small screens. Use Tailwind's responsive prefixes (sm:, md:, lg:) for adjustments. Navigation might collapse into a hamburger menu on smaller screens.
- **Visual Elements:** Clean icons, subtle gradients in buttons or headers, clear data charts/graphs for analysis.
COMPONENT BREAKDOWN (Next.js App Router):
- **`app/layout.tsx`:** Root layout (HTML, Body, Providers, global styles, Tailwind CSS setup).
- **`app/page.tsx`:** Landing Page (Introduction, value proposition, features overview, CTAs for sign-up/login).
- **`app/(auth)/layout.tsx`:** Authentication layout (common styling for login/signup pages).
- **`app/(auth)/login/page.tsx`:** Login form.
- **`app/(auth)/signup/page.tsx`:** Sign up form.
- **`app/(app)/layout.tsx`:** Main application layout (includes sidebar/header, protected routes).
- **`app/(app)/dashboard/page.tsx`:** Dashboard (displays summary risk score, quick links to analysis and profile).
- **`app/(app)/profile/page.tsx`:** User Data Profile form (Input fields for social links, summary, etc.). Uses Server Actions for submission.
- **`app/(app)/analysis/page.tsx`:** Salary Analysis page (Displays detailed risk analysis, salary range estimation, charts).
- **`app/(app)/settings/page.tsx`:** Privacy Control Panel (Toggles for data visibility, consent). Uses Server Actions.
- **`app/api/auth/[...nextauth]/route.ts`:** NextAuth.js route handler.
- **`components/ui/`:** (shadcn/ui components) Button, Input, Card, Sheet, Dialog, Form, etc.
- **`components/layout/`:** Header, Sidebar, Footer components.
- **`components/`:** AuthForm (reusable login/signup), ProfileForm, AnalysisChart, RiskGauge, PrivacyToggles.
- **`lib/db.ts`:** Drizzle ORM connection setup.
- **`lib/schema.ts`:** Drizzle schema definitions (mirrors DB schema).
- **`lib/auth.ts`:** NextAuth.js configuration and helper functions (e.g., `getCurrentUser`).
- **`lib/utils.ts`:** General utility functions.
ANIMATIONS:
- **Page Transitions:** Subtle fade-in/out using Next.js's `AnimatePresence` (if using `framer-motion`) or CSS transitions on route changes.
- **Hover Effects:** Slight scale or background color change on buttons and interactive elements.
- **Loading States:** Skeleton loaders for data fetching, spinners for form submissions/API calls.
- **Chart Animations:** Smooth transitions for data updates or initial rendering in charts.
- **Form Validation:** Inline error message animations (e.g., slight shake or fade-in).
EDGE CASES:
- **No Data:** Display clear 'empty state' messages and prompts for users to add data (e.g., "Your profile is empty. Add your social links to get started.").
- **Authentication:** Handle expired sessions, incorrect login details gracefully. Implement robust error handling for OAuth callbacks.
- **Data Validation:** Use Zod and React Hook Form extensively for client-side and server-side validation of all user inputs.
- **API Errors:** Implement try-catch blocks in Server Actions and Route Handlers. Display user-friendly error messages. Use a global error handler (`error.tsx` in App Router) for unhandled exceptions.
- **Rate Limiting:** (Future consideration) Implement rate limiting for API routes to prevent abuse.
- **Database Migrations:** Use Drizzle's migration tools to manage schema changes.
SAMPLE DATA:
1. **User Profile:**
```json
{
"userId": "user-uuid-123",
"socialMediaLinks": {
"linkedin": "https://linkedin.com/in/johndoe",
"twitter": "https://twitter.com/johndoe_dev",
"github": "https://github.com/johndoe"
},
"onlineActivitySummary": "Active contributor to open-source projects, frequently writes technical blog posts on Medium.",
"professionalCertifications": "AWS Certified Solutions Architect",
"educationLevel": "Master's Degree",
"pastRoles": "Senior Software Engineer, Lead Developer"
}
```
2. **User Settings:**
```json
{
"userId": "user-uuid-123",
"linkedinVisibility": 2, // Public
"twitterVisibility": 1, // Limited
"githubVisibility": 2, // Public
"dataUsageConsent": true
}
```
3. **Salary Analysis:**
```json
{
"userId": "user-uuid-123",
"targetJobTitle": "Senior Software Engineer",
"targetLocation": "San Francisco, CA",
"calculatedSalaryRange": {
"min": 140000,
"max": 190000,
"currency": "USD"
},
"riskScore": 65 // Moderately High Risk
}
```
4. **Another User Profile (Less Public):**
```json
{
"userId": "user-uuid-456",
"socialMediaLinks": {
"linkedin": "https://linkedin.com/in/janedoe_tech"
},
"onlineActivitySummary": "Focused on internal company projects, limited public activity.",
"professionalCertifications": null,
"educationLevel": "Bachelor's Degree",
"pastRoles": "Software Engineer"
}
```
5. **Another User Settings (More Private):**
```json
{
"userId": "user-uuid-456",
"linkedinVisibility": 1, // Limited
"twitterVisibility": 0, // Private
"githubVisibility": 0, // Private
"dataUsageConsent": true
}
```
6. **Another Salary Analysis (Lower Risk):**
```json
{
"userId": "user-uuid-456",
"targetJobTitle": "Software Engineer",
"targetLocation": "Austin, TX",
"calculatedSalaryRange": {
"min": 95000,
"max": 130000,
"currency": "USD"
},
"riskScore": 30 // Low Risk
}
```
7. **User without Analysis:**
* `salaryAnalyses` entry is null or doesn't exist for the user.
* Dashboard shows a prompt to run an analysis.
8. **User with Incomplete Profile:**
* `userDataProfiles` entry is missing key fields.
* Analysis might yield warnings or default values.
9. **Mock External Salary Data Point:**
```json
{
"jobTitle": "Senior Software Engineer",
"location": "San Francisco, CA",
"experienceYears": {"min": 5, "max": 10},
"baseSalary": {"min": 130000, "max": 180000},
"currency": "USD"
}
```
10. **Mock External Salary Data Point 2:**
```json
{
"jobTitle": "Software Engineer",
"location": "Austin, TX",
"experienceYears": {"min": 3, "max": 7},
"baseSalary": {"min": 90000, "max": 125000},
"currency": "USD"
}
```