From d1213f9b3f790a099b9406d54d7e9342ccafc4ae Mon Sep 17 00:00:00 2001 From: Do Siki Date: Sat, 24 Jan 2026 02:30:27 +0100 Subject: [PATCH] feat(ZEE-29): implement design system, dark mode, and JSON content management - Add comprehensive design system with CSS variables, animations, and utility classes - Implement dark mode with ThemeProvider (system preference + manual toggle) - Create JSON-based content management system in src/content/ - Update all pages to use structured JSON content - Add ThemeProvider component with theme toggle button - Update Header with glass effect and animated mobile menu - Update Footer with dark mode support - Update documentation (README.md, CLAUDE.md, knowledge.md, proto/README.md) - Sync with Linear (ZEE-29 completed) --- .agents/types/agent-definition.ts | 429 +++++++++++++++++++ .agents/types/tools.ts | 316 ++++++++++++++ .agents/types/util-types.ts | 175 ++++++++ CLAUDE.md | 45 +- README.md | 41 +- TODO.md | 8 +- TODO.md.backup.1769217893498 | 171 ++++++++ knowledge.md | 74 ++++ proto/README.md | 135 +++++- proto/src/app/globals.css | 556 ++++++++++++++++++++++++- proto/src/app/kapcsolat/layout.tsx | 11 +- proto/src/app/kapcsolat/page.tsx | 95 ++--- proto/src/app/layout.tsx | 32 +- proto/src/app/page.tsx | 264 +++++++++--- proto/src/app/rolunk/page.tsx | 244 ++++++----- proto/src/app/szolgaltatasok/page.tsx | 149 ++----- proto/src/components/Footer.tsx | 149 +++++-- proto/src/components/Header.tsx | 203 +++++++-- proto/src/components/ThemeProvider.tsx | 154 +++++++ proto/src/content/common.json | 17 + proto/src/content/index.ts | 62 +++ proto/src/content/pages/about.json | 53 +++ proto/src/content/pages/contact.json | 77 ++++ proto/src/content/pages/home.json | 10 + proto/src/content/pages/services.json | 94 +++++ proto/src/content/types.ts | 178 ++++++++ proto/tsconfig.json | 2 +- 27 files changed, 3294 insertions(+), 450 deletions(-) create mode 100644 .agents/types/agent-definition.ts create mode 100644 .agents/types/tools.ts create mode 100644 .agents/types/util-types.ts create mode 100644 TODO.md.backup.1769217893498 create mode 100644 knowledge.md create mode 100644 proto/src/components/ThemeProvider.tsx create mode 100644 proto/src/content/common.json create mode 100644 proto/src/content/index.ts create mode 100644 proto/src/content/pages/about.json create mode 100644 proto/src/content/pages/contact.json create mode 100644 proto/src/content/pages/home.json create mode 100644 proto/src/content/pages/services.json create mode 100644 proto/src/content/types.ts diff --git a/.agents/types/agent-definition.ts b/.agents/types/agent-definition.ts new file mode 100644 index 0000000..f449cfe --- /dev/null +++ b/.agents/types/agent-definition.ts @@ -0,0 +1,429 @@ +/** + * Codebuff Agent Type Definitions + * + * This file provides TypeScript type definitions for creating custom Codebuff agents. + * Import these types in your agent files to get full type safety and IntelliSense. + * + * Usage in .agents/your-agent.ts: + * import { AgentDefinition, ToolName, ModelName } from './types/agent-definition' + * + * const definition: AgentDefinition = { + * // ... your agent configuration with full type safety ... + * } + * + * export default definition + */ + +// ============================================================================ +// Agent Definition and Utility Types +// ============================================================================ + +export interface AgentDefinition { + /** Unique identifier for this agent. Must contain only lowercase letters, numbers, and hyphens, e.g. 'code-reviewer' */ + id: string + + /** Version string (if not provided, will default to '0.0.1' and be bumped on each publish) */ + version?: string + + /** Publisher ID for the agent. Must be provided if you want to publish the agent. */ + publisher?: string + + /** Human-readable name for the agent */ + displayName: string + + /** AI model to use for this agent. Can be any model in OpenRouter: https://openrouter.ai/models */ + model: ModelName + + /** + * https://openrouter.ai/docs/use-cases/reasoning-tokens + * One of `max_tokens` or `effort` is required. + * If `exclude` is true, reasoning will be removed from the response. Default is false. + */ + reasoningOptions?: { + enabled?: boolean + exclude?: boolean + } & ( + | { + max_tokens: number + } + | { + effort: 'high' | 'medium' | 'low' | 'minimal' | 'none' + } + ) + + /** + * Provider routing options for OpenRouter. + * Controls which providers to use and fallback behavior. + * See https://openrouter.ai/docs/features/provider-routing + */ + providerOptions?: { + /** + * List of provider slugs to try in order (e.g. ["anthropic", "openai"]) + */ + order?: string[] + /** + * Whether to allow backup providers when primary is unavailable (default: true) + */ + allow_fallbacks?: boolean + /** + * Only use providers that support all parameters in your request (default: false) + */ + require_parameters?: boolean + /** + * Control whether to use providers that may store data + */ + data_collection?: 'allow' | 'deny' + /** + * List of provider slugs to allow for this request + */ + only?: string[] + /** + * List of provider slugs to skip for this request + */ + ignore?: string[] + /** + * List of quantization levels to filter by (e.g. ["int4", "int8"]) + */ + quantizations?: Array< + | 'int4' + | 'int8' + | 'fp4' + | 'fp6' + | 'fp8' + | 'fp16' + | 'bf16' + | 'fp32' + | 'unknown' + > + /** + * Sort providers by price, throughput, or latency + */ + sort?: 'price' | 'throughput' | 'latency' + /** + * Maximum pricing you want to pay for this request + */ + max_price?: { + prompt?: number | string + completion?: number | string + image?: number | string + audio?: number | string + request?: number | string + } + } + + // ============================================================================ + // Tools and Subagents + // ============================================================================ + + /** MCP servers by name. Names cannot contain `/`. */ + mcpServers?: Record + + /** + * Tools this agent can use. + * + * By default, all tools are available from any specified MCP server. In + * order to limit the tools from a specific MCP server, add the tool name(s) + * in the format `'mcpServerName/toolName1'`, `'mcpServerName/toolName2'`, + * etc. + */ + toolNames?: (ToolName | (string & {}))[] + + /** Other agents this agent can spawn, like 'codebuff/file-picker@0.0.1'. + * + * Use the fully qualified agent id from the agent store, including publisher and version: 'codebuff/file-picker@0.0.1' + * (publisher and version are required!) + * + * Or, use the agent id from a local agent file in your .agents directory: 'file-picker'. + */ + spawnableAgents?: string[] + + // ============================================================================ + // Input and Output + // ============================================================================ + + /** The input schema required to spawn the agent. Provide a prompt string and/or a params object or none. + * 80% of the time you want just a prompt string with a description: + * inputSchema: { + * prompt: { type: 'string', description: 'A description of what info would be helpful to the agent' } + * } + */ + inputSchema?: { + prompt?: { type: 'string'; description?: string } + params?: JsonObjectSchema + } + + /** How the agent should output a response to its parent (defaults to 'last_message') + * + * last_message: The last message from the agent, typically after using tools. + * + * all_messages: All messages from the agent, including tool calls and results. + * + * structured_output: Make the agent output a JSON object. Can be used with outputSchema or without if you want freeform json output. + */ + outputMode?: 'last_message' | 'all_messages' | 'structured_output' + + /** JSON schema for structured output (when outputMode is 'structured_output') */ + outputSchema?: JsonObjectSchema + + // ============================================================================ + // Prompts + // ============================================================================ + + /** Prompt for when and why to spawn this agent. Include the main purpose and use cases. + * + * This field is key if the agent is intended to be spawned by other agents. */ + spawnerPrompt?: string + + /** Whether to include conversation history from the parent agent in context. + * + * Defaults to false. + * Use this when the agent needs to know all the previous messages in the conversation. + */ + includeMessageHistory?: boolean + + /** Whether to inherit the parent agent's system prompt instead of using this agent's own systemPrompt. + * + * Defaults to false. + * Use this when you want to enable prompt caching by preserving the same system prompt prefix. + * Cannot be used together with the systemPrompt field. + */ + inheritParentSystemPrompt?: boolean + + /** Background information for the agent. Fairly optional. Prefer using instructionsPrompt for agent instructions. */ + systemPrompt?: string + + /** Instructions for the agent. + * + * IMPORTANT: Updating this prompt is the best way to shape the agent's behavior. + * This prompt is inserted after each user input. */ + instructionsPrompt?: string + + /** Prompt inserted at each agent step. + * + * Powerful for changing the agent's behavior, but usually not necessary for smart models. + * Prefer instructionsPrompt for most instructions. */ + stepPrompt?: string + + // ============================================================================ + // Handle Steps + // ============================================================================ + + /** Programmatically step the agent forward and run tools. + * + * You can either yield: + * - A tool call object with toolName and input properties. + * - 'STEP' to run agent's model and generate one assistant message. + * - 'STEP_ALL' to run the agent's model until it uses the end_turn tool or stops includes no tool calls in a message. + * + * Or use 'return' to end the turn. + * + * Example 1: + * function* handleSteps({ agentState, prompt, params, logger }) { + * logger.info('Starting file read process') + * const { toolResult } = yield { + * toolName: 'read_files', + * input: { paths: ['file1.txt', 'file2.txt'] } + * } + * yield 'STEP_ALL' + * + * // Optionally do a post-processing step here... + * logger.info('Files read successfully, setting output') + * yield { + * toolName: 'set_output', + * input: { + * output: 'The files were read successfully.', + * }, + * } + * } + * + * Example 2: + * handleSteps: function* ({ agentState, prompt, params, logger }) { + * while (true) { + * logger.debug('Spawning thinker agent') + * yield { + * toolName: 'spawn_agents', + * input: { + * agents: [ + * { + * agent_type: 'thinker', + * prompt: 'Think deeply about the user request', + * }, + * ], + * }, + * } + * const { stepsComplete } = yield 'STEP' + * if (stepsComplete) break + * } + * } + */ + handleSteps?: (context: AgentStepContext) => Generator< + ToolCall | 'STEP' | 'STEP_ALL' | StepText | GenerateN, + void, + { + agentState: AgentState + toolResult: ToolResultOutput[] | undefined + stepsComplete: boolean + nResponses?: string[] + } + > +} + +// ============================================================================ +// Supporting Types +// ============================================================================ + +export interface AgentState { + agentId: string + runId: string + parentId: string | undefined + + /** The agent's conversation history: messages from the user and the assistant. */ + messageHistory: Message[] + + /** The last value set by the set_output tool. This is a plain object or undefined if not set. */ + output: Record | undefined + + /** The system prompt for this agent. */ + systemPrompt: string + + /** The tool definitions for this agent. */ + toolDefinitions: Record< + string, + { description: string | undefined; inputSchema: {} } + > + + /** + * The token count from the Anthropic API. + * This is updated on every agent step via the /api/v1/token-count endpoint. + */ + contextTokenCount: number +} + +/** + * Context provided to handleSteps generator function + */ +export interface AgentStepContext { + agentState: AgentState + prompt?: string + params?: Record + logger: Logger +} + +export type StepText = { type: 'STEP_TEXT'; text: string } +export type GenerateN = { type: 'GENERATE_N'; n: number } + +/** + * Tool call object for handleSteps generator + */ +export type ToolCall = { + [K in T]: { + toolName: K + input: GetToolParams + includeToolCall?: boolean + } +}[T] + +// ============================================================================ +// Available Tools +// ============================================================================ + +/** + * File operation tools + */ +export type FileEditingTools = 'read_files' | 'write_file' | 'str_replace' + +/** + * Code analysis tools + */ +export type CodeAnalysisTools = 'code_search' | 'find_files' | 'read_files' + +/** + * Terminal and system tools + */ +export type TerminalTools = 'run_terminal_command' | 'code_search' + +/** + * Web and browser tools + */ +export type WebTools = 'web_search' | 'read_docs' + +/** + * Agent management tools + */ +export type AgentTools = 'spawn_agents' + +/** + * Output and control tools + */ +export type OutputTools = 'set_output' + +// ============================================================================ +// Available Models (see: https://openrouter.ai/models) +// ============================================================================ + +/** + * AI models available for agents. Pick from our selection of recommended models or choose any model in OpenRouter. + * + * See available models at https://openrouter.ai/models + */ +export type ModelName = + // Recommended Models + + // OpenAI + | 'openai/gpt-5.1' + | 'openai/gpt-5.1-chat' + | 'openai/gpt-5-mini' + | 'openai/gpt-5-nano' + + // Anthropic + | 'anthropic/claude-sonnet-4.5' + | 'anthropic/claude-opus-4.1' + + // Gemini + | 'google/gemini-2.5-pro' + | 'google/gemini-2.5-flash' + | 'google/gemini-2.5-flash-lite' + | 'google/gemini-2.5-flash-preview-09-2025' + | 'google/gemini-2.5-flash-lite-preview-09-2025' + + // X-AI + | 'x-ai/grok-4-07-09' + | 'x-ai/grok-4-fast' + | 'x-ai/grok-code-fast-1' + + // Qwen + | 'qwen/qwen3-max' + | 'qwen/qwen3-coder-plus' + | 'qwen/qwen3-coder' + | 'qwen/qwen3-coder:nitro' + | 'qwen/qwen3-coder-flash' + | 'qwen/qwen3-235b-a22b-2507' + | 'qwen/qwen3-235b-a22b-2507:nitro' + | 'qwen/qwen3-235b-a22b-thinking-2507' + | 'qwen/qwen3-235b-a22b-thinking-2507:nitro' + | 'qwen/qwen3-30b-a3b' + | 'qwen/qwen3-30b-a3b:nitro' + + // DeepSeek + | 'deepseek/deepseek-chat-v3-0324' + | 'deepseek/deepseek-chat-v3-0324:nitro' + | 'deepseek/deepseek-r1-0528' + | 'deepseek/deepseek-r1-0528:nitro' + + // Other open source models + | 'moonshotai/kimi-k2' + | 'moonshotai/kimi-k2:nitro' + | 'z-ai/glm-4.6' + | 'z-ai/glm-4.6:nitro' + | (string & {}) + +import type { ToolName, GetToolParams } from './tools' +import type { + Message, + ToolResultOutput, + JsonObjectSchema, + MCPConfig, + Logger, +} from './util-types' + +export type { ToolName, GetToolParams } diff --git a/.agents/types/tools.ts b/.agents/types/tools.ts new file mode 100644 index 0000000..4d47cc8 --- /dev/null +++ b/.agents/types/tools.ts @@ -0,0 +1,316 @@ +/** + * Union type of all available tool names + */ +export type ToolName = + | 'add_message' + | 'ask_user' + | 'code_search' + | 'end_turn' + | 'find_files' + | 'glob' + | 'list_directory' + | 'lookup_agent_info' + | 'read_docs' + | 'read_files' + | 'read_subtree' + | 'run_file_change_hooks' + | 'run_terminal_command' + | 'set_messages' + | 'set_output' + | 'spawn_agents' + | 'str_replace' + | 'suggest_followups' + | 'task_completed' + | 'think_deeply' + | 'web_search' + | 'write_file' + | 'write_todos' + +/** + * Map of tool names to their parameter types + */ +export interface ToolParamsMap { + add_message: AddMessageParams + ask_user: AskUserParams + code_search: CodeSearchParams + end_turn: EndTurnParams + find_files: FindFilesParams + glob: GlobParams + list_directory: ListDirectoryParams + lookup_agent_info: LookupAgentInfoParams + read_docs: ReadDocsParams + read_files: ReadFilesParams + read_subtree: ReadSubtreeParams + run_file_change_hooks: RunFileChangeHooksParams + run_terminal_command: RunTerminalCommandParams + set_messages: SetMessagesParams + set_output: SetOutputParams + spawn_agents: SpawnAgentsParams + str_replace: StrReplaceParams + suggest_followups: SuggestFollowupsParams + task_completed: TaskCompletedParams + think_deeply: ThinkDeeplyParams + web_search: WebSearchParams + write_file: WriteFileParams + write_todos: WriteTodosParams +} + +/** + * Add a new message to the conversation history. To be used for complex requests that can't be solved in a single step, as you may forget what happened! + */ +export interface AddMessageParams { + role: 'user' | 'assistant' + content: string +} + +/** + * Ask the user multiple choice questions and pause execution until they respond. + */ +export interface AskUserParams { + /** List of multiple choice questions to ask the user */ + questions: { + /** The question to ask the user */ + question: string + /** Short label (max 12 chars) displayed as a chip/tag */ + header?: string + /** Array of answer options with label and optional description (minimum 2) */ + options: { + /** The display text for this option */ + label: string + /** Explanation shown when option is focused */ + description?: string + }[] + /** If true, allows selecting multiple options (checkbox). If false, single selection only (radio). */ + multiSelect?: boolean + /** Validation rules for "Other" text input */ + validation?: { + /** Maximum length for "Other" text input */ + maxLength?: number + /** Minimum length for "Other" text input */ + minLength?: number + /** Regex pattern for "Other" text input */ + pattern?: string + /** Custom error message when pattern fails */ + patternError?: string + } + }[] +} + +/** + * Search for string patterns in the project's files. This tool uses ripgrep (rg), a fast line-oriented search tool. Use this tool only when read_files is not sufficient to find the files you need. + */ +export interface CodeSearchParams { + /** The pattern to search for. */ + pattern: string + /** Optional ripgrep flags to customize the search (e.g., "-i" for case-insensitive, "-g *.ts -g *.js" for TypeScript and JavaScript files only, "-g !*.test.ts" to exclude Typescript test files, "-A 3" for 3 lines after match, "-B 2" for 2 lines before match). */ + flags?: string + /** Optional working directory to search within, relative to the project root. Defaults to searching the entire project. */ + cwd?: string + /** Maximum number of results to return per file. Defaults to 15. There is also a global limit of 250 results across all files. */ + maxResults?: number +} + +/** + * End your turn, regardless of any new tool results that might be coming. This will allow the user to type another prompt. + */ +export interface EndTurnParams {} + +/** + * Find several files related to a brief natural language description of the files or the name of a function or class you are looking for. + */ +export interface FindFilesParams { + /** A brief natural language description of the files or the name of a function or class you are looking for. It's also helpful to mention a directory or two to look within. */ + prompt: string +} + +/** + * Search for files matching a glob pattern. Returns matching file paths sorted by modification time. + */ +export interface GlobParams { + /** Glob pattern to match files against (e.g., *.js, src/glob/*.ts, glob/test/glob/*.go). */ + pattern: string + /** Optional working directory to search within, relative to project root. If not provided, searches from project root. */ + cwd?: string +} + +/** + * List files and directories in the specified path. Returns separate arrays of file names and directory names. + */ +export interface ListDirectoryParams { + /** Directory path to list, relative to the project root. */ + path: string +} + +/** + * Retrieve information about an agent by ID + */ +export interface LookupAgentInfoParams { + /** Agent ID (short local or full published format) */ + agentId: string +} + +/** + * Fetch up-to-date documentation for libraries and frameworks using Context7 API. + */ +export interface ReadDocsParams { + /** The library or framework name (e.g., "Next.js", "MongoDB", "React"). Use the official name as it appears in documentation if possible. Only public libraries available in Context7's database are supported, so small or private libraries may not be available. */ + libraryTitle: string + /** Specific topic to focus on (e.g., "routing", "hooks", "authentication") */ + topic: string + /** Optional maximum number of tokens to return. Defaults to 20000. Values less than 10000 are automatically increased to 10000. */ + max_tokens?: number +} + +/** + * Read the multiple files from disk and return their contents. Use this tool to read as many files as would be helpful to answer the user's request. + */ +export interface ReadFilesParams { + /** List of file paths to read. */ + paths: string[] +} + +/** + * Read one or more directory subtrees (as a blob including subdirectories, file names, and parsed variables within each source file) or return parsed variable names for files. If no paths are provided, returns the entire project tree. + */ +export interface ReadSubtreeParams { + /** List of paths to directories or files. Relative to the project root. If omitted, the entire project tree is used. */ + paths?: string[] + /** Maximum token budget for the subtree blob; the tree will be truncated to fit within this budget by first dropping file variables and then removing the most-nested files and directories. */ + maxTokens?: number +} + +/** + * Parameters for run_file_change_hooks tool + */ +export interface RunFileChangeHooksParams { + /** List of file paths that were changed and should trigger file change hooks */ + files: string[] +} + +/** + * Execute a CLI command from the **project root** (different from the user's cwd). + */ +export interface RunTerminalCommandParams { + /** CLI command valid for user's OS. */ + command: string + /** Either SYNC (waits, returns output) or BACKGROUND (runs in background). Default SYNC */ + process_type?: 'SYNC' | 'BACKGROUND' + /** The working directory to run the command in. Default is the project root. */ + cwd?: string + /** Set to -1 for no timeout. Does not apply for BACKGROUND commands. Default 30 */ + timeout_seconds?: number +} + +/** + * Set the conversation history to the provided messages. + */ +export interface SetMessagesParams { + messages: any +} + +/** + * JSON object to set as the agent output. This completely replaces any previous output. If the agent was spawned, this value will be passed back to its parent. If the agent has an outputSchema defined, the output will be validated against it. + */ +export interface SetOutputParams {} + +/** + * Spawn multiple agents and send a prompt and/or parameters to each of them. These agents will run in parallel. Note that that means they will run independently. If you need to run agents sequentially, use spawn_agents with one agent at a time instead. + */ +export interface SpawnAgentsParams { + agents: { + /** Agent to spawn */ + agent_type: string + /** Prompt to send to the agent */ + prompt?: string + /** Parameters object for the agent (if any) */ + params?: Record + }[] +} + +/** + * Replace strings in a file with new strings. + */ +export interface StrReplaceParams { + /** The path to the file to edit. */ + path: string + /** Array of replacements to make. */ + replacements: { + /** The string to replace. This must be an *exact match* of the string you want to replace, including whitespace and punctuation. */ + old: string + /** The string to replace the corresponding old string with. Can be empty to delete. */ + new: string + /** Whether to allow multiple replacements of old string. */ + allowMultiple?: boolean + }[] +} + +/** + * Suggest clickable followup prompts to the user. + */ +export interface SuggestFollowupsParams { + /** List of suggested followup prompts the user can click to send */ + followups: { + /** The full prompt text to send as a user message when clicked */ + prompt: string + /** Short display label for the card (defaults to truncated prompt if not provided) */ + label?: string + }[] +} + +/** + * Signal that the task is complete. Use this tool when: +- The user's request is completely fulfilled +- You need clarification from the user before continuing +- You are stuck or need help from the user to continue + +This tool explicitly marks the end of your work on the current task. + */ +export interface TaskCompletedParams {} + +/** + * Deeply consider complex tasks by brainstorming approaches and tradeoffs step-by-step. + */ +export interface ThinkDeeplyParams { + /** Detailed step-by-step analysis. Initially keep each step concise (max ~5-7 words per step). */ + thought: string +} + +/** + * Search the web for current information using Linkup API. + */ +export interface WebSearchParams { + /** The search query to find relevant web content */ + query: string + /** Search depth - 'standard' for quick results, 'deep' for more comprehensive search. Default is 'standard'. */ + depth?: 'standard' | 'deep' +} + +/** + * Create or edit a file with the given content. + */ +export interface WriteFileParams { + /** Path to the file relative to the **project root** */ + path: string + /** What the change is intended to do in only one sentence. */ + instructions: string + /** Edit snippet to apply to the file. */ + content: string +} + +/** + * Write a todo list to track tasks for multi-step implementations. Use this frequently to maintain an updated step-by-step plan. + */ +export interface WriteTodosParams { + /** List of todos with their completion status. Add ALL of the applicable tasks to the list, so you don't forget to do anything. Try to order the todos the same way you will complete them. Do not mark todos as completed if you have not completed them yet! */ + todos: { + /** Description of the task */ + task: string + /** Whether the task is completed */ + completed: boolean + }[] +} + +/** + * Get parameters type for a specific tool + */ +export type GetToolParams = ToolParamsMap[T] diff --git a/.agents/types/util-types.ts b/.agents/types/util-types.ts new file mode 100644 index 0000000..086eff4 --- /dev/null +++ b/.agents/types/util-types.ts @@ -0,0 +1,175 @@ +// ===== JSON Types ===== +export type JSONValue = + | null + | string + | number + | boolean + | JSONObject + | JSONArray + +export type JSONObject = { [key: string]: JSONValue } + +export type JSONArray = JSONValue[] + +/** + * JSON Schema definition (for prompt schema or output schema) + */ +export type JsonSchema = { + type?: + | 'object' + | 'array' + | 'string' + | 'number' + | 'boolean' + | 'null' + | 'integer' + description?: string + properties?: Record + required?: string[] + enum?: Array + [k: string]: unknown +} +export type JsonObjectSchema = JsonSchema & { type: 'object' } + +// ===== Data Content Types ===== +export type DataContent = string | Uint8Array | ArrayBuffer | Buffer + +// ===== Provider Metadata Types ===== +export type ProviderMetadata = Record> + +// ===== Content Part Types ===== +export type TextPart = { + type: 'text' + text: string + providerOptions?: ProviderMetadata +} + +export type ImagePart = { + type: 'image' + image: DataContent + mediaType?: string + providerOptions?: ProviderMetadata +} + +export type FilePart = { + type: 'file' + data: DataContent + filename?: string + mediaType: string + providerOptions?: ProviderMetadata +} + +export type ReasoningPart = { + type: 'reasoning' + text: string + providerOptions?: ProviderMetadata +} + +export type ToolCallPart = { + type: 'tool-call' + toolCallId: string + toolName: string + input: Record + providerOptions?: ProviderMetadata + providerExecuted?: boolean +} + +export type ToolResultOutput = + | { + type: 'json' + value: JSONValue + } + | { + type: 'media' + data: string + mediaType: string + } + +// ===== Message Types ===== +export type AuxiliaryMessageData = { + providerOptions?: ProviderMetadata + tags?: string[] + + /** @deprecated Use tags instead. */ + timeToLive?: 'agentStep' | 'userPrompt' + /** @deprecated Use tags instead. */ + keepDuringTruncation?: boolean + /** @deprecated Use tags instead. */ + keepLastTags?: string[] +} + +export type SystemMessage = { + role: 'system' + content: TextPart[] +} & AuxiliaryMessageData + +export type UserMessage = { + role: 'user' + content: (TextPart | ImagePart | FilePart)[] +} & AuxiliaryMessageData + +export type AssistantMessage = { + role: 'assistant' + content: (TextPart | ReasoningPart | ToolCallPart)[] +} & AuxiliaryMessageData + +export type ToolMessage = { + role: 'tool' + toolCallId: string + toolName: string + content: ToolResultOutput[] +} & AuxiliaryMessageData + +export type Message = + | SystemMessage + | UserMessage + | AssistantMessage + | ToolMessage + +// ===== MCP Server Types ===== + +/** + * MCP server configuration for stdio-based servers. + * + * Environment variables in `env` can be: + * - A plain string value (hardcoded, e.g., `'production'`) + * - A `$VAR_NAME` reference to read from local environment (e.g., `'$NOTION_TOKEN'`) + * + * The `$VAR_NAME` syntax reads from `process.env.VAR_NAME` at agent load time. + * This keeps secrets out of your agent definitions - store them in `.env.local` instead. + * + * @example + * ```typescript + * env: { + * // Read NOTION_TOKEN from local .env file + * NOTION_TOKEN: '$NOTION_TOKEN', + * // Read MY_API_KEY from local env, pass as API_KEY to MCP server + * API_KEY: '$MY_API_KEY', + * // Hardcoded value (non-secret) + * NODE_ENV: 'production', + * } + * ``` + */ +export type MCPConfig = + | { + type?: 'stdio' + command: string + args?: string[] + env?: Record + } + | { + type?: 'http' | 'sse' + url: string + params?: Record + headers?: Record + } + +// ============================================================================ +// Logger Interface +// ============================================================================ +export interface Logger { + debug: (data: any, msg?: string) => void + info: (data: any, msg?: string) => void + warn: (data: any, msg?: string) => void + error: (data: any, msg?: string) => void +} diff --git a/CLAUDE.md b/CLAUDE.md index 91c7bc3..8c9d539 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,12 @@ websitedev/ ├── proto/ # Main Next.js application │ ├── src/ │ │ ├── app/ # Next.js App Router (pages and API routes) -│ │ ├── components/ # React components (Header, Footer) +│ │ ├── components/ # React components (Header, Footer, ThemeProvider) +│ │ ├── content/ # JSON content management system +│ │ │ ├── types.ts # Content TypeScript definitions +│ │ │ ├── index.ts # Content loader utility +│ │ │ ├── common.json # Shared texts (buttons, labels) +│ │ │ └── pages/ # Page-specific content JSONs │ │ ├── lib/ # Utility libraries (MongoDB, Logger, Site Config) │ │ ├── config/ # Static site configuration │ │ └── types/ # TypeScript type definitions @@ -28,6 +33,8 @@ websitedev/ - **Framework**: Next.js 14 with App Router and TypeScript - **UI**: Tailwind CSS 4 with custom fonts (Geist Sans/Mono) +- **Design System**: CSS Variables, Dark Mode (ThemeProvider), Animations +- **Content Management**: JSON-based structured content in `src/content/` - **Database**: MongoDB with Mongoose ODM - **Logging**: Winston with Loki integration - **Testing**: Jest with React Testing Library @@ -80,11 +87,43 @@ Required environment variables (check `.env` and `proto/.env.local`): ## Site Configuration The site uses a centralized configuration system: -- `src/config/site.ts` - Main site configuration with all content +- `src/config/site.ts` - Main site configuration (company info, navigation, services) - `src/types/site.ts` - TypeScript interfaces for configuration - `src/lib/site-config.ts` - Runtime configuration utilities -This approach enables easy content updates and future CMS integration. +## Content Management System + +All page text content is managed through JSON files for easy modification: + +``` +src/content/ +├── types.ts # TypeScript definitions for content +├── index.ts # Content loader with getPageContent() helper +├── common.json # Shared texts (buttons, labels, validation) +└── pages/ + ├── home.json # Homepage CTA, feature labels + ├── about.json # About page (hero, story, mission, team) + ├── services.json # Services details, support info + └── contact.json # Form labels, FAQ, contact info +``` + +**Usage in components:** +```typescript +import { content, getPageContent } from '@/content' + +const { about: pageContent } = content.pages +// or +const servicesContent = getPageContent('services') +``` + +## Design System + +The project includes a comprehensive design system: + +- **CSS Variables**: Defined in `globals.css` for colors, shadows, transitions +- **Dark Mode**: ThemeProvider component with system preference + manual toggle +- **Animations**: fadeIn, float, pulse, hover effects (lift, scale, glow) +- **Utility Classes**: `.card`, `.btn`, `.icon-container`, etc. ## Database Integration diff --git a/README.md b/README.md index 5b06aae..dadabf8 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ This project uses **separated sync systems** for optimal management: ## 🛠️ Tech Stack -- **Frontend**: Next.js 14, TypeScript, Tailwind CSS +- **Frontend**: Next.js 14, TypeScript, Tailwind CSS 4 +- **Design System**: CSS Variables, Dark Mode, Custom Animations +- **Content Management**: JSON-based structured content - **Backend**: Next.js API Routes, MongoDB - **Testing**: Jest, React Testing Library, Docker - **Deployment**: Dokploy, Docker @@ -59,6 +61,9 @@ This project uses **separated sync systems** for optimal management: ### ✅ Completed Features - [x] Responsive website with modern design +- [x] **Design System** with CSS variables, animations, and micro-interactions +- [x] **Dark Mode** with system preference detection and manual toggle +- [x] **JSON Content Management** - Structured, easily modifiable content system - [x] Contact form with validation & spam protection - [x] Rate limiting and security features - [x] Docker development environment @@ -90,3 +95,37 @@ This project uses **separated sync systems** for optimal management: - [Traceability Matrix](./TRACEABILITY-MATRIX.md) - [Docker Setup](./DOCKER.md) - [Development Guide](./proto/README.md) + +## 🎨 Design System + +The project uses a comprehensive design system with: + +- **CSS Variables**: Brand colors, spacing, shadows, transitions +- **Dark Mode**: Automatic system preference + manual toggle via ThemeProvider +- **Animations**: fadeIn, float, pulse, hover effects (lift, scale, glow) +- **Components**: Buttons, cards, icons with consistent styling + +## 📝 Content Management + +All website text content is managed through JSON files: + +``` +proto/src/content/ +├── types.ts # TypeScript definitions +├── index.ts # Content loader utility +├── common.json # Shared texts (buttons, labels) +└── pages/ + ├── home.json # Homepage content + ├── about.json # About page content + ├── services.json # Services page content + └── contact.json # Contact page content +``` + +**Usage:** +```typescript +import { content, getPageContent } from '@/content' + +// Access content +const aboutContent = content.pages.about +const servicesContent = getPageContent('services') +``` diff --git a/TODO.md b/TODO.md index 52566d6..dadfd51 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,8 @@ Next.js 14 alapú weboldal a mozdIT Bt. számára, Dokploy-on hostolva. | ZEE-33 | Kapcsolat űrlap + API stub | ✅ | | ZEE-34 | /api/health endpoint | ✅ | | ZEE-35 | Dockerfile + Docker Compose fejlesztői környezet | ✅ | +| ZEE-29 | Tailwind + alap layout (design system, dark mode, animációk) | ✅ | +| - | JSON alapú content management rendszer (struktúrált szövegkezelés) | ✅ | | - | Unit teszt infrastruktúra beállítása (Jest + React Testing Library) | ✅ | | - | API endpoint tesztek írása (/api/health) | ✅ | | - | Component tesztek írása (Header, Footer) | ✅ | @@ -29,12 +31,12 @@ Next.js 14 alapú weboldal a mozdIT Bt. számára, Dokploy-on hostolva. ## 🔄 Folyamatban (Linear szerint) | Linear Ticket | Feladat | Státusz | |---------------|---------|---------| -| - | Jelenleg nincs aktív folyamatban lévő feladat | - | +| - | ZEE-152 | - | ## 📋 Tervezett (Backlog) | Linear Ticket | Feladat | Státusz | |---------------|---------|---------| -| ZEE-29 | Tailwind + alap layout | 📋 | + | ZEE-36 | CI (lint, unit) + Staging deploy trigger | 📋 | | ZEE-37 | Playwright smoke E2E + Lighthouse CI | 📋 | | ZEE-38 | Prod app + domain + HTTPS | 📋 | @@ -166,4 +168,4 @@ Minden feladat megtalálható a Linear-ben megfelelő ticket számmal. A TODO.md **Backlog ticketek:** ZEE-29, ZEE-36-46 **Duplikált ticketek eltávolítva:** ZEE-27 (duplikáció a ZEE-28-hoz képest) -**Utolsó szinkronizáció:** 2025-09-05 - Docker fejlesztői környezet implementáció befejezése után \ No newline at end of file +**Utolsó szinkronizáció:** 2025-01-23 - ZEE-29 (Tailwind + layout) és JSON content management befejezése \ No newline at end of file diff --git a/TODO.md.backup.1769217893498 b/TODO.md.backup.1769217893498 new file mode 100644 index 0000000..6be5ca9 --- /dev/null +++ b/TODO.md.backup.1769217893498 @@ -0,0 +1,171 @@ +# mozdIT Weboldal - Fejlesztési TODO Lista + +## Projekt Áttekintés +Next.js 14 alapú weboldal a mozdIT Bt. számára, Dokploy-on hostolva. + +## ⚠️ Projekt Management +**Elsődleges forrás**: Linear (ZEE-28, ZEE-29, ZEE-30, stb.) +**Lokális másolat**: Ez a fájl csak referencia, a Linear az authoritative source + +## ✅ Befejezett (Linear szerint) +| Linear Ticket | Feladat | Státusz | +|---------------|---------|---------| +| ZEE-28 | Repo & Next.js bootstrap - befejezett, dev szerver fut localhost:3001-n | ✅ | +| ZEE-30 | Kezdőlap (Hero + USP + Webmail CTA) | ✅ | +| ZEE-31 | Rólunk oldal | ✅ | +| ZEE-32 | Szolgáltatások oldal | ✅ | +| ZEE-33 | Kapcsolat űrlap + API stub | ✅ | +| ZEE-34 | /api/health endpoint | ✅ | +| ZEE-35 | Dockerfile + Docker Compose fejlesztői környezet | ✅ | +| ZEE-29 | Tailwind + alap layout (design system, dark mode, animációk) | ✅ | +| - | JSON alapú content management rendszer (struktúrált szövegkezelés) | ✅ | +| - | Unit teszt infrastruktúra beállítása (Jest + React Testing Library) | ✅ | +| - | API endpoint tesztek írása (/api/health) | ✅ | +| - | Component tesztek írása (Header, Footer) | ✅ | +| - | Teszt hibák javítása (Jest setup, duplicate elements, type casting) | ✅ | +| - | Mobil navigáció JavaScript funkcionalitás | ✅ | +| - | Contact API endpoint (/api/contact) - rate limiting, spam detection, validáció | ✅ | +| - | Docker fejlesztői környezet (Next.js + MongoDB + Mongo Express + Loki + Grafana) | ✅ | +| - | MongoDB inicializálás és site_config beállítás | ✅ | + +## 🔄 Folyamatban (Linear szerint) +| Linear Ticket | Feladat | Státusz | +|---------------|---------|---------| +| - | Jelenleg nincs aktív folyamatban lévő feladat | - | + +## 📋 Tervezett (Backlog) +| Linear Ticket | Feladat | Státusz | +|---------------|---------|---------| + +| ZEE-36 | CI (lint, unit) + Staging deploy trigger | 📋 | +| ZEE-37 | Playwright smoke E2E + Lighthouse CI | 📋 | +| ZEE-38 | Prod app + domain + HTTPS | 📋 | +| ZEE-39 | Site config migrálás MongoDB-ba | 📋 | +| ZEE-40 | Winston logger + Loki integráció | 📋 | + +## 📋 Követelmény Nyilvántartás (REQ Prefix) +| Linear Ticket | Követelmény | Kategória | Státusz | +|---------------|-------------|-----------|---------| +| ZEE-50 | REQ-001: Kezdőlap Funkcionalitás | Funkcionális | ✅ | +| ZEE-51 | REQ-004: Kapcsolat Űrlap | Funkcionális | ✅ | +| ZEE-52 | REQ-101: Responsive Design | Nem-funkcionális | ✅ | +| ZEE-53 | REQ-102: Accessibility (A11y) | Nem-funkcionális | 🔄 | +| ZEE-54 | REQ-401: Lighthouse Score ≥ 90 | Teljesítmény | 🔄 | +| ZEE-41 | Logging middleware megvalósítása | 📋 | +| ZEE-42 | Grafana dashboard konfiguráció | 📋 | +| ZEE-43 | SEO optimalizálás | 📋 | +| ZEE-44 | Performance optimalizálás | 📋 | +| ZEE-45 | Reszponzív design finomítása | 📋 | + +## 🥒 Test Reporting Rendszer (Gherkin Format) +| Funkció | Státusz | Leírás | +|---------|---------|---------| +| Gherkin generálás | ✅ | Automatikus TC-XXX prefix felismerés | +| Funkcionális területek elemzése | ✅ | 8 kategória szerinti csoportosítás | +| Coverage dashboard | ✅ | Területenkénti lemaradás elemzés | +| TC issue frissítés | ✅ | Linear issues Gherkin formátummal | +| Automatizált reporting | ✅ | GitHub Actions integráció | + +### 🎯 Funkcionális Területek +- **🏠 Weboldal Funkcionalitás**: Kezdőlap, navigáció, oldalak +- **📝 Kapcsolat Űrlap**: Validáció, spam védelem, rate limiting +- **🎨 Design és UX**: Responsive, accessibility, performance +- **🔒 Biztonság**: Input validáció, rate limiting, HTTPS +- **⚡ Teljesítmény**: Lighthouse, optimalizálás, caching +- **🛠️ Technikai Stack**: Next.js, MongoDB, Docker, CI/CD + +### 📊 Jelenlegi Test Coverage +- **Összes teszt**: 56 +- **TC-XXX prefix**: 1 (TC-001) +- **Funkcionális területek**: 1 (Kapcsolat Űrlap) +- **Sikeres tesztek**: 100% +- **Lemaradás**: Nincs +| ZEE-46 | Analytics integráció | 📋 | + +## Technikai Stack +- **Frontend**: Next.js 14, TypeScript, Tailwind CSS +- **Backend**: Next.js API Routes +- **Database**: MongoDB +- **Logging**: Winston + Loki +- **Monitoring**: Grafana +- **Deployment**: Dokploy +- **Testing**: Jest, React Testing Library +- **Project Management**: Linear (elsődleges) + +## Fejlesztési Parancsok + +### Hagyományos fejlesztés +```bash +# Fejlesztői szerver indítása +cd proto && npm run dev + +# Tesztek futtatása +cd proto && npm test + +# Build készítése +cd proto && npm run build +``` + +### Docker fejlesztői környezet +```bash +# Docker stack indítása (teljes környezet) +docker-compose -f docker-compose.dev.yml up -d + +# Docker stack leállítása +docker-compose -f docker-compose.dev.yml down + +# Logok követése +docker-compose -f docker-compose.dev.yml logs -f + +# Alkalmazás újraépítése +docker-compose -f docker-compose.dev.yml up --build -d + +# Teljes tisztítás (adatok törlése) +docker-compose -f docker-compose.dev.yml down -v +``` + +### Elérhető szolgáltatások (Docker) +- **Weboldal**: http://localhost:3000 +- **MongoDB UI**: http://localhost:8081 (admin/password123) +- **Grafana**: http://localhost:3001 (admin/admin123) +- **Loki**: http://localhost:3100 +- **MongoDB**: mongodb://admin:password123@localhost:27017/admin + +## Fontos Megjegyzés +A Linear az authoritative project management rendszer. Ez a fájl csak lokális referencia, mindig ellenőrizd a Linear-t az aktuális státuszért és priorításokért. + +## Frissítési Napló +- **2025-09-05**: TODO.md fájl létrehozása, Linear integráció megjegyzésekkel +- **2025-09-05**: Szinkronizáció Linear Website Development projekttel - aktuális státuszok frissítve +- **2025-09-05**: Nagyobb implementációs mérföldkő - ZEE-31, ZEE-32, ZEE-33 befejezve, mobil navigáció és contact API implementálva +- **2025-09-05**: Docker fejlesztői környezet implementálva - ZEE-34, ZEE-35 befejezve, teljes stack (Next.js + MongoDB + Monitoring) működik +## Szinkronizálás Utmutató + +A TODO.md és Linear között párhuzamos vezetéshez használd a `linear-sync.js` scriptet: + +```bash +# API kulcs hozzáadása a .env fájlhoz (Linear settings -> API) +# Szerkesd a .env fájlt és add hozzá: +# LINEAR_API_KEY=lin_api_your_actual_key_here + +# Szinkronizálás futtatása +node linear-sync.js + +# vagy +./linear-sync.js +``` + +A script: +- Megkeresi a TODO.md-ben hiányzó Linear ticket-eket +- Létrehozza ezeket a Linear-ben +- Frissíti a Linear ticket státuszokat a TODO alapján +- Visszairja a Linear ticket számokat a TODO.md-be + +### ✅ Szinkronizáció állapota: +Minden feladat megtalálható a Linear-ben megfelelő ticket számmal. A TODO.md mostantól teljesen szinkronban van a Linear Website Development projekttel. + +**Befejezett ticketek:** ZEE-28, ZEE-30, ZEE-31, ZEE-32, ZEE-33, ZEE-34, ZEE-35 +**Backlog ticketek:** ZEE-29, ZEE-36-46 +**Duplikált ticketek eltávolítva:** ZEE-27 (duplikáció a ZEE-28-hoz képest) + +**Utolsó szinkronizáció:** 2025-01-23 - ZEE-29 (Tailwind + layout) és JSON content management befejezése \ No newline at end of file diff --git a/knowledge.md b/knowledge.md new file mode 100644 index 0000000..5bf4f3c --- /dev/null +++ b/knowledge.md @@ -0,0 +1,74 @@ +# Project Knowledge + +mozdIT Bt. website - Next.js 14 app for a Hungarian IT services company (web hosting, email, DNS). + +## Quickstart + +```bash +# Setup +cd proto && npm install + +# Dev server (with Turbopack) +cd proto && npm run dev + +# Docker dev environment (includes MongoDB, Grafana, Loki) +docker-compose -f docker-compose.dev.yml up -d + +# Tests +cd proto && npm test # Unit tests +cd proto && npm run test:all # All tests (unit + browser + docker) +``` + +## Architecture + +- **proto/** - Main Next.js 14 application + - `src/app/` - App Router pages and API routes + - `src/components/` - React components (Header, Footer, ThemeProvider) + - `src/content/` - **JSON content management system** + - `types.ts` - Content TypeScript definitions + - `index.ts` - Content loader utility + - `common.json` - Shared texts (buttons, labels) + - `pages/*.json` - Page-specific content + - `src/lib/` - Utilities (MongoDB, Logger, Site Config) + - `src/config/` - Static site configuration + - `src/types/` - TypeScript definitions +- **docker/** - MongoDB initialization scripts +- **docs/** - Project documentation (Hungarian) +- **scripts/** - Test sync and reporting scripts + +Path alias: `@/*` → `./src/*` + +## Commands (run from proto/) + +| Command | Description | +|---------|-------------| +| `npm run dev` | Dev server with Turbopack | +| `npm run build` | Production build | +| `npm run lint` | ESLint check | +| `npm test` | Unit tests | +| `npm run test:browser` | Browser integration tests | +| `npm run test:integration` | Docker integration tests | +| `npm run test:e2e` | End-to-end tests | +| `npm run test:all` | All test suites | +| `npm run docker:dev` | Start Docker stack | + +## Conventions + +- **Language**: Hungarian content, English code/comments +- **Stack**: Next.js 14 App Router, TypeScript, Tailwind CSS 4 +- **Design System**: CSS variables, dark mode, animations in `globals.css` +- **Content**: JSON files in `src/content/` for all page texts +- **Database**: MongoDB with Mongoose +- **Logging**: Winston with Loki integration +- **Testing**: Jest + React Testing Library +- **ESLint**: next/core-web-vitals + next/typescript + +## Gotchas + +- **All commands run from `proto/`** - The main app lives in the proto subdirectory +- **Docker required for integration tests** - Start with `docker-compose -f docker-compose.dev.yml up -d` +- **Dual tracking**: Linear (ZEE-* tickets) + TODO.md for task management +- **Environment variables**: Need `MONGODB_URI`, `MONGODB_DB` for database connection +- **Path mapping**: Use `@/` imports (e.g., `@/lib/mongodb`, `@/content`) +- **Content changes**: Edit JSON files in `src/content/pages/` - rebuild container to see changes +- **Dark mode**: Uses `data-theme` attribute on ``, managed by ThemeProvider diff --git a/proto/README.md b/proto/README.md index e215bc4..4d1cc19 100644 --- a/proto/README.md +++ b/proto/README.md @@ -1,36 +1,131 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# mozdIT Bt. Website - Next.js Application -## Getting Started +Modern Next.js 14 website for mozdIT Bt. - Hungarian IT services company. -First, run the development server: +## 🚀 Quick Start ```bash +# Install dependencies +npm install + +# Development server (with Turbopack) npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev + +# Production build +npm run build + +# Run tests +npm test ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000) to view the site. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +## 📁 Project Structure -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +``` +src/ +├── app/ # Next.js App Router +│ ├── page.tsx # Homepage +│ ├── rolunk/ # About page +│ ├── szolgaltatasok/ # Services page +│ ├── kapcsolat/ # Contact page +│ ├── api/ # API routes +│ └── globals.css # Design system & styles +├── components/ # React components +│ ├── Header.tsx # Navigation header +│ ├── Footer.tsx # Site footer +│ └── ThemeProvider.tsx # Dark mode provider +├── content/ # JSON content management +│ ├── types.ts # Content type definitions +│ ├── index.ts # Content loader +│ ├── common.json # Shared texts +│ └── pages/ # Page-specific content +├── config/ # Site configuration +├── lib/ # Utilities +└── types/ # TypeScript definitions +``` -## Learn More +## 🎨 Design System -To learn more about Next.js, take a look at the following resources: +The project uses a comprehensive design system defined in `globals.css`: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### CSS Variables +- Brand colors (`--color-primary-*`) +- Semantic colors (`--color-background`, `--color-foreground`) +- Shadows, transitions, border radius -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +### Dark Mode +- Automatic system preference detection +- Manual toggle via ThemeProvider +- Uses `data-theme="dark"` attribute -## Deploy on Vercel +### Animations +- `animate-fade-in-up` - Fade in with upward motion +- `animate-float` - Floating effect +- `animate-pulse-slow` - Slow pulsing +- `hover-lift`, `hover-scale`, `hover-glow` - Hover effects -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### Utility Classes +- `.card` - Card component styling +- `.btn`, `.btn-primary`, `.btn-secondary` - Button styles +- `.icon-container` - Icon wrapper styling -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## 📝 Content Management + +All page content is managed through JSON files in `src/content/`: + +```typescript +import { content, getPageContent } from '@/content' + +// Access specific page content +const aboutContent = content.pages.about + +// Or use the helper function +const servicesContent = getPageContent('services') +``` + +### Content Files + +| File | Description | +|------|-------------| +| `common.json` | Shared texts (buttons, labels, validation) | +| `pages/home.json` | Homepage CTA section | +| `pages/about.json` | About page (hero, story, mission, team, CTA) | +| `pages/services.json` | Services (hero, details, support, CTA) | +| `pages/contact.json` | Contact (form labels, FAQ, info) | + +## 🧪 Testing + +```bash +# Unit tests +npm test + +# Watch mode +npm run test:watch + +# Coverage report +npm run test:coverage + +# All test suites +npm run test:all +``` + +## 🐳 Docker Development + +For full-stack development with MongoDB and monitoring: + +```bash +# From project root +docker-compose -f docker-compose.dev.yml up -d +``` + +Services: +- **Website**: http://localhost:3000 +- **MongoDB UI**: http://localhost:8081 +- **Grafana**: http://localhost:3001 + +## 📚 Learn More + +- [Next.js Documentation](https://nextjs.org/docs) +- [Tailwind CSS](https://tailwindcss.com/docs) +- [Project Documentation](../README.md) diff --git a/proto/src/app/globals.css b/proto/src/app/globals.css index a2dc41e..a9db91b 100644 --- a/proto/src/app/globals.css +++ b/proto/src/app/globals.css @@ -1,26 +1,556 @@ @import "tailwindcss"; +/* ======================================== + mozdIT Bt. Design System + ======================================== */ + +/* Design Tokens - Light Theme */ :root { - --background: #ffffff; - --foreground: #171717; + /* Brand Colors */ + --color-primary-50: #eff6ff; + --color-primary-100: #dbeafe; + --color-primary-200: #bfdbfe; + --color-primary-300: #93c5fd; + --color-primary-400: #60a5fa; + --color-primary-500: #3b82f6; + --color-primary-600: #2563eb; + --color-primary-700: #1d4ed8; + --color-primary-800: #1e40af; + --color-primary-900: #1e3a8a; + --color-primary-950: #172554; + + /* Accent Colors */ + --color-accent-50: #eef2ff; + --color-accent-100: #e0e7ff; + --color-accent-200: #c7d2fe; + --color-accent-500: #6366f1; + --color-accent-600: #4f46e5; + + /* Success Colors */ + --color-success-50: #f0fdf4; + --color-success-500: #22c55e; + --color-success-600: #16a34a; + + /* Warning Colors */ + --color-warning-50: #fffbeb; + --color-warning-500: #f59e0b; + + /* Error Colors */ + --color-error-50: #fef2f2; + --color-error-500: #ef4444; + --color-error-600: #dc2626; + + /* Neutral Colors - Light Mode */ + --color-background: #ffffff; + --color-background-secondary: #f9fafb; + --color-background-tertiary: #f3f4f6; + --color-foreground: #111827; + --color-foreground-secondary: #374151; + --color-foreground-muted: #6b7280; + --color-border: #e5e7eb; + --color-border-light: #f3f4f6; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + + /* Transitions */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-bounce: 500ms cubic-bezier(0.34, 1.56, 0.64, 1); + + /* Spacing */ + --container-padding: 1rem; + --section-spacing: 4rem; + + /* Border Radius */ + --radius-sm: 0.375rem; + --radius-md: 0.5rem; + --radius-lg: 0.75rem; + --radius-xl: 1rem; + --radius-full: 9999px; + + /* Fonts */ + --font-sans: var(--font-geist-sans), system-ui, -apple-system, sans-serif; + --font-mono: var(--font-geist-mono), 'Fira Code', monospace; } -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); +/* Dark Mode */ +[data-theme="dark"] { + --color-background: #0f172a; + --color-background-secondary: #1e293b; + --color-background-tertiary: #334155; + --color-foreground: #f8fafc; + --color-foreground-secondary: #e2e8f0; + --color-foreground-muted: #94a3b8; + --color-border: #334155; + --color-border-light: #1e293b; + + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.4); } +/* System preference fallback */ @media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; + :root:not([data-theme="light"]) { + --color-background: #0f172a; + --color-background-secondary: #1e293b; + --color-background-tertiary: #334155; + --color-foreground: #f8fafc; + --color-foreground-secondary: #e2e8f0; + --color-foreground-muted: #94a3b8; + --color-border: #334155; + --color-border-light: #1e293b; + + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.4); } } +/* Tailwind Theme Extension */ +@theme inline { + --color-background: var(--color-background); + --color-foreground: var(--color-foreground); + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); +} + +/* Base Styles */ +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--color-background); + color: var(--color-foreground); + font-family: var(--font-sans); + transition: background-color var(--transition-normal), color var(--transition-normal); +} + +/* Selection */ +::selection { + background: var(--color-primary-500); + color: white; +} + +/* Focus Styles */ +:focus-visible { + outline: 2px solid var(--color-primary-500); + outline-offset: 2px; +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track { + background: var(--color-background-secondary); +} + +::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-foreground-muted); +} + +/* ======================================== + Utility Classes + ======================================== */ + +/* Gradient Backgrounds */ +.bg-gradient-hero { + background: linear-gradient( + 135deg, + var(--color-primary-50) 0%, + var(--color-accent-50) 50%, + var(--color-primary-100) 100% + ); +} + +[data-theme="dark"] .bg-gradient-hero { + background: linear-gradient( + 135deg, + var(--color-background-secondary) 0%, + var(--color-background-tertiary) 50%, + var(--color-background-secondary) 100% + ); +} + +/* Glass Effect */ +.glass { + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); +} + +[data-theme="dark"] .glass { + background: rgba(15, 23, 42, 0.8); +} + +/* ======================================== + Animation Keyframes + ======================================== */ + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideInLeft { + from { + opacity: 0; + transform: translateX(-30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes bounce { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-5px); + } +} + +/* Animation Classes */ +.animate-fade-in { + animation: fadeIn var(--transition-slow) ease-out forwards; +} + +.animate-fade-in-up { + animation: fadeInUp 0.5s ease-out forwards; +} + +.animate-fade-in-down { + animation: fadeInDown 0.5s ease-out forwards; +} + +.animate-slide-in-left { + animation: slideInLeft 0.5s ease-out forwards; +} + +.animate-slide-in-right { + animation: slideInRight 0.5s ease-out forwards; +} + +.animate-scale-in { + animation: scaleIn 0.3s ease-out forwards; +} + +.animate-bounce-slow { + animation: bounce 2s ease-in-out infinite; +} + +.animate-pulse-slow { + animation: pulse 2s ease-in-out infinite; +} + +.animate-float { + animation: float 3s ease-in-out infinite; +} + +.animate-spin-slow { + animation: spin 3s linear infinite; +} + +/* Staggered animations for children */ +.stagger-children > *:nth-child(1) { animation-delay: 0ms; } +.stagger-children > *:nth-child(2) { animation-delay: 100ms; } +.stagger-children > *:nth-child(3) { animation-delay: 200ms; } +.stagger-children > *:nth-child(4) { animation-delay: 300ms; } +.stagger-children > *:nth-child(5) { animation-delay: 400ms; } +.stagger-children > *:nth-child(6) { animation-delay: 500ms; } + +/* Hover Animations */ +.hover-lift { + transition: transform var(--transition-normal), box-shadow var(--transition-normal); +} + +.hover-lift:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); +} + +.hover-scale { + transition: transform var(--transition-fast); +} + +.hover-scale:hover { + transform: scale(1.02); +} + +.hover-glow { + transition: box-shadow var(--transition-normal); +} + +.hover-glow:hover { + box-shadow: 0 0 20px rgba(59, 130, 246, 0.3); +} + +/* Button Styles */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-weight: 500; + border-radius: var(--radius-md); + transition: all var(--transition-normal); + cursor: pointer; + border: none; + text-decoration: none; +} + +.btn:active { + transform: scale(0.98); +} + +.btn-primary { + background: var(--color-primary-600); + color: white; +} + +.btn-primary:hover { + background: var(--color-primary-700); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4); +} + +.btn-secondary { + background: transparent; + color: var(--color-primary-600); + border: 2px solid var(--color-primary-600); +} + +.btn-secondary:hover { + background: var(--color-primary-50); +} + +[data-theme="dark"] .btn-secondary:hover { + background: var(--color-primary-900); +} + +/* Card Styles */ +.card { + background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: var(--radius-xl); + padding: 2rem; + transition: all var(--transition-normal); +} + +.card:hover { + border-color: var(--color-primary-200); + box-shadow: var(--shadow-md); +} + +[data-theme="dark"] .card:hover { + border-color: var(--color-primary-800); +} + +/* Icon Container */ +.icon-container { + display: flex; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + background: var(--color-primary-100); + border-radius: var(--radius-lg); + transition: all var(--transition-normal); +} + +.icon-container-lg { + width: 4rem; + height: 4rem; +} + +[data-theme="dark"] .icon-container { + background: var(--color-primary-900); +} + +.group:hover .icon-container { + background: var(--color-primary-200); + transform: scale(1.1); +} + +[data-theme="dark"] .group:hover .icon-container { + background: var(--color-primary-800); +} + +/* Section Styles */ +.section { + padding: var(--section-spacing) 0; +} + +.container { + max-width: 80rem; + margin: 0 auto; + padding: 0 var(--container-padding); +} + +@media (min-width: 640px) { + :root { + --container-padding: 1.5rem; + } +} + +@media (min-width: 1024px) { + :root { + --container-padding: 2rem; + } +} + +/* Link Styles */ +.link { + color: var(--color-primary-600); + text-decoration: none; + transition: color var(--transition-fast); + position: relative; +} + +.link:hover { + color: var(--color-primary-700); +} + +.link-underline::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + background: var(--color-primary-600); + transition: width var(--transition-normal); +} + +.link-underline:hover::after { + width: 100%; +} + +/* Prose adjustments for dark mode */ +[data-theme="dark"] .prose { + --tw-prose-body: var(--color-foreground-secondary); + --tw-prose-headings: var(--color-foreground); + --tw-prose-links: var(--color-primary-400); + --tw-prose-bold: var(--color-foreground); +} + +/* Reduce motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + + html { + scroll-behavior: auto; + } } diff --git a/proto/src/app/kapcsolat/layout.tsx b/proto/src/app/kapcsolat/layout.tsx index d07072d..58a1f6a 100644 --- a/proto/src/app/kapcsolat/layout.tsx +++ b/proto/src/app/kapcsolat/layout.tsx @@ -1,12 +1,15 @@ import { siteConfig } from '@/config/site' +import { content } from '@/content' import type { Metadata } from 'next' +const { contact: pageContent } = content.pages + export const metadata: Metadata = { - title: `Kapcsolat | ${siteConfig.general.name}`, - description: 'Vegye fel velünk a kapcsolatot! Segítünk minden IT kérdésében. Email, telefon és online űrlap is rendelkezésére áll.', + title: `${pageContent.meta.title} | ${siteConfig.general.name}`, + description: pageContent.meta.description, openGraph: { - title: `Kapcsolat | ${siteConfig.general.name}`, - description: 'Vegye fel velünk a kapcsolatot! Segítünk minden IT kérdésében.', + title: `${pageContent.meta.title} | ${siteConfig.general.name}`, + description: pageContent.meta.description, url: `${siteConfig.general.url}/kapcsolat`, }, } diff --git a/proto/src/app/kapcsolat/page.tsx b/proto/src/app/kapcsolat/page.tsx index 667563c..7335ee0 100644 --- a/proto/src/app/kapcsolat/page.tsx +++ b/proto/src/app/kapcsolat/page.tsx @@ -1,8 +1,11 @@ 'use client' import { siteConfig } from '@/config/site' +import { content } from '@/content' import { useState } from 'react' +const { contact: pageContent } = content.pages + export default function ContactPage() { const [formData, setFormData] = useState({ name: '', @@ -20,27 +23,27 @@ export default function ContactPage() { const newErrors: Record = {} if (!formData.name.trim()) { - newErrors.name = 'A név megadása kötelező' + newErrors.name = pageContent.form.fields.name.error } if (!formData.email.trim()) { - newErrors.email = 'Az email cím megadása kötelező' + newErrors.email = pageContent.form.fields.email.errorRequired } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { - newErrors.email = 'Érvénytelen email cím formátum' + newErrors.email = pageContent.form.fields.email.errorInvalid } if (!formData.subject.trim()) { - newErrors.subject = 'A tárgy megadása kötelező' + newErrors.subject = pageContent.form.fields.subject.error } if (!formData.message.trim()) { - newErrors.message = 'Az üzenet megadása kötelező' + newErrors.message = pageContent.form.fields.message.errorRequired } else if (formData.message.trim().length < 10) { - newErrors.message = 'Az üzenet legalább 10 karakter hosszú legyen' + newErrors.message = pageContent.form.fields.message.errorMinLength } if (!formData.gdprConsent) { - newErrors.gdprConsent = 'Az adatkezelési tájékoztató elfogadása kötelező' + newErrors.gdprConsent = pageContent.form.fields.gdpr.error } setErrors(newErrors) @@ -93,7 +96,6 @@ export default function ContactPage() { [name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value })) - // Clear error when user starts typing if (errors[name]) { setErrors(prev => ({ ...prev, [name]: '' })) } @@ -105,10 +107,10 @@ export default function ContactPage() {

- Kapcsolat + {pageContent.hero.title}

- Vegye fel velünk a kapcsolatot! Szívesen segítünk minden IT kérdésében. + {pageContent.hero.subtitle}

@@ -119,13 +121,13 @@ export default function ContactPage() {

- Küldjön üzenetet + {pageContent.form.title}

{submitStatus === 'success' && (

- ✅ Köszönjük üzenetét! Hamarosan felvesszük Önnel a kapcsolatot. + ✅ {pageContent.form.successMessage}

)} @@ -133,7 +135,7 @@ export default function ContactPage() { {submitStatus === 'error' && (

- ❌ Hiba történt az üzenet küldése során. Kérjük, próbálja újra vagy írjon közvetlenül a {siteConfig.contact.email} címre. + ❌ {pageContent.form.errorMessage.replace('{email}', siteConfig.contact.email)}

)} @@ -141,7 +143,7 @@ export default function ContactPage() {
{errors.name &&

{errors.name}

}
{errors.email &&

{errors.email}

}
{errors.subject &&

{errors.subject}

}