- {isMenuOpen ? 'Close main menu' : 'Open main menu'}
+ {isMenuOpen ? common.a11y.closeMenu : common.a11y.openMenu}
{/* Hamburger to X animation */}
setIsMenuOpen(false)}
className={`block px-4 py-3 rounded-lg text-base font-medium transition-all duration-200 ${
- item.label === 'Kapcsolat' ? 'btn btn-primary text-white mt-2' : ''
+ item.primary ? 'btn btn-primary text-white mt-2' : ''
}`}
style={{
animationDelay: `${index * 50}ms`,
- ...(item.label !== 'Kapcsolat' ? {
+ ...(!item.primary ? {
color: 'var(--color-foreground)',
background: 'transparent',
} : {})
}}
onMouseEnter={(e) => {
- if (item.label !== 'Kapcsolat') {
+ if (!item.primary) {
e.currentTarget.style.background = 'var(--color-primary-50)'
e.currentTarget.style.color = 'var(--color-primary-600)'
}
}}
onMouseLeave={(e) => {
- if (item.label !== 'Kapcsolat') {
+ if (!item.primary) {
e.currentTarget.style.background = 'transparent'
e.currentTarget.style.color = 'var(--color-foreground)'
}
diff --git a/proto/src/config/site.ts b/proto/src/config/site.ts
index 543be18..c83a022 100755
--- a/proto/src/config/site.ts
+++ b/proto/src/config/site.ts
@@ -10,7 +10,7 @@ export const siteConfig: SiteConfig = {
name: 'mozdIT Bt.',
description: 'Megbízható web- és email szolgáltatás személyre szabott támogatással. Stabil tárhely, üzembiztos levelezés és DNS adminisztráció gyors reakcióval.',
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://localhost:3000',
- ogImage: '/og-image.png',
+ ogImage: '/mozdit_logo_text.png',
locale: 'hu-HU'
},
@@ -19,7 +19,7 @@ export const siteConfig: SiteConfig = {
{ label: 'Kezdőlap', href: '/' },
{ label: 'Rólunk', href: '/rolunk' },
{ label: 'Szolgáltatások', href: '/szolgaltatasok' },
- { label: 'Kapcsolat', href: '/kapcsolat' }
+ { label: 'Kapcsolat', href: '/kapcsolat', primary: true }
],
footer: [
{ label: 'Kezdőlap', href: '/' },
@@ -41,32 +41,7 @@ export const siteConfig: SiteConfig = {
contact: {
email: process.env.NEXT_PUBLIC_CONTACT_EMAIL || 'info@mozdit.hu',
- // address lives in content/common.json (footer.address) — CMS-editable
- form: {
- title: 'Kapcsolatfelvétel',
- description: 'Legyen szíves érdeklődését vagy problémáját részletesen megfogalmazni.',
- submitText: 'Üzenet küldése',
- fields: {
- name: {
- label: 'Név',
- placeholder: 'Vezetéknév Keresztnév',
- required: true
- },
- email: {
- label: 'Email cím',
- placeholder: 'pelda@email.hu',
- required: true
- },
- message: {
- label: 'Üzenet',
- placeholder: 'Kérjük írja le érdeklődését részletesen...',
- required: true
- },
- consent: {
- label: 'Elfogadom az adatkezelési tájékoztatót',
- required: true
- }
- }
- }
+ // address lives in content/common.json (footer.address) — CMS-editable.
+ // The form fields live in content/pages/contact.json.
}
}
\ No newline at end of file
diff --git a/proto/src/content/common.json b/proto/src/content/common.json
index 275dc23..464562e 100644
--- a/proto/src/content/common.json
+++ b/proto/src/content/common.json
@@ -20,5 +20,9 @@
"footer": {
"copyright": "© 2002–{year} mozdIT Bt. Minden jog fenntartva.",
"address": "Szigetszentmiklós, Magyarország"
+ },
+ "a11y": {
+ "openMenu": "Főmenü megnyitása",
+ "closeMenu": "Főmenü bezárása"
}
}
diff --git a/proto/src/content/schema.js b/proto/src/content/schema.js
index d14df02..e7693e9 100644
--- a/proto/src/content/schema.js
+++ b/proto/src/content/schema.js
@@ -16,6 +16,7 @@ const schemas = {
validation: object({ required: string, invalidEmail: string, minLength: string }),
staging: object({ banner: string }),
footer: object({ copyright: string, address: string }),
+ a11y: object({ openMenu: string, closeMenu: string }),
}),
home: object({
hero: object({ title: string, subtitle: string, description: string, trustBullets: array(string), cta: object({ primary: ctaLink, secondary: ctaLink }) }),
diff --git a/proto/src/content/types.ts b/proto/src/content/types.ts
index 74335c7..5d44066 100755
--- a/proto/src/content/types.ts
+++ b/proto/src/content/types.ts
@@ -197,6 +197,10 @@ export interface CommonContent {
copyright: string
address: string
}
+ a11y: {
+ openMenu: string
+ closeMenu: string
+ }
}
export interface LegalPageContent {
diff --git a/proto/src/lib/site-config.test.ts b/proto/src/lib/site-config.test.ts
deleted file mode 100755
index 2afcbb2..0000000
--- a/proto/src/lib/site-config.test.ts
+++ /dev/null
@@ -1,382 +0,0 @@
-import { getSiteConfig, saveSiteConfig, initializeDefaultConfig } from './site-config'
-import { getCollection } from './mongodb'
-import { siteConfig as staticConfig } from '@/config/site'
-import { SiteConfig } from '@/types/site'
-import logger from './logger'
-
-// Mock dependencies
-jest.mock('./mongodb')
-jest.mock('./logger', () => ({
- __esModule: true,
- default: {
- info: jest.fn(),
- warn: jest.fn(),
- error: jest.fn()
- }
-}))
-jest.mock('@/config/site', () => ({
- siteConfig: {
- general: {
- name: 'Test Site',
- description: 'Test Description',
- url: 'https://test.com',
- ogImage: 'https://test.com/og.jpg',
- locale: 'en'
- },
- navigation: {
- main: [
- { label: 'Home', href: '/' },
- { label: 'About', href: '/about' }
- ],
- footer: [
- { label: 'Privacy', href: '/privacy' },
- { label: 'Terms', href: '/terms' }
- ]
- },
- hero: {
- title: 'Welcome',
- subtitle: 'Test Subtitle',
- description: 'Test description',
- cta: {
- primary: {
- text: 'Get Started',
- href: '/get-started'
- }
- }
- },
- services: {
- title: 'Our Services',
- subtitle: 'What we offer',
- services: []
- },
- about: {
- title: 'About Us',
- description: ['Test about section'],
- usps: []
- },
- footer: {
- copyright: '© 2024 Test Site',
- links: []
- },
- contact: {
- email: 'test@test.com',
- address: 'Test Address',
- form: {
- title: 'Contact Us',
- description: 'Get in touch',
- submitText: 'Send Message',
- fields: {
- name: { label: 'Name', placeholder: 'Your name', required: true },
- email: { label: 'Email', placeholder: 'your@email.com', required: true },
- message: { label: 'Message', placeholder: 'Your message', required: true },
- consent: { label: 'I agree', required: true }
- }
- }
- }
- }
-}))
-
-const mockCollection = {
- findOne: jest.fn(),
- replaceOne: jest.fn()
-}
-
-
-
-describe('Site Config', () => {
- beforeEach(() => {
- jest.clearAllMocks()
- ;(getCollection as jest.Mock).mockResolvedValue(mockCollection)
- })
-
- describe('getSiteConfig', () => {
- it('should return MongoDB config when available', async () => {
- const mockConfig: SiteConfig = {
- general: {
- name: 'MongoDB Site',
- description: 'MongoDB Description',
- url: 'https://mongodb.com',
- ogImage: 'https://mongodb.com/og.jpg',
- locale: 'en'
- },
- navigation: {
- main: [{ label: 'Home', href: '/' }],
- footer: [{ label: 'Privacy', href: '/privacy' }]
- },
- hero: {
- title: 'MongoDB Hero',
- subtitle: 'MongoDB Subtitle',
- description: 'MongoDB Description',
- cta: {
- primary: {
- text: 'Get Started',
- href: '/get-started'
- }
- }
- },
- services: {
- title: 'Services',
- subtitle: 'Our services',
- services: []
- },
- about: {
- title: 'About',
- description: ['About us'],
- usps: []
- },
- footer: {
- copyright: '© 2024 MongoDB',
- links: []
- },
- contact: {
- email: 'mongodb@test.com',
- address: 'MongoDB Address',
- form: {
- title: 'Contact',
- description: 'Get in touch',
- submitText: 'Send',
- fields: {
- name: { label: 'Name', placeholder: 'Name', required: true },
- email: { label: 'Email', placeholder: 'Email', required: true },
- message: { label: 'Message', placeholder: 'Message', required: true },
- consent: { label: 'Consent', required: true }
- }
- }
- }
- }
-
- mockCollection.findOne.mockResolvedValue({
- _id: 'mock-id',
- data: mockConfig
- })
-
- const config = await getSiteConfig()
-
- expect(config).toEqual(mockConfig)
- expect(mockCollection.findOne).toHaveBeenCalledWith({
- type: 'site_config',
- environment: 'development'
- })
- expect(logger.info).toHaveBeenCalledWith(
- 'Loaded site config from MongoDB',
- { configId: 'mock-id' }
- )
- })
-
- it('should return static config when MongoDB config not found', async () => {
- mockCollection.findOne.mockResolvedValue(null)
-
- const config = await getSiteConfig()
-
- expect(config).toEqual(staticConfig)
- expect(logger.info).toHaveBeenCalledWith(
- 'MongoDB config not found, using static fallback'
- )
- })
-
- it('should return static config when MongoDB is unavailable', async () => {
- mockCollection.findOne.mockRejectedValue(new Error('Connection failed'))
-
- const config = await getSiteConfig()
-
- expect(config).toEqual(staticConfig)
- expect(logger.warn).toHaveBeenCalledWith(
- 'MongoDB unavailable, using static config',
- { error: 'Connection failed' }
- )
- })
- })
-
- describe('saveSiteConfig', () => {
- it('should save config to MongoDB successfully', async () => {
- const newConfig: SiteConfig = {
- general: {
- name: 'New Site',
- description: 'New Description',
- url: 'https://new.com',
- ogImage: 'https://new.com/og.jpg',
- locale: 'en'
- },
- navigation: {
- main: [{ label: 'Home', href: '/' }],
- footer: [{ label: 'Privacy', href: '/privacy' }]
- },
- hero: {
- title: 'New Hero',
- subtitle: 'New Subtitle',
- description: 'New Description',
- cta: {
- primary: {
- text: 'Get Started',
- href: '/get-started'
- }
- }
- },
- services: {
- title: 'Services',
- subtitle: 'Our services',
- services: []
- },
- about: {
- title: 'About',
- description: ['About us'],
- usps: []
- },
- footer: {
- copyright: '© 2024 New',
- links: []
- },
- contact: {
- email: 'new@test.com',
- address: 'New Address',
- form: {
- title: 'Contact',
- description: 'Get in touch',
- submitText: 'Send',
- fields: {
- name: { label: 'Name', placeholder: 'Name', required: true },
- email: { label: 'Email', placeholder: 'Email', required: true },
- message: { label: 'Message', placeholder: 'Message', required: true },
- consent: { label: 'Consent', required: true }
- }
- }
- }
- }
-
- mockCollection.replaceOne.mockResolvedValue({ acknowledged: true })
-
- const result = await saveSiteConfig(newConfig)
-
- expect(result).toBe(true)
- expect(mockCollection.replaceOne).toHaveBeenCalledWith(
- { type: 'site_config', environment: 'development' },
- {
- type: 'site_config',
- environment: 'development',
- data: newConfig,
- lastModified: expect.any(Date)
- },
- { upsert: true }
- )
- expect(logger.info).toHaveBeenCalledWith('Saved site config to MongoDB')
- })
-
- it('should return false when save fails', async () => {
- const newConfig: SiteConfig = {
- general: {
- name: 'New Site',
- description: 'New Description',
- url: 'https://new.com',
- ogImage: 'https://new.com/og.jpg',
- locale: 'en'
- },
- navigation: {
- main: [{ label: 'Home', href: '/' }],
- footer: [{ label: 'Privacy', href: '/privacy' }]
- },
- hero: {
- title: 'New Hero',
- subtitle: 'New Subtitle',
- description: 'New Description',
- cta: {
- primary: {
- text: 'Get Started',
- href: '/get-started'
- }
- }
- },
- services: {
- title: 'Services',
- subtitle: 'Our services',
- services: []
- },
- about: {
- title: 'About',
- description: ['About us'],
- usps: []
- },
- footer: {
- copyright: '© 2024 New',
- links: []
- },
- contact: {
- email: 'new@test.com',
- address: 'New Address',
- form: {
- title: 'Contact',
- description: 'Get in touch',
- submitText: 'Send',
- fields: {
- name: { label: 'Name', placeholder: 'Name', required: true },
- email: { label: 'Email', placeholder: 'Email', required: true },
- message: { label: 'Message', placeholder: 'Message', required: true },
- consent: { label: 'Consent', required: true }
- }
- }
- }
- }
-
- mockCollection.replaceOne.mockRejectedValue(new Error('Save failed'))
-
- const result = await saveSiteConfig(newConfig)
-
- expect(result).toBe(false)
- expect(logger.error).toHaveBeenCalledWith(
- 'Failed to save config to MongoDB',
- { error: 'Save failed' }
- )
- })
- })
-
- describe('initializeDefaultConfig', () => {
- it('should initialize default config when none exists', async () => {
- mockCollection.findOne.mockResolvedValue(null)
- mockCollection.replaceOne.mockResolvedValue({ acknowledged: true })
-
- await initializeDefaultConfig()
-
- expect(mockCollection.findOne).toHaveBeenCalledWith({
- type: 'site_config',
- environment: 'development'
- })
- expect(mockCollection.replaceOne).toHaveBeenCalledWith(
- { type: 'site_config', environment: 'development' },
- {
- type: 'site_config',
- environment: 'development',
- data: staticConfig,
- lastModified: expect.any(Date)
- },
- { upsert: true }
- )
- expect(logger.info).toHaveBeenCalledWith(
- 'Initialized default site config in MongoDB'
- )
- })
-
- it('should not initialize when config already exists', async () => {
- mockCollection.findOne.mockResolvedValue({
- _id: 'existing-id',
- data: staticConfig
- })
-
- await initializeDefaultConfig()
-
- expect(mockCollection.replaceOne).not.toHaveBeenCalled()
- expect(logger.info).toHaveBeenCalledWith(
- 'Site config already exists in MongoDB'
- )
- })
-
- it('should handle initialization errors gracefully', async () => {
- mockCollection.findOne.mockRejectedValue(new Error('Connection failed'))
-
- await initializeDefaultConfig()
-
- expect(logger.error).toHaveBeenCalledWith(
- 'Failed to initialize site config',
- { error: 'Connection failed' }
- )
- })
- })
-})
\ No newline at end of file
diff --git a/proto/src/lib/site-config.ts b/proto/src/lib/site-config.ts
deleted file mode 100755
index 8ba5835..0000000
--- a/proto/src/lib/site-config.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Hybrid approach: File-based fallback + MongoDB integration for future
-
-import { siteConfig as staticConfig } from '@/config/site'
-import { SiteConfig } from '@/types/site'
-import { getCollection } from './mongodb'
-import logger from './logger'
-
-/**
- * Get site configuration with hybrid approach
- * 1. Try MongoDB first (production-ready)
- * 2. Fall back to static file (development/development safe)
- */
-export async function getSiteConfig(): Promise {
- try {
- const collection = await getCollection('site_config')
- const doc = await collection.findOne({ type: 'site_config', environment: 'development' })
-
- if (doc && doc.data) {
- logger.info('Loaded site config from MongoDB', { configId: doc._id })
- return doc.data as SiteConfig
- } else {
- logger.info('MongoDB config not found, using static fallback')
- return staticConfig
- }
- } catch (error) {
- logger.warn('MongoDB unavailable, using static config', { error: (error as Error).message })
- return staticConfig
- }
-}
-
-/**
- * Save site configuration to MongoDB
- * For future admin panel integration
- */
-export async function saveSiteConfig(config: SiteConfig): Promise {
- try {
- const collection = await getCollection('site_config')
- await collection.replaceOne(
- { type: 'site_config', environment: 'development' },
- {
- type: 'site_config',
- environment: 'development',
- data: config,
- lastModified: new Date()
- },
- { upsert: true }
- )
-
- logger.info('Saved site config to MongoDB')
- return true
- } catch (error) {
- logger.error('Failed to save config to MongoDB', { error: (error as Error).message })
- return false
- }
-}
-
-/**
- * Initialize default config in MongoDB (one-time setup)
- */
-export async function initializeDefaultConfig(): Promise {
- try {
- const collection = await getCollection('site_config')
- const existingConfig = await collection.findOne({ type: 'site_config', environment: 'development' })
-
- if (!existingConfig) {
- await saveSiteConfig(staticConfig)
- logger.info('Initialized default site config in MongoDB')
- } else {
- logger.info('Site config already exists in MongoDB')
- }
- } catch (error) {
- logger.error('Failed to initialize site config', { error: (error as Error).message })
- }
-}
\ No newline at end of file
diff --git a/proto/src/types/site.ts b/proto/src/types/site.ts
index 092517e..ddd3baa 100755
--- a/proto/src/types/site.ts
+++ b/proto/src/types/site.ts
@@ -20,6 +20,7 @@ export interface NavigationItem {
label: string
href: string
external?: boolean
+ primary?: boolean
}
export interface NavigationConfig {
@@ -83,38 +84,11 @@ export interface FooterConfig {
export interface ContactConfig {
phone?: string
email: string
- address: string
socialMedia?: {
platform: string
url: string
label: string
}[]
- form: {
- title: string
- description: string
- submitText: string
- fields: {
- name: {
- label: string
- placeholder: string
- required: boolean
- }
- email: {
- label: string
- placeholder: string
- required: boolean
- }
- message: {
- label: string
- placeholder: string
- required: boolean
- }
- consent: {
- label: string
- required: boolean
- }
- }
- }
}
export interface EnvConfig {