forked from cardosofelipe/fast-next-template
feat: complete Syndarix rebranding from PragmaStack
- Update PROJECT_NAME to Syndarix in backend config - Update all frontend components with Syndarix branding - Replace all GitHub URLs with Gitea Syndarix repo URLs - Update metadata, headers, footers with new branding - Update tests to match new URLs - Update E2E tests for new repo references - Preserve "Built on PragmaStack" attribution in docs Closes #13 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PragmaStack Backend API
|
||||
# Syndarix Backend API
|
||||
|
||||
> The pragmatic, production-ready FastAPI backend for PragmaStack.
|
||||
> The pragmatic, production-ready FastAPI backend for Syndarix.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
PROJECT_NAME: str = "PragmaStack"
|
||||
PROJECT_NAME: str = "Syndarix"
|
||||
VERSION: str = "1.0.0"
|
||||
API_V1_STR: str = "/api/v1"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# PragmaStack - Frontend
|
||||
# Syndarix - Frontend
|
||||
|
||||
Production-ready Next.js 16 frontend with TypeScript, authentication, admin panel, and internationalization.
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ NEXT_PUBLIC_DEMO_MODE=true npm run dev
|
||||
**1. Fork Repository**
|
||||
|
||||
```bash
|
||||
gh repo fork your-repo/fast-next-template
|
||||
git clone https://gitea.pragmazest.com/cardosofelipe/syndarix.git
|
||||
```
|
||||
|
||||
**2. Connect to Vercel**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Internationalization (i18n) Guide
|
||||
|
||||
This document describes the internationalization implementation in the PragmaStack.
|
||||
This document describes the internationalization implementation in Syndarix.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
## Logo
|
||||
|
||||
The **PragmaStack** logo represents the core values of the project: structure, speed, and clarity.
|
||||
The **Syndarix** logo represents the core values of the project: structure, speed, and clarity.
|
||||
|
||||
<div align="center">
|
||||
<img src="../../public/logo.svg" alt="PragmaStack Logo" width="300" />
|
||||
<img src="../../public/logo.svg" alt="Syndarix Logo" width="300" />
|
||||
<p><em>The Stack: Geometric layers representing the full-stack architecture.</em></p>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ The **PragmaStack** logo represents the core values of the project: structure, s
|
||||
For smaller contexts (favicons, headers), we use the simplified icon:
|
||||
|
||||
<div align="center">
|
||||
<img src="../../public/logo-icon.svg" alt="PragmaStack Icon" width="64" />
|
||||
<img src="../../public/logo-icon.svg" alt="Syndarix Icon" width="64" />
|
||||
</div>
|
||||
|
||||
For now, we use the **Lucide React** icon set for all iconography. Icons should be used sparingly and meaningfully to enhance understanding, not just for decoration.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Branding Guidelines
|
||||
|
||||
Welcome to the **PragmaStack** branding guidelines. This section defines who we are, how we speak, and how we look.
|
||||
Welcome to the **Syndarix** branding guidelines. This section defines who we are, how we speak, and how we look.
|
||||
|
||||
## Contents
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Quick Start Guide
|
||||
|
||||
Get up and running with the PragmaStack design system immediately. This guide covers the essential patterns you need to build 80% of interfaces.
|
||||
Get up and running with the Syndarix design system immediately. This guide covers the essential patterns you need to build 80% of interfaces.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# AI Code Generation Guidelines
|
||||
|
||||
**For AI Assistants**: This document contains strict rules for generating code in the PragmaStack project. Follow these rules to ensure generated code matches the design system perfectly.
|
||||
**For AI Assistants**: This document contains strict rules for generating code in the Syndarix project. Follow these rules to ensure generated code matches the design system perfectly.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Quick Reference
|
||||
|
||||
**Bookmark this page** for instant lookups of colors, spacing, typography, components, and common patterns. Your go-to cheat sheet for the PragmaStack design system.
|
||||
**Bookmark this page** for instant lookups of colors, spacing, typography, components, and common patterns. Your go-to cheat sheet for the Syndarix design system.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Design System Documentation
|
||||
|
||||
**PragmaStack Design System** - A comprehensive guide to building consistent, accessible, and beautiful user interfaces.
|
||||
**Syndarix Design System** - A comprehensive guide to building consistent, accessible, and beautiful user interfaces.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ test.describe('Homepage - Desktop Navigation', () => {
|
||||
|
||||
test('should display header with logo and navigation', async ({ page }) => {
|
||||
// Logo should be visible
|
||||
await expect(page.getByRole('link', { name: /PragmaStack/i })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: /Syndarix/i })).toBeVisible();
|
||||
|
||||
// Desktop navigation links should be visible (use locator to find within header)
|
||||
const header = page.locator('header').first();
|
||||
@@ -23,8 +23,8 @@ test.describe('Homepage - Desktop Navigation', () => {
|
||||
});
|
||||
|
||||
test('should display GitHub link with star badge', async ({ page }) => {
|
||||
// Find GitHub link by checking for one that has github.com in href
|
||||
const githubLink = page.locator('a[href*="github.com"]').first();
|
||||
// Find GitHub link by checking for one that has gitea.pragmazest.com in href
|
||||
const githubLink = page.locator('a[href*="gitea.pragmazest.com"]').first();
|
||||
await expect(githubLink).toBeVisible();
|
||||
await expect(githubLink).toHaveAttribute('target', '_blank');
|
||||
});
|
||||
@@ -120,7 +120,7 @@ test.describe('Homepage - Hero Section', () => {
|
||||
test('should navigate to GitHub when clicking View on GitHub', async ({ page }) => {
|
||||
const githubLink = page.getByRole('link', { name: /View on GitHub/i }).first();
|
||||
await expect(githubLink).toBeVisible();
|
||||
await expect(githubLink).toHaveAttribute('href', expect.stringContaining('github.com'));
|
||||
await expect(githubLink).toHaveAttribute('href', expect.stringContaining('gitea.pragmazest.com'));
|
||||
});
|
||||
|
||||
test('should navigate to components when clicking Explore Components', async ({ page }) => {
|
||||
@@ -250,7 +250,7 @@ test.describe('Homepage - Feature Sections', () => {
|
||||
});
|
||||
|
||||
test('should display philosophy section', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: /Why PragmaStack/i })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: /Why Syndarix/i })).toBeVisible();
|
||||
await expect(page.getByText(/MIT licensed/i).first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -264,7 +264,7 @@ test.describe('Homepage - Footer', () => {
|
||||
// Scroll to footer
|
||||
await page.locator('footer').scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(page.getByText(/PragmaStack. MIT Licensed/i)).toBeVisible();
|
||||
await expect(page.getByText(/Syndarix. MIT Licensed/i)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -285,7 +285,7 @@ test.describe('Homepage - Accessibility', () => {
|
||||
});
|
||||
|
||||
test('should have accessible links with proper attributes', async ({ page }) => {
|
||||
const githubLink = page.locator('a[href*="github.com"]').first();
|
||||
const githubLink = page.locator('a[href*="gitea.pragmazest.com"]').first();
|
||||
await expect(githubLink).toHaveAttribute('target', '_blank');
|
||||
await expect(githubLink).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
|
||||
@@ -7,42 +7,42 @@
|
||||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.12.3';
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82';
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse');
|
||||
const activeClientIds = new Set();
|
||||
const PACKAGE_VERSION = '2.12.3'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
addEventListener('install', function () {
|
||||
self.skipWaiting();
|
||||
});
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
addEventListener('message', async function (event) {
|
||||
const clientId = Reflect.get(event.source || {}, 'id');
|
||||
const clientId = Reflect.get(event.source || {}, 'id')
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId);
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
});
|
||||
})
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
});
|
||||
break;
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
@@ -52,12 +52,12 @@ addEventListener('message', async function (event) {
|
||||
packageVersion: PACKAGE_VERSION,
|
||||
checksum: INTEGRITY_CHECKSUM,
|
||||
},
|
||||
});
|
||||
break;
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId);
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
@@ -67,51 +67,54 @@ addEventListener('message', async function (event) {
|
||||
frameType: client.frameType,
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId);
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId;
|
||||
});
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister();
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
addEventListener('fetch', function (event) {
|
||||
const requestInterceptedAt = Date.now();
|
||||
const requestInterceptedAt = Date.now()
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (event.request.mode === 'navigate') {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
|
||||
return;
|
||||
if (
|
||||
event.request.cache === 'only-if-cached' &&
|
||||
event.request.mode !== 'same-origin'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been terminated (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = crypto.randomUUID();
|
||||
event.respondWith(handleRequest(event, requestId, requestInterceptedAt));
|
||||
});
|
||||
const requestId = crypto.randomUUID()
|
||||
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
@@ -119,18 +122,23 @@ addEventListener('fetch', function (event) {
|
||||
* @param {number} requestInterceptedAt
|
||||
*/
|
||||
async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
const client = await resolveMainClient(event);
|
||||
const requestCloneForEvents = event.request.clone();
|
||||
const response = await getResponse(event, client, requestId, requestInterceptedAt);
|
||||
const client = await resolveMainClient(event)
|
||||
const requestCloneForEvents = event.request.clone()
|
||||
const response = await getResponse(
|
||||
event,
|
||||
client,
|
||||
requestId,
|
||||
requestInterceptedAt,
|
||||
)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents);
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
||||
|
||||
// Clone the response so both the client and the library could consume it.
|
||||
const responseClone = response.clone();
|
||||
const responseClone = response.clone()
|
||||
|
||||
sendToClient(
|
||||
client,
|
||||
@@ -151,11 +159,11 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
},
|
||||
},
|
||||
},
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : []
|
||||
);
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
||||
)
|
||||
}
|
||||
|
||||
return response;
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,30 +175,30 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
* @returns {Promise<Client | undefined>}
|
||||
*/
|
||||
async function resolveMainClient(event) {
|
||||
const client = await self.clients.get(event.clientId);
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (activeClientIds.has(event.clientId)) {
|
||||
return client;
|
||||
return client
|
||||
}
|
||||
|
||||
if (client?.frameType === 'top-level') {
|
||||
return client;
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
});
|
||||
})
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible';
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id);
|
||||
});
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,34 +211,36 @@ async function resolveMainClient(event) {
|
||||
async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const requestClone = event.request.clone();
|
||||
const requestClone = event.request.clone()
|
||||
|
||||
function passthrough() {
|
||||
// Cast the request headers to a new Headers instance
|
||||
// so the headers can be manipulated with.
|
||||
const headers = new Headers(requestClone.headers);
|
||||
const headers = new Headers(requestClone.headers)
|
||||
|
||||
// Remove the "accept" header value that marked this request as passthrough.
|
||||
// This prevents request alteration and also keeps it compliant with the
|
||||
// user-defined CORS policies.
|
||||
const acceptHeader = headers.get('accept');
|
||||
const acceptHeader = headers.get('accept')
|
||||
if (acceptHeader) {
|
||||
const values = acceptHeader.split(',').map((value) => value.trim());
|
||||
const filteredValues = values.filter((value) => value !== 'msw/passthrough');
|
||||
const values = acceptHeader.split(',').map((value) => value.trim())
|
||||
const filteredValues = values.filter(
|
||||
(value) => value !== 'msw/passthrough',
|
||||
)
|
||||
|
||||
if (filteredValues.length > 0) {
|
||||
headers.set('accept', filteredValues.join(', '));
|
||||
headers.set('accept', filteredValues.join(', '))
|
||||
} else {
|
||||
headers.delete('accept');
|
||||
headers.delete('accept')
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(requestClone, { headers });
|
||||
return fetch(requestClone, { headers })
|
||||
}
|
||||
|
||||
// Bypass mocking when the client is not active.
|
||||
if (!client) {
|
||||
return passthrough();
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
@@ -238,11 +248,11 @@ async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return passthrough();
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Notify the client that a request has been intercepted.
|
||||
const serializedRequest = await serializeRequest(event.request);
|
||||
const serializedRequest = await serializeRequest(event.request)
|
||||
const clientMessage = await sendToClient(
|
||||
client,
|
||||
{
|
||||
@@ -253,20 +263,20 @@ async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
...serializedRequest,
|
||||
},
|
||||
},
|
||||
[serializedRequest.body]
|
||||
);
|
||||
[serializedRequest.body],
|
||||
)
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_RESPONSE': {
|
||||
return respondWithMock(clientMessage.data);
|
||||
return respondWithMock(clientMessage.data)
|
||||
}
|
||||
|
||||
case 'PASSTHROUGH': {
|
||||
return passthrough();
|
||||
return passthrough()
|
||||
}
|
||||
}
|
||||
|
||||
return passthrough();
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,18 +287,21 @@ async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
*/
|
||||
function sendToClient(client, message, transferrables = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel();
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error);
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data);
|
||||
};
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)]);
|
||||
});
|
||||
client.postMessage(message, [
|
||||
channel.port2,
|
||||
...transferrables.filter(Boolean),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,17 +314,17 @@ function respondWithMock(response) {
|
||||
// instance will have status code set to 0. Since it's not possible to create
|
||||
// a Response instance with status code 0, handle that use-case separately.
|
||||
if (response.status === 0) {
|
||||
return Response.error();
|
||||
return Response.error()
|
||||
}
|
||||
|
||||
const mockedResponse = new Response(response.body, response);
|
||||
const mockedResponse = new Response(response.body, response)
|
||||
|
||||
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
|
||||
value: true,
|
||||
enumerable: true,
|
||||
});
|
||||
})
|
||||
|
||||
return mockedResponse;
|
||||
return mockedResponse
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,5 +345,5 @@ async function serializeRequest(request) {
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: await request.arrayBuffer(),
|
||||
keepalive: request.keepalive,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Footer } from '@/components/layout/Footer';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s | PragmaStack',
|
||||
template: '%s | Syndarix',
|
||||
default: 'Dashboard',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import { AdminSidebar, Breadcrumbs } from '@/components/admin';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s | Admin | PragmaStack',
|
||||
template: '%s | Admin | Syndarix',
|
||||
default: 'Admin Dashboard',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,8 +26,8 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Demo Tour | PragmaStack',
|
||||
description: 'Try all features with demo credentials - comprehensive guide to the PragmaStack',
|
||||
title: 'Demo Tour | Syndarix',
|
||||
description: 'Try all features with demo credentials - comprehensive guide to the Syndarix',
|
||||
};
|
||||
|
||||
const demoCategories = [
|
||||
|
||||
@@ -120,7 +120,7 @@ export default function DocsHub() {
|
||||
<h2 className="text-4xl font-bold tracking-tight mb-4">Design System Documentation</h2>
|
||||
<p className="text-lg text-muted-foreground mb-8">
|
||||
Comprehensive guides, best practices, and references for building consistent,
|
||||
accessible, and maintainable user interfaces with the PragmaStack design system.
|
||||
accessible, and maintainable user interfaces with the Syndarix design system.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3 justify-center">
|
||||
<Link href="/dev/docs/design-system/00-quick-start">
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Design System Hub | PragmaStack',
|
||||
title: 'Design System Hub | Syndarix',
|
||||
description:
|
||||
'Interactive design system demonstrations with live examples - explore components, layouts, spacing, and forms built with shadcn/ui and Tailwind CSS',
|
||||
};
|
||||
@@ -90,7 +90,7 @@ export default function DesignSystemHub() {
|
||||
</div>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Interactive demonstrations, live examples, and comprehensive documentation for the
|
||||
PragmaStack design system. Built with shadcn/ui + Tailwind CSS 4.
|
||||
Syndarix design system. Built with shadcn/ui + Tailwind CSS 4.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* istanbul ignore file -- @preserve Landing page with complex interactions tested via E2E */
|
||||
/**
|
||||
* Homepage / Landing Page
|
||||
* Main landing page for the PragmaStack project
|
||||
* Main landing page for the Syndarix project
|
||||
* Showcases features, tech stack, and provides demos for developers
|
||||
*/
|
||||
|
||||
@@ -68,7 +68,7 @@ export default function Home() {
|
||||
<div className="container mx-auto px-6 py-8">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
© {new Date().getFullYear()} PragmaStack. MIT Licensed.
|
||||
© {new Date().getFullYear()} Syndarix. MIT Licensed.
|
||||
</div>
|
||||
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
||||
<Link href="/demos" className="hover:text-foreground transition-colors">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
/**
|
||||
* PragmaStack Design System
|
||||
* Syndarix Design System
|
||||
* Theme: Modern Minimal (from tweakcn.com)
|
||||
* Primary: Blue | Color Space: OKLCH
|
||||
*
|
||||
|
||||
@@ -96,12 +96,12 @@ export function DevLayout({ children }: DevLayoutProps) {
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
<Image
|
||||
src="/logo-icon.svg"
|
||||
alt="PragmaStack Logo"
|
||||
alt="Syndarix Logo"
|
||||
width={24}
|
||||
height={24}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
<h1 className="text-base font-semibold">PragmaStack</h1>
|
||||
<h1 className="text-base font-semibold">Syndarix</h1>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
Dev
|
||||
</Badge>
|
||||
|
||||
@@ -14,8 +14,8 @@ import { Link } from '@/lib/i18n/routing';
|
||||
|
||||
const commands = [
|
||||
{ text: '# Clone the repository', delay: 0 },
|
||||
{ text: '$ git clone https://github.com/your-org/fast-next-template.git', delay: 800 },
|
||||
{ text: '$ cd fast-next-template', delay: 1600 },
|
||||
{ text: '$ git clone https://gitea.pragmazest.com/cardosofelipe/syndarix.git', delay: 800 },
|
||||
{ text: '$ cd syndarix', delay: 1600 },
|
||||
{ text: '', delay: 2200 },
|
||||
{ text: '# Start with Docker (one command)', delay: 2400 },
|
||||
{ text: '$ docker-compose up', delay: 3200 },
|
||||
|
||||
@@ -49,7 +49,7 @@ export function CTASection({ onOpenDemoModal }: CTASectionProps) {
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-4">
|
||||
<Button asChild size="lg" className="gap-2 text-base group">
|
||||
<a
|
||||
href="https://github.com/your-org/fast-next-template"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -75,7 +75,7 @@ export function CTASection({ onOpenDemoModal }: CTASectionProps) {
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="ghost" className="gap-2 text-base group">
|
||||
<a
|
||||
href="https://github.com/your-org/fast-next-template#documentation"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix#documentation"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
||||
@@ -44,7 +44,7 @@ const features = [
|
||||
'12+ documentation guides covering architecture, design system, testing patterns, deployment, and AI code generation guidelines. Interactive API docs with Swagger and ReDoc',
|
||||
highlight: 'Developer-first docs',
|
||||
ctaText: 'Browse Docs',
|
||||
ctaHref: 'https://github.com/your-org/fast-next-template#documentation',
|
||||
ctaHref: 'https://gitea.pragmazest.com/cardosofelipe/syndarix#documentation',
|
||||
},
|
||||
{
|
||||
icon: Server,
|
||||
@@ -53,7 +53,7 @@ const features = [
|
||||
'Docker deployment configs, database migrations with Alembic helpers, connection pooling, health checks, monitoring setup, and production security headers',
|
||||
highlight: 'Deploy with confidence',
|
||||
ctaText: 'Deployment Guide',
|
||||
ctaHref: 'https://github.com/your-org/fast-next-template#deployment',
|
||||
ctaHref: 'https://gitea.pragmazest.com/cardosofelipe/syndarix#deployment',
|
||||
},
|
||||
{
|
||||
icon: Code,
|
||||
|
||||
@@ -48,13 +48,13 @@ export function Header({ onOpenDemoModal }: HeaderProps) {
|
||||
>
|
||||
<Image
|
||||
src="/logo-icon.svg"
|
||||
alt="PragmaStack Logo"
|
||||
alt="Syndarix Logo"
|
||||
width={32}
|
||||
height={32}
|
||||
className="h-8 w-8"
|
||||
/>
|
||||
<span className="bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
|
||||
PragmaStack
|
||||
Syndarix
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -72,7 +72,7 @@ export function Header({ onOpenDemoModal }: HeaderProps) {
|
||||
|
||||
{/* GitHub Link with Star */}
|
||||
<a
|
||||
href="https://github.com/your-org/fast-next-template"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
@@ -135,7 +135,7 @@ export function Header({ onOpenDemoModal }: HeaderProps) {
|
||||
|
||||
{/* GitHub Link */}
|
||||
<a
|
||||
href="https://github.com/your-org/fast-next-template"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
|
||||
@@ -72,7 +72,7 @@ export function HeroSection({ onOpenDemoModal }: HeroSectionProps) {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
Opinionated, secure, and production-ready. PragmaStack gives you the solid foundation
|
||||
Opinionated, secure, and production-ready. Syndarix gives you the solid foundation
|
||||
you need to stop configuring and start shipping.{' '}
|
||||
<span className="text-foreground font-medium">Start building features on day one.</span>
|
||||
</motion.p>
|
||||
@@ -93,7 +93,7 @@ export function HeroSection({ onOpenDemoModal }: HeroSectionProps) {
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="outline" className="gap-2 text-base group">
|
||||
<a
|
||||
href="https://github.com/your-org/fast-next-template"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
||||
@@ -33,7 +33,7 @@ export function PhilosophySection() {
|
||||
viewport={{ once: true, margin: '-100px' }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-6">Why PragmaStack?</h2>
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-6">Why Syndarix?</h2>
|
||||
<div className="space-y-4 text-lg text-muted-foreground leading-relaxed">
|
||||
<p>
|
||||
We built this template after rebuilding the same authentication, authorization, and
|
||||
|
||||
@@ -13,8 +13,8 @@ import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const codeString = `# Clone and start with Docker
|
||||
git clone https://github.com/your-org/fast-next-template.git
|
||||
cd fast-next-template
|
||||
git clone https://gitea.pragmazest.com/cardosofelipe/syndarix.git
|
||||
cd syndarix
|
||||
docker-compose up
|
||||
|
||||
# Or set up locally
|
||||
|
||||
@@ -18,12 +18,12 @@ export function Footer() {
|
||||
<div className="flex items-center gap-2 text-center text-sm text-muted-foreground md:text-left">
|
||||
<Image
|
||||
src="/logo-icon.svg"
|
||||
alt="PragmaStack Logo"
|
||||
alt="Syndarix Logo"
|
||||
width={20}
|
||||
height={20}
|
||||
className="h-5 w-5 opacity-70"
|
||||
/>
|
||||
<span>© {currentYear} PragmaStack. All rights reserved.</span>
|
||||
<span>© {currentYear} Syndarix. All rights reserved.</span>
|
||||
</div>
|
||||
<div className="flex space-x-6">
|
||||
<Link
|
||||
@@ -33,7 +33,7 @@ export function Footer() {
|
||||
Settings
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/cardosofelipe/pragmastack"
|
||||
href="https://gitea.pragmazest.com/cardosofelipe/syndarix"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
|
||||
@@ -86,12 +86,12 @@ export function Header() {
|
||||
<Link href="/" className="flex items-center space-x-2">
|
||||
<Image
|
||||
src="/logo-icon.svg"
|
||||
alt="PragmaStack Logo"
|
||||
alt="Syndarix Logo"
|
||||
width={32}
|
||||
height={32}
|
||||
className="h-8 w-8"
|
||||
/>
|
||||
<span className="text-xl font-bold text-foreground">PragmaStack</span>
|
||||
<span className="text-xl font-bold text-foreground">Syndarix</span>
|
||||
</Link>
|
||||
|
||||
{/* Navigation Links */}
|
||||
|
||||
@@ -13,8 +13,8 @@ export type Locale = 'en' | 'it';
|
||||
*/
|
||||
export const siteConfig = {
|
||||
name: {
|
||||
en: 'PragmaStack',
|
||||
it: 'PragmaStack',
|
||||
en: 'Syndarix',
|
||||
it: 'Syndarix',
|
||||
},
|
||||
description: {
|
||||
en: 'Production-ready FastAPI + Next.js full-stack template with authentication, admin panel, and comprehensive testing',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Tests for Home Page
|
||||
* Tests for the new PragmaStack landing page
|
||||
* Tests for the new Syndarix landing page
|
||||
*/
|
||||
|
||||
import { render, screen, within, fireEvent } from '@testing-library/react';
|
||||
@@ -87,13 +87,13 @@ describe('HomePage', () => {
|
||||
it('renders header with logo', () => {
|
||||
render(<Home />);
|
||||
const header = screen.getByRole('banner');
|
||||
expect(within(header).getByText('PragmaStack')).toBeInTheDocument();
|
||||
expect(within(header).getByText('Syndarix')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders footer with copyright', () => {
|
||||
render(<Home />);
|
||||
const footer = screen.getByRole('contentinfo');
|
||||
expect(within(footer).getByText(/PragmaStack. MIT Licensed/i)).toBeInTheDocument();
|
||||
expect(within(footer).getByText(/Syndarix. MIT Licensed/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,7 +210,7 @@ describe('HomePage', () => {
|
||||
describe('Philosophy Section', () => {
|
||||
it('renders why this template exists', () => {
|
||||
render(<Home />);
|
||||
expect(screen.getByText(/Why PragmaStack\?/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Why Syndarix\?/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders what you wont find section', () => {
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('CTASection', () => {
|
||||
);
|
||||
|
||||
const githubLink = screen.getByRole('link', { name: /get started on github/i });
|
||||
expect(githubLink).toHaveAttribute('href', 'https://github.com/your-org/fast-next-template');
|
||||
expect(githubLink).toHaveAttribute('href', 'https://gitea.pragmazest.com/cardosofelipe/syndarix');
|
||||
expect(githubLink).toHaveAttribute('target', '_blank');
|
||||
expect(githubLink).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
@@ -101,7 +101,7 @@ describe('CTASection', () => {
|
||||
const docsLink = screen.getByRole('link', { name: /read documentation/i });
|
||||
expect(docsLink).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/your-org/fast-next-template#documentation'
|
||||
'https://gitea.pragmazest.com/cardosofelipe/syndarix#documentation'
|
||||
);
|
||||
expect(docsLink).toHaveAttribute('target', '_blank');
|
||||
expect(docsLink).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('Header', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('PragmaStack')).toBeInTheDocument();
|
||||
expect(screen.getByText('Syndarix')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('logo links to homepage', () => {
|
||||
@@ -67,7 +67,7 @@ describe('Header', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
const logoLink = screen.getByRole('link', { name: /PragmaStack/i });
|
||||
const logoLink = screen.getByRole('link', { name: /Syndarix/i });
|
||||
expect(logoLink).toHaveAttribute('href', '/');
|
||||
});
|
||||
|
||||
@@ -97,12 +97,12 @@ describe('Header', () => {
|
||||
|
||||
const githubLinks = screen.getAllByRole('link', { name: /github/i });
|
||||
const desktopGithubLink = githubLinks.find((link) =>
|
||||
link.getAttribute('href')?.includes('github.com')
|
||||
link.getAttribute('href')?.includes('gitea.pragmazest.com')
|
||||
);
|
||||
|
||||
expect(desktopGithubLink).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/your-org/fast-next-template'
|
||||
'https://gitea.pragmazest.com/cardosofelipe/syndarix'
|
||||
);
|
||||
expect(desktopGithubLink).toHaveAttribute('target', '_blank');
|
||||
expect(desktopGithubLink).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('HeroSection', () => {
|
||||
);
|
||||
|
||||
const githubLink = screen.getByRole('link', { name: /view on github/i });
|
||||
expect(githubLink).toHaveAttribute('href', 'https://github.com/your-org/fast-next-template');
|
||||
expect(githubLink).toHaveAttribute('href', 'https://gitea.pragmazest.com/cardosofelipe/syndarix');
|
||||
expect(githubLink).toHaveAttribute('target', '_blank');
|
||||
expect(githubLink).toHaveAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Footer', () => {
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
expect(
|
||||
screen.getByText(`© ${currentYear} PragmaStack. All rights reserved.`)
|
||||
screen.getByText(`© ${currentYear} Syndarix. All rights reserved.`)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('Header', () => {
|
||||
|
||||
render(<Header />);
|
||||
|
||||
expect(screen.getByText('PragmaStack')).toBeInTheDocument();
|
||||
expect(screen.getByText('Syndarix')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders theme toggle', () => {
|
||||
|
||||
@@ -27,8 +27,8 @@ describe('metadata utilities', () => {
|
||||
});
|
||||
|
||||
it('should have English and Italian names', () => {
|
||||
expect(siteConfig.name.en).toBe('PragmaStack');
|
||||
expect(siteConfig.name.it).toBe('PragmaStack');
|
||||
expect(siteConfig.name.en).toBe('Syndarix');
|
||||
expect(siteConfig.name.it).toBe('Syndarix');
|
||||
});
|
||||
|
||||
it('should have English and Italian descriptions', () => {
|
||||
|
||||
Reference in New Issue
Block a user