fix: consolidate legacy CSS, fix dynamic routes, convert anchors to Link components
- Remove duplicate App.css, consolidate into index.css as single Tailwind entry point - Move maxWidth.container to tailwind.config.js theme extension - Update App.jsx import from ./App.css to ./index.css - Fix router.jsx to use dynamic :slug routes for services and industries - Fix ServiceDetail.jsx and IndustryDetail.jsx to use useParams() - Convert Header.jsx and MobileNav.jsx <a> tags to React Router <Link> components - Add scripts/docker-test.sh for persistence verification - Add project-requirements.md
This commit is contained in:
parent
a7fa18ec63
commit
87203bcded
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Project Requirements — Queue North Website
|
||||||
|
|
||||||
|
These requirements apply to all agents working on Queue North Website.
|
||||||
|
|
||||||
|
## Project Philosophy
|
||||||
|
|
||||||
|
- Feel modern for 2026 standards
|
||||||
|
- Prioritize responsiveness and reactivity
|
||||||
|
- Provide smooth user interaction
|
||||||
|
- Avoid outdated UI/UX patterns
|
||||||
|
- Maintain fast perceived performance
|
||||||
|
- Remain lightweight and maintainable
|
||||||
|
- Prioritize usability over unnecessary complexity
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
- **Build:** Vite
|
||||||
|
- **Frontend:** React 19 with client-side routing (React Router 7)
|
||||||
|
- **Styling:** Tailwind CSS with custom Queue North theme
|
||||||
|
- **UI Components:** shadcn/ui-style local primitives (Button, Card, Input, etc.)
|
||||||
|
- **State:** TanStack Query for server state
|
||||||
|
- **Notifications:** Sonner (toast)
|
||||||
|
- **Backend:** Express (Node.js)
|
||||||
|
- **Database:** SQLite via better-sqlite3
|
||||||
|
- **NOT Next.js.** This project uses Vite + React SPA, not Next.js App Router.
|
||||||
|
|
||||||
|
## Frontend Standards
|
||||||
|
|
||||||
|
- React SPA with React Router (no SSR, no server components)
|
||||||
|
- shadcn/ui-style primitives in `src/components/ui/`
|
||||||
|
- Tailwind utilities cleanly and predictably
|
||||||
|
- Responsive design (mobile + desktop)
|
||||||
|
- Loading states, error states, accessible interfaces
|
||||||
|
- Queue North brand colors: navy, light blue, white palette
|
||||||
|
- Georgia font for numeric content
|
||||||
|
|
||||||
|
## Backend Standards
|
||||||
|
|
||||||
|
- Express.js REST API
|
||||||
|
- SQLite via better-sqlite3
|
||||||
|
- Lead capture endpoints (`/api/leads`, `/api/support`)
|
||||||
|
- Validate all input, sanitize user-supplied data
|
||||||
|
- Structured error handling, no silent failures
|
||||||
|
- Environment variables for configuration, no hardcoded secrets
|
||||||
|
|
||||||
|
## Database Standards
|
||||||
|
|
||||||
|
- SQLite only
|
||||||
|
- Validate schema changes before deployment
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
- Readable, maintainable, no overengineering
|
||||||
|
- Remove dead code, consistent formatting
|
||||||
|
- Document non-obvious logic
|
||||||
|
- Prefer clarity over cleverness
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- OWASP best practices
|
||||||
|
- Input validation on all endpoints
|
||||||
|
- No secrets in logs
|
||||||
|
- Review dependencies for vulnerabilities
|
||||||
|
|
||||||
|
## Requirement Change Policy
|
||||||
|
|
||||||
|
Requirements may NOT be modified without explicit approval from `_null`.
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Docker persistence test script for Queue North Website
|
||||||
|
# Verifies SQLite database survives container restart with volume mount
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Docker Persistence Test for Queue North Website ==="
|
||||||
|
|
||||||
|
# Stop any existing container
|
||||||
|
docker stop queuenorth-test 2>/dev/null || true
|
||||||
|
docker rm queuenorth-test 2>/dev/null || true
|
||||||
|
|
||||||
|
# Remove any existing volumes for fresh start
|
||||||
|
docker volume rm queuenorth-test-db 2>/dev/null || true
|
||||||
|
docker volume rm queuenorth-test-logs 2>/dev/null || true
|
||||||
|
|
||||||
|
# Build fresh image
|
||||||
|
echo "Building fresh Docker image..."
|
||||||
|
docker build -t queuenorth-test .
|
||||||
|
|
||||||
|
# Create volumes for persistence
|
||||||
|
docker volume create queuenorth-test-db > /dev/null
|
||||||
|
docker volume create queuenorth-test-logs > /dev/null
|
||||||
|
|
||||||
|
# Run container with volume mount
|
||||||
|
echo "Starting container with persistent volume..."
|
||||||
|
docker run -d \
|
||||||
|
--name queuenorth-test \
|
||||||
|
-p 3002:3001 \
|
||||||
|
-v queuenorth-test-db:/app/db \
|
||||||
|
-v queuenorth-test-logs:/app/logs \
|
||||||
|
-e NODE_ENV=production \
|
||||||
|
-e SERVER_PORT=3001 \
|
||||||
|
queuenorth-test
|
||||||
|
|
||||||
|
# Wait for server to be ready
|
||||||
|
echo "Waiting for server to be ready..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Verify health endpoint
|
||||||
|
if curl -s http://localhost:3002/api/health | grep -q '"status":"ok"'; then
|
||||||
|
echo "✓ Health check passed"
|
||||||
|
else
|
||||||
|
echo "✗ Health check failed"
|
||||||
|
docker logs queuenorth-test
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Insert test data via API
|
||||||
|
echo "Inserting test lead data..."
|
||||||
|
curl -s -X POST http://localhost:3002/api/leads \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"company":"Docker Test Co","name":"Docker Test User","email":"docker@test.com","phone":"555-DOCKER","zip":"54321","message":"Docker persistence test","service_interest":"contact-center"}' > /dev/null
|
||||||
|
|
||||||
|
echo "Inserting test support request data..."
|
||||||
|
curl -s -X POST http://localhost:3002/api/support \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"Docker Test User","company":"Docker Test Co","email":"docker@test.com","phone":"555-DOCKER","issue":"Docker persistence test support request","priority":"medium"}' > /dev/null
|
||||||
|
|
||||||
|
# Verify data was inserted using Node.js (sqlite3 CLI not available in container)
|
||||||
|
echo "Verifying data in database..."
|
||||||
|
|
||||||
|
# Check leads table
|
||||||
|
if docker exec queuenorth-test node -e "
|
||||||
|
const sqlite3 = require('better-sqlite3');
|
||||||
|
const db = new sqlite3('/app/db/queuenorth.db');
|
||||||
|
const row = db.prepare('SELECT COUNT(*) as c FROM leads WHERE email=?').get('docker@test.com');
|
||||||
|
console.log(row.c);
|
||||||
|
process.exit(row.c === 1 ? 0 : 1);
|
||||||
|
" 2>/dev/null; then
|
||||||
|
echo "✓ Test lead data persisted in database"
|
||||||
|
else
|
||||||
|
echo "✗ Test lead data NOT persisted in database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check support_requests table
|
||||||
|
if docker exec queuenorth-test node -e "
|
||||||
|
const sqlite3 = require('better-sqlite3');
|
||||||
|
const db = new sqlite3('/app/db/queuenorth.db');
|
||||||
|
const row = db.prepare('SELECT COUNT(*) as c FROM support_requests WHERE email=?').get('docker@test.com');
|
||||||
|
console.log(row.c);
|
||||||
|
process.exit(row.c === 1 ? 0 : 1);
|
||||||
|
" 2>/dev/null; then
|
||||||
|
echo "✓ Test support request data persisted in database"
|
||||||
|
else
|
||||||
|
echo "✗ Test support request data NOT persisted in database"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop the container (simulates restart)
|
||||||
|
echo "Stopping container to simulate restart..."
|
||||||
|
docker stop queuenorth-test > /dev/null
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Restart with same volume mount
|
||||||
|
echo "Restarting container with same volume..."
|
||||||
|
docker start queuenorth-test > /dev/null
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Verify data is still there after restart
|
||||||
|
echo "Verifying data persists after restart..."
|
||||||
|
|
||||||
|
# Check leads after restart
|
||||||
|
if docker exec queuenorth-test node -e "
|
||||||
|
const sqlite3 = require('better-sqlite3');
|
||||||
|
const db = new sqlite3('/app/db/queuenorth.db');
|
||||||
|
const row = db.prepare('SELECT COUNT(*) as c FROM leads WHERE email=?').get('docker@test.com');
|
||||||
|
console.log(row.c);
|
||||||
|
process.exit(row.c === 1 ? 0 : 1);
|
||||||
|
" 2>/dev/null; then
|
||||||
|
echo "✓ Data persists after container restart"
|
||||||
|
else
|
||||||
|
echo "✗ Data NOT persisted after container restart"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check support_requests after restart
|
||||||
|
if docker exec queuenorth-test node -e "
|
||||||
|
const sqlite3 = require('better-sqlite3');
|
||||||
|
const db = new sqlite3('/app/db/queuenorth.db');
|
||||||
|
const row = db.prepare('SELECT COUNT(*) as c FROM support_requests WHERE email=?').get('docker@test.com');
|
||||||
|
console.log(row.c);
|
||||||
|
process.exit(row.c === 1 ? 0 : 1);
|
||||||
|
" 2>/dev/null; then
|
||||||
|
echo "✓ Support requests data persists after container restart"
|
||||||
|
else
|
||||||
|
echo "✗ Support requests data NOT persisted after container restart"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
echo "Cleaning up..."
|
||||||
|
docker stop queuenorth-test > /dev/null
|
||||||
|
docker rm queuenorth-test > /dev/null
|
||||||
|
docker volume rm queuenorth-test-db > /dev/null
|
||||||
|
docker volume rm queuenorth-test-logs > /dev/null
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== All Docker persistence tests passed! ==="
|
||||||
|
echo "SQLite database correctly persists across container restarts with volume mount."
|
||||||
69
src/App.css
69
src/App.css
|
|
@ -1,69 +0,0 @@
|
||||||
/* App styles */
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
color: #0F172A;
|
|
||||||
background-color: #F8FAFC;
|
|
||||||
line-height: 1.5;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0EA5E9;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Section spacing - mobile first */
|
|
||||||
.section {
|
|
||||||
padding: 4rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Desktop section spacing */
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.section {
|
|
||||||
padding: 6rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero section styling */
|
|
||||||
.hero {
|
|
||||||
min-height: 70vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background: linear-gradient(135deg, #0B2A3C 0%, #071A2A 100%);
|
|
||||||
color: white;
|
|
||||||
padding: 4rem 0 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Light section background */
|
|
||||||
.section-alt {
|
|
||||||
background: #EEF6FB;
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { Outlet } from 'react-router-dom'
|
||||||
import Header from './components/layout/Header.jsx'
|
import Header from './components/layout/Header.jsx'
|
||||||
import Footer from './components/layout/Footer.jsx'
|
import Footer from './components/layout/Footer.jsx'
|
||||||
import MobileNav from './components/layout/MobileNav.jsx'
|
import MobileNav from './components/layout/MobileNav.jsx'
|
||||||
import './App.css'
|
import './index.css'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { SheetTrigger } from '@/components/ui/Sheet'
|
import { SheetTrigger } from '@/components/ui/Sheet'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const [isScrolled, setIsScrolled] = useState(false)
|
const [isScrolled, setIsScrolled] = useState(false)
|
||||||
|
|
@ -40,21 +41,21 @@ const Header = () => {
|
||||||
{/* Desktop Nav */}
|
{/* Desktop Nav */}
|
||||||
<nav className="hidden md:flex items-center gap-6">
|
<nav className="hidden md:flex items-center gap-6">
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) => (
|
||||||
<a
|
<Link
|
||||||
key={link.name}
|
key={link.name}
|
||||||
href={link.href}
|
to={link.href}
|
||||||
className="text-sm font-medium text-navy-light hover:text-white transition-colors"
|
className="text-sm font-medium text-navy-light hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
</a>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* CTA Button */}
|
{/* CTA Button */}
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
<a href="/contact" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors">
|
<Link to="/contact" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors">
|
||||||
Request Consultation
|
Request Consultation
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mobile Menu Toggle */}
|
{/* Mobile Menu Toggle */}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/Sheet'
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/Sheet'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
const MobileNav = () => {
|
const MobileNav = () => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
@ -71,13 +72,13 @@ const MobileNav = () => {
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{primaryLinks.map((link) => (
|
{primaryLinks.map((link) => (
|
||||||
<li key={link.name}>
|
<li key={link.name}>
|
||||||
<a
|
<Link
|
||||||
href={link.href}
|
to={link.href}
|
||||||
onClick={closeMobileMenu}
|
onClick={closeMobileMenu}
|
||||||
className="block text-base font-medium text-navy-light hover:text-white transition-colors py-2"
|
className="block text-base font-medium text-navy-light hover:text-white transition-colors py-2"
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -89,13 +90,13 @@ const MobileNav = () => {
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{services.map((service) => (
|
{services.map((service) => (
|
||||||
<li key={service.name}>
|
<li key={service.name}>
|
||||||
<a
|
<Link
|
||||||
href={service.href}
|
to={service.href}
|
||||||
onClick={closeMobileMenu}
|
onClick={closeMobileMenu}
|
||||||
className="block text-sm text-navy-light hover:text-white transition-colors py-2 border-b border-white/10 last:border-0"
|
className="block text-sm text-navy-light hover:text-white transition-colors py-2 border-b border-white/10 last:border-0"
|
||||||
>
|
>
|
||||||
{service.name}
|
{service.name}
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -107,13 +108,13 @@ const MobileNav = () => {
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{industries.map((industry) => (
|
{industries.map((industry) => (
|
||||||
<li key={industry.name}>
|
<li key={industry.name}>
|
||||||
<a
|
<Link
|
||||||
href={industry.href}
|
to={industry.href}
|
||||||
onClick={closeMobileMenu}
|
onClick={closeMobileMenu}
|
||||||
className="block text-sm text-navy-light hover:text-white transition-colors py-2 border-b border-white/10 last:border-0"
|
className="block text-sm text-navy-light hover:text-white transition-colors py-2 border-b border-white/10 last:border-0"
|
||||||
>
|
>
|
||||||
{industry.name}
|
{industry.name}
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -121,13 +122,13 @@ const MobileNav = () => {
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="mt-auto pt-6">
|
<div className="mt-auto pt-6">
|
||||||
<a
|
<Link
|
||||||
href="/contact"
|
to="/contact"
|
||||||
onClick={closeMobileMenu}
|
onClick={closeMobileMenu}
|
||||||
className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 w-full bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors"
|
className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 w-full bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors"
|
||||||
>
|
>
|
||||||
Request Consultation
|
Request Consultation
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Container - custom max-width */
|
||||||
.container {
|
.container {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
import { industries } from '@/data/industries'
|
import { industries } from '@/data/industries'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
||||||
|
|
||||||
const IndustryDetail = ({ name }) => {
|
const IndustryDetail = () => {
|
||||||
const industry = industries.find(i => i.id === name)
|
const { slug } = useParams()
|
||||||
|
const industry = industries.find(i => i.id === slug)
|
||||||
|
|
||||||
if (!industry) {
|
if (!industry) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
import { services } from '@/data/services'
|
import { services } from '@/data/services'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
||||||
|
|
||||||
const ServiceDetail = ({ name }) => {
|
const ServiceDetail = () => {
|
||||||
const service = services.find(s => s.id === name)
|
const { slug } = useParams()
|
||||||
|
const service = services.find(s => s.id === slug)
|
||||||
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,9 @@ const router = createBrowserRouter([
|
||||||
{ index: true, element: <Home /> },
|
{ index: true, element: <Home /> },
|
||||||
{ path: 'about', element: <About /> },
|
{ path: 'about', element: <About /> },
|
||||||
{ path: 'services', element: <Services /> },
|
{ path: 'services', element: <Services /> },
|
||||||
{ path: 'services/unified-communications', element: <ServiceDetail name="unified-communications" /> },
|
{ path: 'services/:slug', element: <ServiceDetail /> },
|
||||||
{ path: 'services/contact-center', element: <ServiceDetail name="contact-center" /> },
|
|
||||||
{ path: 'services/managed-support', element: <ServiceDetail name="managed-support" /> },
|
|
||||||
{ path: 'services/consulting-training', element: <ServiceDetail name="consulting-training" /> },
|
|
||||||
{ path: 'services/infrastructure-cabling', element: <ServiceDetail name="infrastructure-cabling" /> },
|
|
||||||
{ path: 'services/wireless-access', element: <ServiceDetail name="wireless-access" /> },
|
|
||||||
{ path: 'services/local-networking', element: <ServiceDetail name="local-networking" /> },
|
|
||||||
{ path: 'industries', element: <Industries /> },
|
{ path: 'industries', element: <Industries /> },
|
||||||
{ path: 'industries/healthcare', element: <IndustryDetail name="healthcare" /> },
|
{ path: 'industries/:slug', element: <IndustryDetail /> },
|
||||||
{ path: 'industries/retail', element: <IndustryDetail name="retail" /> },
|
|
||||||
{ path: 'industries/manufacturing', element: <IndustryDetail name="manufacturing" /> },
|
|
||||||
{ path: 'industries/education-finance', element: <IndustryDetail name="education-finance" /> },
|
|
||||||
{ path: '8x8', element: <EightXEight /> },
|
{ path: '8x8', element: <EightXEight /> },
|
||||||
{ path: 'contact', element: <Contact /> },
|
{ path: 'contact', element: <Contact /> },
|
||||||
{ path: 'support', element: <Support /> },
|
{ path: 'support', element: <Support /> },
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ export default {
|
||||||
'40': '10rem',
|
'40': '10rem',
|
||||||
'48': '12rem',
|
'48': '12rem',
|
||||||
},
|
},
|
||||||
|
maxWidth: {
|
||||||
|
'container': '1280px',
|
||||||
|
},
|
||||||
boxShadow: {
|
boxShadow: {
|
||||||
'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
||||||
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
|
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue