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:
2025-12-29 13:30:45 +01:00
parent 6e3cdebbfb
commit ebd307cab4
38 changed files with 159 additions and 146 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -1,4 +1,4 @@
# PragmaStack - Frontend
# Syndarix - Frontend
Production-ready Next.js 16 frontend with TypeScript, authentication, admin panel, and internationalization.

View File

@@ -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**

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.
---

View File

@@ -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.
---

View File

@@ -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.
---

View File

@@ -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.
---

View File

@@ -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');
});

View File

@@ -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,
};
}
}

View File

@@ -10,7 +10,7 @@ import { Footer } from '@/components/layout/Footer';
export const metadata: Metadata = {
title: {
template: '%s | PragmaStack',
template: '%s | Syndarix',
default: 'Dashboard',
},
};

View File

@@ -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',
},
};

View File

@@ -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 = [

View File

@@ -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">

View File

@@ -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>

View File

@@ -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">

View File

@@ -1,7 +1,7 @@
@import 'tailwindcss';
/**
* PragmaStack Design System
* Syndarix Design System
* Theme: Modern Minimal (from tweakcn.com)
* Primary: Blue | Color Space: OKLCH
*

View File

@@ -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>

View File

@@ -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 },

View File

@@ -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"
>

View File

@@ -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,

View File

@@ -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)}

View File

@@ -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"
>

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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 */}

View File

@@ -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',

View File

@@ -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', () => {

View File

@@ -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');

View File

@@ -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');

View File

@@ -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');
});

View File

@@ -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();
});

View File

@@ -63,7 +63,7 @@ describe('Header', () => {
render(<Header />);
expect(screen.getByText('PragmaStack')).toBeInTheDocument();
expect(screen.getByText('Syndarix')).toBeInTheDocument();
});
it('renders theme toggle', () => {

View File

@@ -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', () => {