# User Guide — WorkRoot Company Site > Complete guide for content management, environment setup, and troubleshooting. --- ## Table of Contents 1. [Environment Setup](#1-environment-setup) 2. [Development Workflow](#2-development-workflow) 3. [Managing Blog Posts](#3-managing-blog-posts) 4. [Updating Portfolio Items](#4-updating-portfolio-items) 5. [Available npm Scripts](#5-available-npm-scripts) 6. [Troubleshooting Common Issues](#6-troubleshooting-common-issues) --- ## 1. Environment Setup ### Prerequisites | Requirement | Version | |------------|---------| | Node.js | 18.x or higher | | npm | 8.x or higher | ### Step 1: Install Dependencies ```bash npm install ``` ### Step 2: Configure Environment Variables Copy the example environment file and fill in your values: ```bash cp .env.example .env ``` Open `.env` and configure the following: #### Server Settings (Required) ```env HOST=0.0.0.0 PORT=10000 NODE_ENV=development ``` #### Contact Form Email (Required for contact form to work) Uncomment and fill in your SMTP credentials. Gmail example: ```env SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your_email@gmail.com SMTP_PASS=your_app_password_here # Use App Password, not your regular password CONTACT_EMAIL=hello@workroot.in # Where contact form submissions are sent ``` > **Gmail tip:** Go to Google Account → Security → 2-Step Verification → App passwords to generate an App Password. #### Newsletter Integration (Optional — pick one) **Option A: Mailchimp** ```env MAILCHIMP_API_KEY=your_mailchimp_api_key MAILCHIMP_LIST_ID=your_audience_list_id MAILCHIMP_DC=us1 ``` **Option B: ConvertKit** ```env CONVERTKIT_API_KEY=your_convertkit_api_key CONVERTKIT_FORM_ID=your_form_id ``` #### Analytics (Optional — pick one) **Option A: Google Analytics 4** ```env GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX ``` **Option B: Plausible (privacy-friendly)** ```env PLAUSIBLE_DOMAIN=workroot.in ``` #### Error Monitoring (Optional but Recommended for Production) ```env SENTRY_DSN=https://xxx@oXXXXXX.ingest.sentry.io/XXXXXXX LOG_LEVEL=info # Options: debug | info | warn | error RELEASE_VERSION=1.0.0 ``` ### Step 3: Start the Development Server ```bash npm run dev ``` The site will be available at `http://localhost:4321`. --- ## 2. Development Workflow ### Local Development ```bash npm run dev # Start dev server with hot reload npm run build # Build for production npm run preview # Preview the production build locally ``` ### Production Server ```bash npm run start:prod # Start the production Node.js server ``` The production server uses `server.mjs` which includes gzip/brotli compression and caching headers. --- ## 3. Managing Blog Posts Blog posts are written in Markdown and stored in `src/content/blog/`. Each file is automatically turned into a page at `/blog/[filename]/`. ### Creating a New Blog Post 1. Create a new `.md` file in `src/content/blog/`: ``` src/content/blog/my-new-post.md ``` The filename becomes the URL slug. Use lowercase with hyphens: `my-new-post.md` → `/blog/my-new-post`. 2. Add the required frontmatter at the top of the file: ```markdown --- title: "Your Post Title" description: "A brief description for SEO and social sharing (150-160 characters recommended)." pubDate: 2026-03-21 heroImage: "/images/blog/your-image.jpg" category: "Web Development" tags: ["Tag1", "Tag2", "Tag3"] author: name: "Author Name" avatar: "/images/team/author.jpg" draft: false --- Your post content goes here... ``` 3. Write your content in Markdown below the frontmatter. ### Frontmatter Reference | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | Post title shown on the page | | `description` | string | Yes | Meta description for SEO | | `pubDate` | date | Yes | Publication date (`YYYY-MM-DD`) | | `updatedDate` | date | No | Last updated date | | `heroImage` | string | No | Path to hero image (e.g., `/images/blog/image.jpg`) | | `category` | enum | Yes | Must be one of the valid categories (see below) | | `tags` | string[] | Yes | Array of relevant tags | | `author.name` | string | Yes | Author's display name | | `author.avatar` | string | No | Path to author's avatar image | | `draft` | boolean | No | Set to `true` to hide from listing (default: `false`) | ### Valid Categories The `category` field must be exactly one of these values: - `Web Development` - `Mobile Apps` - `AI/ML` - `Cloud` - `DevOps` - `Security` > **Important:** The value must match exactly (case-sensitive). Using an invalid category will cause a build error. ### Working with Draft Posts To write a post without publishing it: ```yaml draft: true ``` Draft posts won't appear in the blog listing but will still build. To publish, change `draft: false` or remove the field. ### Adding Images to Blog Posts 1. Place your image in `public/images/blog/`: ``` public/images/blog/my-post-hero.jpg ``` 2. Reference it in the frontmatter: ```yaml heroImage: "/images/blog/my-post-hero.jpg" ``` 3. To embed images in the post body, use standard Markdown: ```markdown ![Alt text describing the image](/images/blog/diagram.png) ``` ### Updating an Existing Post 1. Open the post's `.md` file in `src/content/blog/` 2. Update the content and/or frontmatter 3. Add or update `updatedDate` to reflect when it was changed: ```yaml updatedDate: 2026-03-21 ``` ### Deleting a Blog Post Simply delete the `.md` file from `src/content/blog/`. The route will no longer exist after the next build. --- ## 4. Updating Portfolio Items Portfolio items are defined as JavaScript objects inside `src/pages/portfolio.astro`. There is no separate content folder for portfolio items. ### Locating the Portfolio Data Open `src/pages/portfolio.astro` and look for the array of project objects near the top of the file (inside the frontmatter `---` block). ### Adding a New Portfolio Item Add a new object to the projects array: ```javascript { id: 'my-new-project', // Unique ID, used for filtering. Use kebab-case. title: 'My New Project', category: 'web', // Must be: 'web', 'mobile', or 'ai' thumbnail: '/images/portfolio/my-project.jpg', description: 'Short 1-2 line description shown on the card.', techStack: ['React', 'Node.js', 'PostgreSQL'], client: 'Client Name', duration: '3 months', results: [ '50% reduction in load time', '30% increase in user engagement', ], fullDescription: 'Detailed paragraph describing the project, the challenge, and the solution. This appears in the expanded case study view.', } ``` ### Portfolio Item Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | Yes | Unique identifier. Use kebab-case (e.g., `my-project`). | | `title` | string | Yes | Project name displayed on the card | | `category` | string | Yes | Must be `'web'`, `'mobile'`, or `'ai'` | | `thumbnail` | string | Yes | Image path or URL for the project card | | `description` | string | Yes | Short summary (1-2 sentences) | | `techStack` | string[] | Yes | Technologies used (shown as badges) | | `client` | string | Yes | Client or company name | | `duration` | string | Yes | How long the project took (e.g., `'6 months'`) | | `results` | string[] | Yes | Key achievements/metrics (use 2-4 bullet points) | | `fullDescription` | string | Yes | Detailed description for the case study view | ### Valid Categories for Portfolio | Category | Shows Up Under | |----------|---------------| | `'web'` | "Web Development" filter | | `'mobile'` | "Mobile Apps" filter | | `'ai'` | "AI Solutions" filter | ### Editing an Existing Portfolio Item 1. Open `src/pages/portfolio.astro` 2. Find the object with the matching `id` 3. Update any fields you want to change 4. Save the file — changes will appear immediately in dev mode ### Removing a Portfolio Item Delete the entire object `{ ... }` from the projects array. Make sure to remove any trailing commas to keep valid JavaScript. ### Using External Images (Unsplash, CDN, etc.) You can use external image URLs directly in the `thumbnail` field: ```javascript thumbnail: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=600&h=400&fit=crop', ``` For local images, place them in `public/images/portfolio/` and reference as `/images/portfolio/filename.jpg`. --- ## 5. Available npm Scripts ### Development | Command | Description | |---------|-------------| | `npm run dev` | Start local development server (hot reload at localhost:4321) | | `npm run build` | Build the site for production | | `npm run preview` | Preview production build locally | | `npm run start` | Start the standalone Node.js server | | `npm run start:prod` | Start the server in production mode | ### Testing | Command | Description | |---------|-------------| | `npm test` | Run all Playwright tests | | `npm run test:smoke` | Quick smoke test (critical pages only) | | `npm run test:critical` | Test critical user paths | | `npm run test:api` | Test API endpoints | | `npm run test:forms` | Test contact and newsletter forms | | `npm run test:chromium` | Run tests in Chrome only | | `npm run test:firefox` | Run tests in Firefox only | | `npm run test:mobile` | Run mobile device tests | | `npm run test:report` | Open the last test report in a browser | | `npm run test:ci` | Run CI-appropriate subset of tests | ### Backups | Command | Description | |---------|-------------| | `npm run backup:full` | Full site backup | | `npm run backup:content` | Backup content files only | | `npm run backup:config` | Backup configuration files | | `npm run backup:list` | List available backups | | `npm run backup:restore` | Restore from a backup | | `npm run backup:verify` | Verify backup integrity | | `npm run backup:cleanup` | Remove old backups | ### Validation | Command | Description | |---------|-------------| | `npm run validate:schema` | Validate content schemas | | `npm run validate:structured-data` | Validate JSON-LD structured data | --- ## 6. Troubleshooting Common Issues ### Build fails with "Invalid frontmatter" error **Symptom:** Running `npm run build` shows an error like `Invalid value for field "category"`. **Cause:** A blog post has an invalid or misspelled `category` value. **Fix:** Open the failing `.md` file and ensure `category` is exactly one of: ``` Web Development | Mobile Apps | AI/ML | Cloud | DevOps | Security ``` --- ### Blog post not appearing on the site **Cause 1:** The post has `draft: true` set. **Fix:** Change to `draft: false` or remove the `draft` line. **Cause 2:** The `pubDate` is in the future. **Fix:** Set `pubDate` to today's date or earlier. **Cause 3:** Missing required frontmatter fields. **Fix:** Ensure `title`, `description`, `pubDate`, `category`, `tags`, and `author.name` are all present. --- ### Contact form submissions not arriving **Cause:** SMTP credentials are not configured or are incorrect. **Checklist:** 1. Confirm `.env` has `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, and `CONTACT_EMAIL` set 2. If using Gmail, use an **App Password** (not your account password) — see [Google App Passwords](https://support.google.com/accounts/answer/185833) 3. Check `LOG_LEVEL=debug` in `.env` and restart the server to see detailed SMTP logs 4. Test the API directly: `curl -X POST http://localhost:10000/api/contact -H "Content-Type: application/json" -d '{"name":"Test","email":"test@example.com","message":"Hello"}'` --- ### Newsletter subscription not working **Cause:** Newsletter provider credentials are missing or incorrect. **Checklist:** 1. Confirm the correct provider variables are set in `.env` (Mailchimp OR ConvertKit, not both) 2. For Mailchimp: verify the `MAILCHIMP_DC` matches your API key's data center (the part after `-` in your API key, e.g., `us1`) 3. For ConvertKit: confirm the `CONVERTKIT_FORM_ID` is the numeric form ID, not the form name 4. Check server logs for error details --- ### Dev server won't start (port conflict) **Symptom:** Error: `Port 4321 is already in use`. **Fix:** ```bash # Find what's using the port netstat -ano | findstr :4321 # Windows lsof -i :4321 # Mac/Linux # Or change the port in astro.config.mjs: server: { port: 4322 } ``` --- ### Production server not starting **Symptom:** `npm run start:prod` fails or crashes immediately. **Checklist:** 1. Run `npm run build` first — the server requires a production build in `dist/` 2. Confirm `NODE_ENV=production` in your `.env` 3. Check that `PORT` is not blocked by a firewall 4. Review error output carefully — missing env variables often cause startup failures --- ### Images not loading in production **Cause:** Images placed in `src/` instead of `public/`. **Fix:** All static assets (images, fonts, etc.) must be in the `public/` directory: ``` public/images/blog/my-image.jpg ✓ src/images/my-image.jpg ✗ (won't be served) ``` --- ### Analytics not tracking **Cause:** `GOOGLE_ANALYTICS_ID` or `PLAUSIBLE_DOMAIN` not set, or the value uses the old Universal Analytics format. **Fix for GA4:** The ID must start with `G-`, not `UA-`: ```env GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX ✓ GOOGLE_ANALYTICS_ID=UA-XXXXXXXXX ✗ (old format, won't work) ``` After updating `.env`, rebuild and redeploy for changes to take effect. --- ### Test suite failing locally **Symptom:** Playwright tests fail with connection errors or timeout. **Fix:** 1. Ensure the dev/production server is running before tests 2. Install Playwright browsers if first time: `npx playwright install` 3. For API tests, ensure env variables are set 4. Run a focused subset to isolate the issue: `npm run test:smoke` --- ### Validate structured data / schema errors **Symptom:** `npm run validate:structured-data` shows errors. **Fix:** Run the validator to see which pages have issues: ```bash npm run validate:structured-data ``` Check the output for specific JSON-LD errors and fix the structured data in `src/components/SEO.astro` or the relevant page. --- ## Additional Resources | Resource | Location | |----------|----------| | Security audit & headers | `SECURITY-AUDIT.md` | | SEO implementation details | `SEO-IMPLEMENTATION.md` | | Performance baseline metrics | `PERFORMANCE-BASELINE.md` | | Deployment instructions | `DEPLOYMENT.md` | | Quick deployment reference | `QUICK-DEPLOY.md` | | Changelog | `CHANGELOG.md` | | Server setup | `SERVER_README.md` | --- *Last updated: 2026-03-21*