First Commit - Portfolio Page
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// src/i18n/locales/en/blog/index.ts
|
||||
export const blog = {
|
||||
meta: {
|
||||
title: 'Blog - JTL Integration & Digital Solutions',
|
||||
description: 'Insights and Best Practices on JTL-WaWi, E-Commerce, and Digital Transformation',
|
||||
header: {
|
||||
title: 'Insights & Best Practices',
|
||||
subtitle: 'Insights and Best Practices on JTL-WaWi, E-Commerce, and Digital Transformation'
|
||||
}
|
||||
},
|
||||
posts: {
|
||||
'erp-integration-breuninger': {
|
||||
title: 'ApparelMagic and TradeByte: Analysis of Complex Integrations',
|
||||
date: '2024-02-09',
|
||||
excerpt: 'Detailed analysis of an e-commerce integration between Apparel Magic and Breuninger via TradeByte',
|
||||
category: 'System Integration',
|
||||
tags: ['ERP', 'Apparel Magic', 'TradeByte', 'API Integration', 'Python', 'MariaDB'],
|
||||
coverImage: '/images/posts/erp-integration-breuninger/cover.jpg',
|
||||
content: {
|
||||
intro: {
|
||||
title: 'Apparel Magic meets Breuninger: A Technical Analysis of the TradeByte Integration',
|
||||
description: 'The integration of e-commerce systems with established marketplaces poses unique challenges for companies. In this article, I share my experiences from a complex integration project between Apparel Magic as an ERP system and the Breuninger platform via TradeByte.'
|
||||
},
|
||||
background: {
|
||||
title: 'Project Background',
|
||||
systems: {
|
||||
erp: 'Apparel Magic as an ERP system for product data and inventory management',
|
||||
middleware: 'TradeByte as the middleware for the Breuninger platform'
|
||||
},
|
||||
challenges: {
|
||||
sync: 'Bidirectional synchronization of orders',
|
||||
customers: 'Automated customer creation',
|
||||
delivery: 'Management of delivery notes',
|
||||
inventory: 'Real-time inventory management'
|
||||
}
|
||||
},
|
||||
tech: {
|
||||
title: 'Technical Architecture',
|
||||
components: {
|
||||
title: 'Key Components'
|
||||
},
|
||||
database: {
|
||||
title: 'Middleware Database',
|
||||
description: 'The MariaDB database serves as the central data store for the integration:'
|
||||
}
|
||||
},
|
||||
api: {
|
||||
title: 'API Integrations',
|
||||
apparel_magic: {
|
||||
title: 'Apparel Magic API',
|
||||
description: 'The REST API of Apparel Magic is used for customer creation and inventory management:'
|
||||
},
|
||||
tradebyte: {
|
||||
title: 'TradeByte Integration',
|
||||
description: 'The TradeByte API uses a combination of REST and XML:'
|
||||
}
|
||||
},
|
||||
order_process: {
|
||||
title: 'Order Process',
|
||||
steps: {
|
||||
fetch: 'Regular retrieval of new orders from TradeByte',
|
||||
customers: 'Automatic customer creation in Apparel Magic',
|
||||
process: 'Order processing and status update',
|
||||
delivery: 'Generation and storage of delivery notes'
|
||||
},
|
||||
delivery_notes: {
|
||||
title: 'Delivery Note Management'
|
||||
}
|
||||
},
|
||||
challenges: {
|
||||
title: 'Challenges and Solutions',
|
||||
status: {
|
||||
title: '1. Status Management',
|
||||
description: 'A particular challenge was managing order statuses:'
|
||||
},
|
||||
error_handling: {
|
||||
title: '2. Error Handling and Monitoring',
|
||||
description: 'Robust error handling was essential for production operation:'
|
||||
}
|
||||
},
|
||||
best_practices: {
|
||||
title: 'Best Practices',
|
||||
api: {
|
||||
title: '1. API Communication',
|
||||
items: {
|
||||
retry: 'Implementation of retry mechanisms',
|
||||
errors: 'Careful error handling',
|
||||
logging: 'Comprehensive logging of all communications'
|
||||
}
|
||||
},
|
||||
data: {
|
||||
title: '2. Data Management',
|
||||
items: {
|
||||
storage: 'Central data storage in MariaDB',
|
||||
transactions: 'Transactional security for critical operations',
|
||||
validation: 'Regular data validation'
|
||||
}
|
||||
},
|
||||
automation: {
|
||||
title: '3. Process Automation',
|
||||
items: {
|
||||
customers: 'Automated customer creation',
|
||||
status: 'Automatic status updates',
|
||||
delivery: 'Systematic delivery note management'
|
||||
}
|
||||
}
|
||||
},
|
||||
lessons: {
|
||||
title: 'Lessons Learned',
|
||||
api: {
|
||||
title: '1. API Design',
|
||||
items: {
|
||||
docs: 'Thorough API documentation is essential',
|
||||
auth: 'Understanding of various authentication methods',
|
||||
limits: 'Consideration of rate limits'
|
||||
}
|
||||
},
|
||||
validation: {
|
||||
title: '2. Data Validation',
|
||||
items: {
|
||||
rules: 'Implementation of strict validation rules',
|
||||
edge_cases: 'Careful handling of edge cases',
|
||||
cleanup: 'Automated data cleanup'
|
||||
}
|
||||
},
|
||||
optimization: {
|
||||
title: '3. Process Optimization',
|
||||
items: {
|
||||
automation: 'Continuous improvement of automation',
|
||||
performance: 'Regular performance reviews',
|
||||
monitoring: 'Proactive monitoring'
|
||||
}
|
||||
}
|
||||
},
|
||||
conclusion: {
|
||||
title: 'Conclusion',
|
||||
requirements: {
|
||||
understanding: 'Deep understanding of both systems',
|
||||
implementation: 'Careful implementation of API communication',
|
||||
error_handling: 'Robust error handling',
|
||||
monitoring: 'Continuous monitoring'
|
||||
},
|
||||
results: 'The developed solution successfully processes orders and enables seamless integration between the systems. In particular, automated customer creation and delivery note management have proven to be efficiency boosters.'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
ui: {
|
||||
readMore: 'Read More',
|
||||
loading: {
|
||||
posts: 'Loading blog posts...',
|
||||
post: 'Loading blog post...'
|
||||
},
|
||||
errors: {
|
||||
posts: 'Error loading blog posts',
|
||||
post: 'The requested blog post does not exist or has been moved.'
|
||||
},
|
||||
backToBlog: 'Back to Blog',
|
||||
publishedOn: 'Published on',
|
||||
category: 'Category',
|
||||
tags: 'Tags',
|
||||
notFound: {
|
||||
title: 'Post Not Found',
|
||||
message: 'The requested blog post does not exist or has been moved.'
|
||||
}
|
||||
},
|
||||
categories: {
|
||||
'Integration': 'Integration',
|
||||
'Entwicklung': 'Development',
|
||||
'Automatisierung': 'Automation'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type BlogConfig = typeof blog;
|
||||
export type BlogPost = BlogConfig['posts'][keyof BlogConfig['posts']];
|
||||
@@ -0,0 +1,138 @@
|
||||
// src/i18n/locales/en/blog/posts/erp-integration-breuninger.ts
|
||||
export const erpIntegrationBreuninger = {
|
||||
posts: {
|
||||
erp_integration: {
|
||||
title: "ApparelMagic and TradeByte: Analysis of Complex Integrations",
|
||||
date: "2024-02-09",
|
||||
excerpt: "Detailed analysis of an e-commerce integration between Apparel Magic and Breuninger via TradeByte",
|
||||
category: "System Integration",
|
||||
tags: ["ERP", "Apparel Magic", "TradeByte", "API Integration", "Python", "MariaDB"],
|
||||
coverImage: "/images/posts/erp-integration-breuninger/cover.jpg",
|
||||
intro: {
|
||||
title: "Apparel Magic meets Breuninger: A Technical Analysis of the TradeByte Integration",
|
||||
description: "The integration of e-commerce systems with established marketplaces presents unique challenges for companies. In this article, I share my experiences from a complex integration project between Apparel Magic as an ERP system and the Breuninger platform via TradeByte."
|
||||
},
|
||||
background: {
|
||||
title: "Project Background",
|
||||
systems: {
|
||||
erp: "Apparel Magic as an ERP system for product data and inventory management",
|
||||
middleware: "TradeByte as middleware for the Breuninger platform"
|
||||
},
|
||||
challenges: {
|
||||
sync: "Bidirectional synchronization of orders",
|
||||
customers: "Automated customer creation",
|
||||
delivery: "Management of delivery notes",
|
||||
inventory: "Real-time inventory management"
|
||||
}
|
||||
},
|
||||
tech: {
|
||||
title: "Technical Architecture",
|
||||
components: {
|
||||
title: "Central Components"
|
||||
},
|
||||
database: {
|
||||
title: "Middleware Database",
|
||||
description: "The MariaDB database serves as the central data store for the integration:"
|
||||
}
|
||||
},
|
||||
api: {
|
||||
title: "API Integrations",
|
||||
apparel_magic: {
|
||||
title: "Apparel Magic API",
|
||||
description: "The REST API of Apparel Magic is used for customer creation and inventory management:"
|
||||
},
|
||||
tradebyte: {
|
||||
title: "TradeByte Integration",
|
||||
description: "The TradeByte API uses a combination of REST and XML:"
|
||||
}
|
||||
},
|
||||
order_process: {
|
||||
title: "Order Process",
|
||||
steps: {
|
||||
fetch: "Regular retrieval of new orders from TradeByte",
|
||||
customers: "Automatic customer creation in Apparel Magic",
|
||||
process: "Order processing and status update",
|
||||
delivery: "Generation and storage of delivery notes"
|
||||
},
|
||||
delivery_notes: {
|
||||
title: "Delivery Note Management"
|
||||
}
|
||||
},
|
||||
challenges: {
|
||||
title: "Challenges and Solutions",
|
||||
status: {
|
||||
title: "1. Status Management",
|
||||
description: "A particular challenge was managing order statuses:"
|
||||
},
|
||||
error_handling: {
|
||||
title: "2. Error Handling and Monitoring",
|
||||
description: "Robust error handling was essential for production operations:"
|
||||
}
|
||||
},
|
||||
best_practices: {
|
||||
title: "Best Practices",
|
||||
api: {
|
||||
title: "1. API Communication",
|
||||
items: {
|
||||
retry: "Implementation of retry mechanisms",
|
||||
errors: "Careful error handling",
|
||||
logging: "Comprehensive logging of all communications"
|
||||
}
|
||||
},
|
||||
data: {
|
||||
title: "2. Data Management",
|
||||
items: {
|
||||
storage: "Central data storage in MariaDB",
|
||||
transactions: "Transactional security for critical operations",
|
||||
validation: "Regular data validation"
|
||||
}
|
||||
},
|
||||
automation: {
|
||||
title: "3. Process Automation",
|
||||
items: {
|
||||
customers: "Automated customer creation",
|
||||
status: "Automatic status updates",
|
||||
delivery: "Systematic delivery note management"
|
||||
}
|
||||
}
|
||||
},
|
||||
lessons: {
|
||||
title: "Lessons Learned",
|
||||
api: {
|
||||
title: "1. API Design",
|
||||
items: {
|
||||
docs: "Thorough API documentation is essential",
|
||||
auth: "Understanding of various authentication methods",
|
||||
limits: "Consideration of rate limits"
|
||||
}
|
||||
},
|
||||
validation: {
|
||||
title: "2. Data Validation",
|
||||
items: {
|
||||
rules: "Implementation of strict validation rules",
|
||||
edge_cases: "Careful handling of edge cases",
|
||||
cleanup: "Automated data cleanup"
|
||||
}
|
||||
},
|
||||
optimization: {
|
||||
title: "3. Process Optimization",
|
||||
items: {
|
||||
automation: "Continuous improvement of automation",
|
||||
performance: "Regular performance reviews",
|
||||
monitoring: "Proactive monitoring"
|
||||
}
|
||||
}
|
||||
},
|
||||
conclusion: {
|
||||
title: "Conclusion",
|
||||
requirements: {
|
||||
understanding: "Deep understanding of both systems",
|
||||
implementation: "Careful implementation of the API communication",
|
||||
error_handling: "Robust error handling",
|
||||
monitoring: "Continuous monitoring"
|
||||
},
|
||||
results: "The developed solution successfully processes orders and enables seamless integration between the systems. In particular, the automated customer creation and delivery note management have proven to enhance efficiency."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,253 @@
|
||||
// src/i18n/locales/en/blog/posts/fullstack-development-timetracking.ts
|
||||
export const fullstackDevelopmentTimetracking = {
|
||||
meta: {
|
||||
title: 'Fullstack Development with Python and React: Architecture of our Time Tracking Solution',
|
||||
date: '2024-02-09',
|
||||
excerpt: 'A technical deep dive into the implementation of a modern time tracking solution',
|
||||
category: 'System Architecture',
|
||||
coverImage: '/images/posts/fullstack-development-timetracking/cover.jpg',
|
||||
tags: ['Python', 'React', 'TypeScript', 'MSSQL', 'System Design']
|
||||
},
|
||||
content: {
|
||||
intro: {
|
||||
title: 'Fullstack Development with Python and React: Architecture of our Time Tracking Solution',
|
||||
description: 'Developing a robust time tracking solution requires not only technical expertise but also a deep understanding of complex business rules and user requirements. In this article, I share our experiences in implementing a modern fullstack time tracking solution with Python and React.'
|
||||
},
|
||||
systemArchitecture: {
|
||||
title: 'System Architecture',
|
||||
frontend: {
|
||||
title: 'Frontend (Next.js + TypeScript)',
|
||||
description: 'The frontend architecture is based on Next.js with TypeScript and follows a component-based approach:',
|
||||
code: {
|
||||
types: `// types/TimeEntry.ts
|
||||
interface TimeEntry {
|
||||
id: number;
|
||||
date: string;
|
||||
checkIn: string;
|
||||
checkOut: string | null;
|
||||
userId: number;
|
||||
status: 'complete' | 'incomplete';
|
||||
}`,
|
||||
component: `// components/TimeEntryForm.tsx
|
||||
const TimeEntryForm: React.FC<TimeEntryFormProps> = ({ onSubmit }) => {
|
||||
const [entry, setEntry] = useState<TimeEntry>({
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
checkIn: new Date().toLocaleTimeString(),
|
||||
checkOut: null,
|
||||
status: 'incomplete'
|
||||
});
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateTimeEntry(entry)) return;
|
||||
try {
|
||||
await onSubmit(entry);
|
||||
} catch (error) {
|
||||
console.error('Error submitting time entry:', error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className='space-y-4'>
|
||||
<DateInput
|
||||
value={entry.date}
|
||||
onChange={(date) => setEntry({ ...entry, date })}
|
||||
/>
|
||||
<TimeInput
|
||||
value={entry.checkIn}
|
||||
onChange={(time) => setEntry({ ...entry, checkIn: time })}
|
||||
/>
|
||||
{/* Additional form elements */}
|
||||
</form>
|
||||
);
|
||||
};`
|
||||
}
|
||||
},
|
||||
backend: {
|
||||
title: 'Backend (Flask + MSSQL)',
|
||||
description: 'The backend uses Flask for the API and MSSQL for data persistence:',
|
||||
code: {
|
||||
models: `# models/time_entry.py
|
||||
class TimeEntry(db.Model):
|
||||
__tablename__ = 'Stundenzettel'
|
||||
id = db.Column('ID', db.Decimal, primary_key=True)
|
||||
personal_id = db.Column('Personal_ID', db.Decimal)
|
||||
datum = db.Column('Datum', db.Date)
|
||||
kommen = db.Column('Kommen', db.Time)
|
||||
gehen = db.Column('Gehen', db.Time)
|
||||
@validates('gehen')
|
||||
def validate_checkout(self, key, value):
|
||||
if value and value < self.kommen:
|
||||
raise ValueError('Checkout time cannot be before checkin time')
|
||||
return value`,
|
||||
routes: `# routes/time_entries.py
|
||||
@app.route('/api/time-entries', methods=['POST'])
|
||||
@jwt_required
|
||||
def create_time_entry():
|
||||
data = request.get_json()
|
||||
user_id = get_jwt_identity()
|
||||
try:
|
||||
validate_time_entry_creation(data, user_id)
|
||||
entry = TimeEntry(
|
||||
personal_id=user_id,
|
||||
datum=data['date'],
|
||||
kommen=data['checkIn'],
|
||||
gehen=data.get('checkOut')
|
||||
)
|
||||
db.session.add(entry)
|
||||
db.session.commit()
|
||||
return jsonify(entry.to_dict()), 201
|
||||
except ValidationError as e:
|
||||
return jsonify({'error': str(e)}), 400`
|
||||
}
|
||||
}
|
||||
},
|
||||
businessLogic: {
|
||||
title: 'Business Logic Implementation',
|
||||
validation: {
|
||||
title: 'Time Entry Validation',
|
||||
description: 'The validation logic ensures that all business rules are followed:',
|
||||
code: `def validate_time_entry_creation(data: dict, user_id: int) -> None:
|
||||
'''Validates a new time entry according to business rules.'''
|
||||
# Check for existing incomplete entries
|
||||
incomplete_entry = TimeEntry.query.filter_by(
|
||||
personal_id=user_id,
|
||||
gehen=None,
|
||||
datum=data['date']
|
||||
).first()
|
||||
if incomplete_entry:
|
||||
raise ValidationError('Cannot create new entry while incomplete entry exists')
|
||||
# Check for time overlap with existing entries
|
||||
overlapping_entry = TimeEntry.query.filter(
|
||||
TimeEntry.personal_id == user_id,
|
||||
TimeEntry.datum == data['date'],
|
||||
TimeEntry.kommen <= data['checkIn'],
|
||||
TimeEntry.gehen >= data['checkIn']
|
||||
).first()
|
||||
if overlapping_entry:
|
||||
raise ValidationError('Time entry overlaps with existing entry')`
|
||||
}
|
||||
},
|
||||
databaseDesign: {
|
||||
title: 'Database Design',
|
||||
description: 'The MSSQL database schema is designed for efficiency and integrity:',
|
||||
code: `CREATE TABLE dbo.Stundenzettel (
|
||||
ID decimal NOT NULL PRIMARY KEY,
|
||||
Personal_ID decimal,
|
||||
Datum date,
|
||||
Kommen time,
|
||||
Gehen time,
|
||||
xStatus int,
|
||||
xBenutzer nvarchar(15),
|
||||
xDatum datetime,
|
||||
xVersion timestamp
|
||||
);
|
||||
CREATE INDEX idx_personal_datum
|
||||
ON dbo.Stundenzettel(Personal_ID, Datum);`
|
||||
},
|
||||
security: {
|
||||
title: 'Security Implementation',
|
||||
authentication: {
|
||||
title: 'JWT Authentication',
|
||||
code: `# auth/jwt_handler.py
|
||||
from flask_jwt_extended import create_access_token
|
||||
def authenticate_user(username: str, password: str) -> str:
|
||||
user = Personal.query.filter_by(
|
||||
Benutzername=username,
|
||||
Passwort=password # In production, use proper password hashing
|
||||
).first()
|
||||
if not user:
|
||||
raise AuthenticationError('Invalid credentials')
|
||||
return create_access_token(identity=user.ID)`
|
||||
}
|
||||
},
|
||||
bestPractices: {
|
||||
title: 'Best Practices and Learnings',
|
||||
points: [
|
||||
{
|
||||
title: 'Data Validation at Multiple Levels',
|
||||
items: [
|
||||
'Frontend validation for immediate feedback',
|
||||
'Backend validation for business rules',
|
||||
'Database constraints for data integrity'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Error Handling',
|
||||
items: [
|
||||
'Structured error messages',
|
||||
'User-friendly error messages in frontend',
|
||||
'Detailed logging in backend'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Performance Optimization',
|
||||
items: [
|
||||
'Indexing of critical database fields',
|
||||
'Frontend caching of time entries',
|
||||
'Lazy loading for historical data'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
challenges: {
|
||||
title: 'Challenges and Solutions',
|
||||
timezones: {
|
||||
title: '1. Timezone Handling',
|
||||
description: 'A particular challenge was the correct handling of timezones:',
|
||||
code: `// utils/dateTime.ts
|
||||
export const formatTimeForDisplay = (time: string): string => {
|
||||
return new Date(\`1970-01-01T\${time}\`).toLocaleTimeString('de-DE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
export const formatTimeForAPI = (time: string): string => {
|
||||
return new Date(\`1970-01-01T\${time}\`).toISOString().split('T')[1];
|
||||
};`
|
||||
},
|
||||
concurrency: {
|
||||
title: '2. Concurrent Updates',
|
||||
description: 'Handling concurrent updates required special attention:',
|
||||
code: `from sqlalchemy import and_, or_
|
||||
def update_time_entry(entry_id: int, data: dict) -> TimeEntry:
|
||||
entry = TimeEntry.query.filter_by(id=entry_id).with_for_update().first()
|
||||
if not entry:
|
||||
raise NotFoundError('Time entry not found')
|
||||
# Optimistic locking using version field
|
||||
if entry.xVersion != data['version']:
|
||||
raise ConcurrencyError('Entry was modified by another user')
|
||||
entry.gehen = data.get('checkOut')
|
||||
db.session.commit()
|
||||
return entry`
|
||||
},
|
||||
offline: {
|
||||
title: '3. Offline Capability',
|
||||
description: 'For offline functionality, we implemented a service worker strategy:',
|
||||
code: `// service-worker.ts
|
||||
const CACHE_NAME = 'timetracking-v1';
|
||||
self.addEventListener('fetch', (event) => {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(response => {
|
||||
return response || fetch(event.request).then(response => {
|
||||
return caches.open(CACHE_NAME).then(cache => {
|
||||
cache.put(event.request, response.clone());
|
||||
return response;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});`
|
||||
}
|
||||
},
|
||||
conclusion: {
|
||||
title: 'Conclusion',
|
||||
description: 'Developing a time tracking solution requires careful planning and consideration of numerous business rules. By using modern technologies like Next.js, TypeScript, and Flask, we were able to implement a robust and user-friendly solution.',
|
||||
keyPoints: [
|
||||
'Strict typing with TypeScript',
|
||||
'Comprehensive validation logic',
|
||||
'Efficient database design',
|
||||
'User-friendly error handling'
|
||||
],
|
||||
results: 'The solution has been successfully in use for several months and reliably processes hundreds of time entries daily.'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
// src/i18n/locales/en/blog/posts/rfid-automation.ts
|
||||
export const rfidAutomation = {
|
||||
meta: {
|
||||
title: 'RFID Label Automation: From Database to Printer',
|
||||
date: '2024-02-09',
|
||||
excerpt: 'A detailed technical analysis of implementing an automated RFID label printing system with Python',
|
||||
category: 'System Integration',
|
||||
coverImage: '/images/posts/rfid-automation/cover.jpg',
|
||||
tags: ['RFID', 'Python', 'ZPL', 'Automation', 'Zebra Printer', 'EPC']
|
||||
},
|
||||
content: {
|
||||
intro: {
|
||||
title: 'RFID Label Automation: From Database to Printer',
|
||||
description: 'In modern logistics and retail environments, efficient and error-free labeling of products with RFID tags is crucial. In this article, I share my experiences implementing an automated solution for generating and printing RFID labels with chip encoding.'
|
||||
},
|
||||
requirements: {
|
||||
title: 'Project Background',
|
||||
points: [
|
||||
'Automatic generation of EPC codes (Electronic Product Code)',
|
||||
'Creation of ZPL code for Zebra printers',
|
||||
'Direct network communication with the printer',
|
||||
'Integration with existing product data',
|
||||
'Error handling and logging'
|
||||
]
|
||||
},
|
||||
implementation: {
|
||||
title: 'Technical Implementation',
|
||||
epcGeneration: {
|
||||
title: 'EPC Code Generation',
|
||||
description: 'The EPC codes are generated according to the SGTIN standard (Serialized Global Trade Item Number):',
|
||||
code: `def generate_encoded_epc(company_prefix, indicator, item_ref, serial):
|
||||
sgtin = SGTIN(company_prefix, indicator, item_ref, serial)
|
||||
return sgtin.encode()`
|
||||
},
|
||||
zplTemplates: {
|
||||
title: 'ZPL Template Management',
|
||||
description: 'ZPL templates were implemented for different label types:',
|
||||
code: `def generate_zpl_code(artikelnr, description, ean, price, material, water_resistance, glass_type, encoded_epc):
|
||||
formatted_price = f'{price:.2f}'
|
||||
return f'''
|
||||
CT~~CD,~CC^~CT~
|
||||
^XA
|
||||
~TA000
|
||||
~JSN
|
||||
^LT35
|
||||
^MNW
|
||||
^MTT
|
||||
^PON
|
||||
^PMN
|
||||
^LH0,0
|
||||
^JMA
|
||||
^PR2,2
|
||||
~SD23
|
||||
^JUS
|
||||
^LRN
|
||||
^CI27
|
||||
^PA0,1,1,0
|
||||
^RS8,,,3
|
||||
^XZ
|
||||
^XA
|
||||
^MMT
|
||||
^PW413
|
||||
^LL531
|
||||
^LS-24
|
||||
^FT150,57^A0N,33,33^FH\\^CI27^FD{artikelnr}^FS^CI27
|
||||
^FT44,86^A0N,21,20^FH\\^CI27^FD{description}^FS^CI27
|
||||
^FT130,141^A0N,50,51^FH\\^CI27^FD{formatted_price} €^FS^CI27
|
||||
^FT167,175^A0N,21,20^FH\\^CI27^FD{material}^FS^CI27
|
||||
^FT185,201^A0N,21,20^FH\\^CI27^FD{water_resistance}^FS^CI27
|
||||
^FT155,227^A0N,21,20^FH\\^CI27^FD{glass_type}^FS^CI27
|
||||
^BY3,2,113^FT73,420^BEN,,Y,N
|
||||
^FH\\^FD{ean}^FS
|
||||
^RFW,H,1,2,1^FD3000^FS
|
||||
^RFW,H,2,12,1^FD{encoded_epc}^FS
|
||||
^PQ1,0,1,Y
|
||||
^XZ'''`
|
||||
},
|
||||
printerCommunication: {
|
||||
title: 'Network Communication with the Printer',
|
||||
description: 'Communication with the Zebra printer is done via TCP/IP:',
|
||||
code: `def send_to_printer(data, printer_ip='192.168.68.50', printer_port=9100):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect((printer_ip, printer_port))
|
||||
sock.sendall(data.encode('utf-8'))
|
||||
logger.info('Data successfully sent to printer')
|
||||
except Exception as e:
|
||||
logger.error(f'Error sending data to printer: {e}')`
|
||||
},
|
||||
serialNumberManagement: {
|
||||
title: 'Data Processing and Serial Number Management',
|
||||
description: 'Managing serial numbers is critical for the uniqueness of EPC codes:',
|
||||
code: `def initialize_serial_number():
|
||||
global current_serial_number
|
||||
try:
|
||||
serial_log_content = SERIAL_NUMBER_LOG_PATH.read_text().strip()
|
||||
current_serial_number = int(serial_log_content)
|
||||
except (FileNotFoundError, ValueError):
|
||||
current_serial_number = START_SERIAL_NUMBER
|
||||
def get_next_serial_number():
|
||||
global current_serial_number
|
||||
current_serial_number += 1
|
||||
return current_serial_number
|
||||
def finalize_serial_number():
|
||||
SERIAL_NUMBER_LOG_PATH.write_text(str(current_serial_number))`
|
||||
}
|
||||
},
|
||||
features: {
|
||||
title: 'Implemented Features',
|
||||
errorHandling: {
|
||||
title: '1. Robust Error Handling',
|
||||
points: [
|
||||
'Input data validation',
|
||||
'Network connection verification',
|
||||
'Serial number persistence',
|
||||
'Detailed logging of all operations'
|
||||
]
|
||||
},
|
||||
configurability: {
|
||||
title: '2. Configurability',
|
||||
points: [
|
||||
'Adjustable printer IP and port',
|
||||
'Customizable ZPL templates',
|
||||
'Flexible EPC code generation',
|
||||
'Configurable serial number ranges'
|
||||
]
|
||||
},
|
||||
scalability: {
|
||||
title: '3. Scalability',
|
||||
points: [
|
||||
'Batch processing of product data',
|
||||
'Parallel print jobs possible',
|
||||
'Efficient resource management',
|
||||
'Modular code for easy extensibility'
|
||||
]
|
||||
}
|
||||
},
|
||||
bestPractices: {
|
||||
title: 'Best Practices',
|
||||
dataValidation: {
|
||||
title: '1. Data Validation',
|
||||
points: [
|
||||
'Strict typing for critical data fields',
|
||||
'Verification of required fields',
|
||||
'Format and plausibility checks'
|
||||
]
|
||||
},
|
||||
faultTolerance: {
|
||||
title: '2. Fault Tolerance',
|
||||
points: [
|
||||
'Automatic retry attempts',
|
||||
'Graceful degradation',
|
||||
'Detailed error logs'
|
||||
]
|
||||
},
|
||||
maintainability: {
|
||||
title: '3. Maintainability',
|
||||
points: [
|
||||
'Modular code structure',
|
||||
'Comprehensive documentation',
|
||||
'Clear separation of configuration and code'
|
||||
]
|
||||
}
|
||||
},
|
||||
lessonsLearned: {
|
||||
title: 'Lessons Learned',
|
||||
zplSpecifics: {
|
||||
title: '1. ZPL Specifics',
|
||||
points: [
|
||||
'Detailed knowledge of ZPL specifications necessary',
|
||||
'Careful validation of generated ZPL codes',
|
||||
'Regular testing with different printer models'
|
||||
]
|
||||
},
|
||||
rfidStandards: {
|
||||
title: '2. RFID Standards',
|
||||
points: [
|
||||
'Adherence to EPC standards critical',
|
||||
'Careful management of serial numbers',
|
||||
'Validation of generated EPC codes'
|
||||
]
|
||||
},
|
||||
networkCommunication: {
|
||||
title: '3. Network Communication',
|
||||
points: [
|
||||
'Robust error handling for network issues',
|
||||
'Timeouts and retry attempts',
|
||||
'Buffering of print jobs'
|
||||
]
|
||||
}
|
||||
},
|
||||
conclusion: {
|
||||
title: 'Conclusion',
|
||||
description: 'The implemented solution enables efficient and reliable automation of the RFID labeling process. Through the combination of EPC code generation, ZPL template management, and direct printer communication, a robust system has been created that has proven itself in practice.',
|
||||
keyPoints: [
|
||||
'Thorough planning of system architecture',
|
||||
'Comprehensive error handling',
|
||||
'Careful documentation',
|
||||
'Regular testing and validation'
|
||||
],
|
||||
results: 'The solution has been running stably in production for several months and processes hundreds of labels daily.'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
export const common = {
|
||||
// SEO & Meta
|
||||
siteTitle: 'Damjan Savić - Digital Solutions & JTL Integration',
|
||||
siteDescription: 'Developer of custom solutions for process automation',
|
||||
// Navigation
|
||||
nav: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
services: 'Services',
|
||||
portfolio: 'Portfolio',
|
||||
blog: 'Blog',
|
||||
contact: 'Contact'
|
||||
},
|
||||
// Common Actions
|
||||
actions: {
|
||||
readMore: 'Read More',
|
||||
viewMore: 'View More',
|
||||
viewAll: 'View All',
|
||||
back: 'Back',
|
||||
close: 'Close',
|
||||
submit: 'Submit',
|
||||
loading: 'Loading...'
|
||||
},
|
||||
// UI Components
|
||||
ui: {
|
||||
published: 'Published on',
|
||||
lastUpdated: 'Last updated on',
|
||||
category: 'Category',
|
||||
categories: 'Categories',
|
||||
tag: 'Tag',
|
||||
tags: 'Tags',
|
||||
author: 'Author',
|
||||
minuteRead: 'minute read',
|
||||
tableOfContents: 'Table of Contents'
|
||||
},
|
||||
// Error Messages
|
||||
errors: {
|
||||
generic: 'An error has occurred',
|
||||
notFound: 'Page not found',
|
||||
loading: 'Error loading',
|
||||
return: 'Return to homepage'
|
||||
},
|
||||
// Loading States
|
||||
loading: {
|
||||
content: 'Loading content',
|
||||
page: 'Loading page',
|
||||
processing: 'Processing'
|
||||
},
|
||||
// Dates
|
||||
dates: {
|
||||
months: {
|
||||
long: [
|
||||
'January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'
|
||||
],
|
||||
short: [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
]
|
||||
},
|
||||
weekdays: {
|
||||
long: [
|
||||
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
|
||||
'Thursday', 'Friday', 'Saturday'
|
||||
],
|
||||
short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
}
|
||||
},
|
||||
// Social Media
|
||||
social: {
|
||||
followMe: 'Follow me on',
|
||||
shareOn: 'Share on',
|
||||
linkedin: 'LinkedIn',
|
||||
github: 'GitHub',
|
||||
email: 'Email'
|
||||
},
|
||||
// Contact Form
|
||||
contact: {
|
||||
title: 'Contact',
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
message: 'Message',
|
||||
submit: 'Send Message',
|
||||
success: 'Your message has been sent successfully',
|
||||
error: 'There was an error sending your message'
|
||||
},
|
||||
// Accessibility
|
||||
a11y: {
|
||||
skipToContent: 'Skip to main content',
|
||||
menuOpen: 'Open menu',
|
||||
menuClose: 'Close menu',
|
||||
darkMode: 'Toggle dark mode',
|
||||
carousel: {
|
||||
next: 'Next image',
|
||||
previous: 'Previous image'
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
export type CommonTranslations = typeof common;
|
||||
@@ -0,0 +1,9 @@
|
||||
const cookiebanner = {
|
||||
cookies: {
|
||||
message: "This website uses cookies to enhance your browsing experience. The data will not be shared with third parties.",
|
||||
accept: "Accept Cookies",
|
||||
decline: "Decline"
|
||||
}
|
||||
};
|
||||
|
||||
export default cookiebanner;
|
||||
@@ -0,0 +1,40 @@
|
||||
// src/i18n/locales/en/footer.ts
|
||||
const footer = {
|
||||
sections: {
|
||||
contact: {
|
||||
title: "Contact",
|
||||
email: "info@damjan-savic.com",
|
||||
location: "Cologne, Germany",
|
||||
aria: {
|
||||
emailLink: "Send email",
|
||||
locationText: "Location"
|
||||
}
|
||||
},
|
||||
navigation: {
|
||||
title: "Navigation",
|
||||
links: {
|
||||
portfolio: "Portfolio",
|
||||
blog: "Blog",
|
||||
about: "About",
|
||||
contact: "Contact"
|
||||
}
|
||||
},
|
||||
social: {
|
||||
title: "Social Media",
|
||||
aria: {
|
||||
linkedin: "Visit my LinkedIn profile",
|
||||
github: "Visit my GitHub profile"
|
||||
}
|
||||
}
|
||||
},
|
||||
legal: {
|
||||
copyright: "All rights reserved",
|
||||
links: {
|
||||
privacy: "Privacy Policy",
|
||||
terms: "Legal Notice"
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
|
||||
export default footer;
|
||||
export type FooterTranslations = typeof footer;
|
||||
@@ -0,0 +1,109 @@
|
||||
// src/i18n/locales/en/index.ts
|
||||
|
||||
// Blog post translations
|
||||
import { erpIntegrationBreuninger } from './blog/posts/erp-integration-breuninger';
|
||||
import { fullstackDevelopmentTimetracking } from './blog/posts/fullstack-development-timetracking';
|
||||
import { rfidAutomation as blogRfidAutomation } from './blog/posts/rfid-automation';
|
||||
import { blog } from './blog';
|
||||
|
||||
// Page translations
|
||||
import { about } from './pages/about/index';
|
||||
import { contact } from './pages/contact/index';
|
||||
import { dashboard } from './pages/dashboard';
|
||||
import { home } from './pages/home/index';
|
||||
import { login } from './pages/login';
|
||||
import { notfound } from './pages/notfound';
|
||||
import { portfolio as portfolioPage } from './pages/portfolio';
|
||||
import { privacy } from './pages/privacy';
|
||||
import { terms } from './pages/terms';
|
||||
|
||||
// Portfolio project translations
|
||||
import { aiDataReader } from './portfolio/projects/ai-data-reader';
|
||||
import { portfolio } from './portfolio';
|
||||
|
||||
// Common translations and configurations
|
||||
import { common } from './common';
|
||||
import { meta } from './meta';
|
||||
import { navigation } from './navigation';
|
||||
|
||||
// Footer translations
|
||||
import footer from './footer';
|
||||
|
||||
// Merging all translation objects
|
||||
const translations = {
|
||||
// Pages
|
||||
pages: {
|
||||
about,
|
||||
contact,
|
||||
dashboard,
|
||||
home,
|
||||
login,
|
||||
notfound,
|
||||
portfolio: portfolioPage,
|
||||
privacy,
|
||||
terms,
|
||||
},
|
||||
// Blog
|
||||
blog: {
|
||||
...blog,
|
||||
posts: {
|
||||
erpIntegrationBreuninger,
|
||||
fullstackDevelopmentTimetracking,
|
||||
rfidAutomation: blogRfidAutomation,
|
||||
},
|
||||
},
|
||||
// Portfolio
|
||||
portfolio: {
|
||||
...portfolio,
|
||||
projects: {
|
||||
aiDataReader,
|
||||
},
|
||||
},
|
||||
// Footer
|
||||
footer,
|
||||
// Common configurations
|
||||
common: {
|
||||
...common,
|
||||
cookies: {
|
||||
message: "This website uses cookies to enhance your browsing experience and provide personalized content. The collected data will not be shared with third parties.",
|
||||
accept: "Accept Cookies",
|
||||
decline: "Decline"
|
||||
}
|
||||
},
|
||||
meta,
|
||||
navigation,
|
||||
} as const;
|
||||
|
||||
// Default export: complete translations object
|
||||
export default translations;
|
||||
|
||||
// Type definition for translations
|
||||
export type Translations = typeof translations;
|
||||
|
||||
// Re-exports for direct access to individual modules
|
||||
export {
|
||||
// Pages
|
||||
about,
|
||||
contact,
|
||||
dashboard,
|
||||
home,
|
||||
login,
|
||||
notfound,
|
||||
portfolioPage as portfolioPageTranslations,
|
||||
privacy,
|
||||
terms,
|
||||
// Blog
|
||||
blog,
|
||||
erpIntegrationBreuninger,
|
||||
fullstackDevelopmentTimetracking,
|
||||
blogRfidAutomation as blogRfidAutomationPost,
|
||||
// Portfolio
|
||||
portfolio,
|
||||
aiDataReader,
|
||||
// Footer
|
||||
footer,
|
||||
// Common
|
||||
common,
|
||||
meta,
|
||||
navigation,
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
export const meta = {
|
||||
site: {
|
||||
name: 'Damjan Savić',
|
||||
title: 'Damjan Savić - Digital Solutions & JTL Integration',
|
||||
subtitle: 'Developer of custom solutions for process automation',
|
||||
description: 'With a background in software development and expertise in digital transformation, I help companies optimize and automate their business processes.',
|
||||
keywords: [
|
||||
'JTL Integration',
|
||||
'E-Commerce Solutions',
|
||||
'Process Automation',
|
||||
'Digital Consulting',
|
||||
'ERP Systems',
|
||||
'Python Development',
|
||||
'System Integration',
|
||||
'Digital Marketing'
|
||||
].join(', ')
|
||||
},
|
||||
company: {
|
||||
name: 'Damjan Savić',
|
||||
shortName: 'Damjan Savić',
|
||||
description: 'Your software development partner for fast, reliable, and cost-efficient solutions.',
|
||||
address: 'Rotdornallee',
|
||||
city: 'Köln',
|
||||
postalCode: '50999',
|
||||
country: 'Germany',
|
||||
phone: '+49 175 695 0979',
|
||||
email: 'info@damjan-savic.com',
|
||||
website: 'www.damjan-savic.com'
|
||||
},
|
||||
author: {
|
||||
name: 'Damjan Savić',
|
||||
role: 'Consultant Digital Solutions',
|
||||
company: 'Ritter Digital GmbH',
|
||||
location: 'Oberhausen, Germany',
|
||||
email: 'info@damjan-savic.com',
|
||||
website: 'www.damjan-savic.com',
|
||||
languages: [
|
||||
{ code: 'en', level: 'C2', name: 'English' },
|
||||
{ code: 'sr', level: 'C2', name: 'Serbian' },
|
||||
{ code: 'de', level: 'C2', name: 'German' },
|
||||
{ code: 'fr', level: 'B2', name: 'French' },
|
||||
{ code: 'es', level: 'B2', name: 'Spanish' },
|
||||
{ code: 'ru', level: 'A1', name: 'Russian' }
|
||||
]
|
||||
},
|
||||
social: {
|
||||
linkedin: 'https://www.linkedin.com/in/damjansavic/',
|
||||
github: 'https://github.com/damjansavic',
|
||||
email: 'info@damjan-savic.com'
|
||||
},
|
||||
expertise: {
|
||||
areas: [
|
||||
'JTL Integration & Consulting',
|
||||
'Process Automation',
|
||||
'E-Commerce Development',
|
||||
'System Integration',
|
||||
'ERP Systems',
|
||||
'Digital Transformation'
|
||||
],
|
||||
technologies: [
|
||||
'Python',
|
||||
'React/Next.js',
|
||||
'MariaDB',
|
||||
'Docker',
|
||||
'JTL-WaWi',
|
||||
'Shopify',
|
||||
'Shopware'
|
||||
]
|
||||
},
|
||||
seo: {
|
||||
defaultLanguage: 'en',
|
||||
alternateLanguages: ['de', 'sr'],
|
||||
robots: 'index, follow',
|
||||
googleSiteVerification: '', // If needed
|
||||
og: {
|
||||
type: 'website',
|
||||
locale: 'en_US',
|
||||
siteName: 'Damjan Savić - Digital Solutions',
|
||||
images: {
|
||||
url: '/images/og-image.jpg',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'Damjan Savić - Digital Solutions & JTL Integration'
|
||||
}
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
site: '@damjansavic',
|
||||
creator: '@damjansavic'
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
export type MetaConfig = typeof meta;
|
||||
@@ -0,0 +1,79 @@
|
||||
// C:\Development\Damjan Savic\Portfolio\src\i18n\locales\en\navigation.ts
|
||||
export const navigation = {
|
||||
// Main navigation
|
||||
main: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
portfolio: 'Portfolio',
|
||||
blog: 'Blog',
|
||||
contact: 'Contact'
|
||||
},
|
||||
// Dashboard and Admin
|
||||
admin: {
|
||||
login: 'Login',
|
||||
dashboard: 'Dashboard',
|
||||
logout: 'Logout'
|
||||
},
|
||||
// Footer Navigation
|
||||
footer: {
|
||||
legal: {
|
||||
privacy: 'Privacy Policy',
|
||||
terms: 'Terms & Conditions',
|
||||
imprint: 'Imprint'
|
||||
},
|
||||
sections: {
|
||||
portfolio: 'Portfolio',
|
||||
blog: 'Blog',
|
||||
contact: 'Contact'
|
||||
}
|
||||
},
|
||||
// Page sections
|
||||
sections: {
|
||||
experience: 'Experience',
|
||||
skills: 'Skills',
|
||||
projects: 'Projects',
|
||||
about: 'About'
|
||||
},
|
||||
// Breadcrumbs and back navigation
|
||||
navigation: {
|
||||
backTo: {
|
||||
home: 'Back to Home',
|
||||
portfolio: 'Back to Portfolio',
|
||||
blog: 'Back to Blog'
|
||||
},
|
||||
breadcrumbs: {
|
||||
home: 'Home',
|
||||
portfolio: 'Portfolio',
|
||||
blog: 'Blog',
|
||||
about: 'About',
|
||||
contact: 'Contact'
|
||||
}
|
||||
},
|
||||
// Mobile Navigation
|
||||
mobile: {
|
||||
menu: 'Menu',
|
||||
close: 'Close',
|
||||
open: 'Open Menu'
|
||||
},
|
||||
// URLs (for dynamic route generation)
|
||||
urls: {
|
||||
home: '/',
|
||||
about: '/about',
|
||||
portfolio: '/portfolio',
|
||||
blog: '/blog',
|
||||
contact: '/contact',
|
||||
login: '/login',
|
||||
dashboard: '/dashboard',
|
||||
privacy: '/privacy',
|
||||
terms: '/terms'
|
||||
},
|
||||
// Meta information for navigation
|
||||
meta: {
|
||||
main: 'Main Navigation',
|
||||
footer: 'Footer Navigation',
|
||||
social: 'Social Media Navigation',
|
||||
legal: 'Legal Navigation'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type NavigationConfig = typeof navigation;
|
||||
@@ -0,0 +1,33 @@
|
||||
export const hero = {
|
||||
// Navigation/Header
|
||||
aboutMe: 'ABOUT ME',
|
||||
// Main Section
|
||||
title: 'Digital Solutions Expert',
|
||||
description: 'With a background in software development and expertise in digital transformation, I help companies optimize and automate their processes through innovative solutions.',
|
||||
// Expertise Areas
|
||||
expertise: {
|
||||
processAutomation: {
|
||||
title: 'Process Automation',
|
||||
description: 'Development of efficient automated workflows'
|
||||
},
|
||||
systemIntegration: {
|
||||
title: 'System Integration',
|
||||
description: 'Connection and optimization of business systems'
|
||||
},
|
||||
customDevelopment: {
|
||||
title: 'Custom Development',
|
||||
description: 'Creation of tailored software solutions'
|
||||
}
|
||||
},
|
||||
// Languages
|
||||
languages: {
|
||||
english: 'English',
|
||||
serbian: 'Serbian',
|
||||
german: 'German',
|
||||
french: 'French',
|
||||
spanish: 'Spanish',
|
||||
russian: 'Russian'
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type HeroTranslations = typeof hero;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { hero } from './hero';
|
||||
import { journey } from './journey';
|
||||
import { schema } from './schema';
|
||||
import { skillbar } from './skillbar';
|
||||
import { skillgroup } from './skillgroup';
|
||||
import { skills } from './skills';
|
||||
import { workflow } from './workflow';
|
||||
export const about = {
|
||||
hero,
|
||||
journey,
|
||||
schema,
|
||||
skillbar,
|
||||
skillgroup,
|
||||
skills,
|
||||
workflow
|
||||
} as const;
|
||||
// Typen exportieren
|
||||
export type { HeroTranslations } from './hero';
|
||||
export type { JourneyTranslations } from './journey';
|
||||
export type { SchemaTranslations } from './schema';
|
||||
export type { SkillBarTranslations } from './skillbar';
|
||||
export type { SkillGroupTranslations } from './skillgroup';
|
||||
export type { SkillsTranslations } from './skills';
|
||||
export type { WorkflowTranslations } from './workflow';
|
||||
@@ -0,0 +1,95 @@
|
||||
export const journey = {
|
||||
// Header
|
||||
title: 'Professional Journey',
|
||||
subtitle: 'From digital media to enterprise solutions',
|
||||
// Education Section
|
||||
education: {
|
||||
title: 'Education',
|
||||
degrees: {
|
||||
masters: {
|
||||
degree: 'M.A. Software Development',
|
||||
university: 'Middlesex University, London',
|
||||
period: '2020 - 2022'
|
||||
},
|
||||
bachelors: {
|
||||
degree: 'B.A. Cross-Media Production',
|
||||
university: 'Middlesex University, London',
|
||||
period: '2018 - 2020'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Languages Section
|
||||
languages: {
|
||||
title: 'Languages',
|
||||
items: {
|
||||
english: {
|
||||
name: 'English',
|
||||
level: 'C2'
|
||||
},
|
||||
serbian: {
|
||||
name: 'Serbian',
|
||||
level: 'C2'
|
||||
},
|
||||
german: {
|
||||
name: 'German',
|
||||
level: 'C2'
|
||||
},
|
||||
french: {
|
||||
name: 'French',
|
||||
level: 'B2'
|
||||
},
|
||||
spanish: {
|
||||
name: 'Spanish',
|
||||
level: 'B2'
|
||||
},
|
||||
russian: {
|
||||
name: 'Russian',
|
||||
level: 'A1'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Work History
|
||||
work: {
|
||||
positions: [
|
||||
{
|
||||
period: '08/2023 - Present',
|
||||
company: 'Ritter Digital GmbH',
|
||||
title: 'Digital Solutions Consultant',
|
||||
location: 'Oberhausen',
|
||||
highlights: [
|
||||
'ERP consulting and implementation (JTL WaWi)',
|
||||
'Process automation & full-stack development',
|
||||
'RFID technology & automation',
|
||||
'Lead generation & management',
|
||||
'AI-supported content management'
|
||||
]
|
||||
},
|
||||
{
|
||||
period: '02/2023 - 08/2023',
|
||||
company: 'Joyce & Girls Co AG',
|
||||
title: 'ERP Integration Specialist',
|
||||
location: 'Köln',
|
||||
highlights: [
|
||||
'Complex system integration (Apparel Magic, TradeByte)',
|
||||
'Server administration and automation',
|
||||
'Database and API development',
|
||||
'Digital marketing and campaign analysis'
|
||||
]
|
||||
},
|
||||
{
|
||||
period: '08/2022 - 01/2023',
|
||||
company: 'Magnarius',
|
||||
title: 'RPA Developer & E-Commerce Manager',
|
||||
location: 'Bergisch Gladbach',
|
||||
highlights: [
|
||||
'Shopware 6 development and JTL integration',
|
||||
'Google Ads automation',
|
||||
'CMS management and optimization',
|
||||
'SEO strategy implementation'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type JourneyTranslations = typeof journey;
|
||||
@@ -0,0 +1,31 @@
|
||||
export const schema = {
|
||||
page: {
|
||||
name: 'About Damjan Savić',
|
||||
description: 'Learn more about Damjan Savić, a certified JTL expert with extensive experience in e-commerce and warehouse management solutions.'
|
||||
},
|
||||
person: {
|
||||
jobTitle: 'JTL Integration Expert',
|
||||
description: 'With extensive experience in integrating and optimizing e-commerce systems, I help companies with the digital transformation of their business processes.',
|
||||
languages: {
|
||||
english: 'English',
|
||||
serbian: 'Serbian',
|
||||
german: 'German',
|
||||
french: 'French',
|
||||
spanish: 'Spanish',
|
||||
russian: 'Russian'
|
||||
},
|
||||
skills: {
|
||||
python: 'Python',
|
||||
googleAds: 'Google Ads',
|
||||
metaAds: 'Meta Ads',
|
||||
shopware: 'Shopware',
|
||||
shopify: 'Shopify',
|
||||
wooCommerce: 'WooCommerce',
|
||||
jtlWawi: 'JTL WAWI',
|
||||
serverAdmin: 'Server Administration',
|
||||
airflow: 'AirFlow'
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type SchemaTranslations = typeof schema;
|
||||
@@ -0,0 +1,13 @@
|
||||
export const skillbar = {
|
||||
// Accessibility labels
|
||||
ariaLabel: {
|
||||
skillLevel: 'Skill Level',
|
||||
progressBar: 'Progress Bar'
|
||||
},
|
||||
// Screen reader text
|
||||
screenReader: {
|
||||
progress: '{{level}} percent progress for {{skill}}'
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type SkillBarTranslations = typeof skillbar;
|
||||
@@ -0,0 +1,63 @@
|
||||
export const skillgroup = {
|
||||
// Categories
|
||||
categories: {
|
||||
development: 'Development',
|
||||
ecommerce: 'E-Commerce',
|
||||
marketing: 'Marketing',
|
||||
automation: 'Automation',
|
||||
languages: 'Programming Languages',
|
||||
databases: 'Databases'
|
||||
},
|
||||
// Skills by category
|
||||
skills: {
|
||||
development: {
|
||||
frontend: 'Frontend Development',
|
||||
backend: 'Backend Development',
|
||||
fullstack: 'Full-Stack Development',
|
||||
api: 'API Development',
|
||||
testing: 'Software Testing'
|
||||
},
|
||||
ecommerce: {
|
||||
jtl: 'JTL WaWi',
|
||||
shopware: 'Shopware',
|
||||
shopify: 'Shopify',
|
||||
woocommerce: 'WooCommerce',
|
||||
magento: 'Magento'
|
||||
},
|
||||
marketing: {
|
||||
seo: 'Search Engine Optimization',
|
||||
sea: 'Search Engine Advertising',
|
||||
analytics: 'Web Analytics',
|
||||
googleAds: 'Google Ads',
|
||||
metaAds: 'Meta Ads'
|
||||
},
|
||||
automation: {
|
||||
rpa: 'Robotic Process Automation',
|
||||
workflow: 'Workflow Automation',
|
||||
cicd: 'CI/CD',
|
||||
testing: 'Test Automation',
|
||||
deployment: 'Deployment Automation'
|
||||
},
|
||||
languages: {
|
||||
python: 'Python',
|
||||
javascript: 'JavaScript',
|
||||
typescript: 'TypeScript',
|
||||
php: 'PHP',
|
||||
sql: 'SQL'
|
||||
},
|
||||
databases: {
|
||||
mysql: 'MySQL',
|
||||
postgresql: 'PostgreSQL',
|
||||
mongodb: 'MongoDB',
|
||||
redis: 'Redis',
|
||||
elasticsearch: 'Elasticsearch'
|
||||
}
|
||||
},
|
||||
// Accessibility
|
||||
ariaLabels: {
|
||||
skillGroup: 'Skill Group',
|
||||
skillList: 'List of skills in this category'
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type SkillGroupTranslations = typeof skillgroup;
|
||||
@@ -0,0 +1,46 @@
|
||||
// en/pages/about/skills.ts
|
||||
export const skills = {
|
||||
title: 'Skills',
|
||||
skillGroups: [
|
||||
{
|
||||
category: 'Development',
|
||||
items: [
|
||||
{ name: 'Python', level: 90 },
|
||||
{ name: 'Google Ads', level: 85 },
|
||||
{ name: 'Meta Ads', level: 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'E-Commerce',
|
||||
items: [
|
||||
{ name: 'Shopware', level: 85 },
|
||||
{ name: 'Shopify', level: 85 },
|
||||
{ name: 'WooCommerce', level: 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'System Administration',
|
||||
items: [
|
||||
{ name: 'JTL WAWI', level: 85 },
|
||||
{ name: 'Server', level: 75 },
|
||||
{ name: 'AirFlow', level: 60 }
|
||||
]
|
||||
}
|
||||
],
|
||||
ui: {
|
||||
percentage: 'percent',
|
||||
levelLabel: 'Level',
|
||||
skillLevel: '{{level}}% proficiency in {{skill}}'
|
||||
},
|
||||
aria: {
|
||||
skillGroup: 'Skill group for {{category}}',
|
||||
skillBar: 'Progress bar for {{skill}}',
|
||||
skillIcon: 'Icon for {{category}}',
|
||||
selectSkill: 'Select skill'
|
||||
},
|
||||
screenReader: {
|
||||
skillProgress: '{{skill}} with a level of {{level}} percent'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type SkillsTranslations = typeof skills;
|
||||
@@ -0,0 +1,44 @@
|
||||
export const workflow = {
|
||||
// Page title
|
||||
title: 'MY WORKFLOW',
|
||||
description: 'Systematic approach to software development through defined steps',
|
||||
// Workflow steps
|
||||
steps: [
|
||||
{
|
||||
number: '01',
|
||||
title: 'REQUIREMENTS ANALYSIS'
|
||||
},
|
||||
{
|
||||
number: '02',
|
||||
title: 'ASSESSMENT OF DEADLINES AND CONSTRAINTS'
|
||||
},
|
||||
{
|
||||
number: '03',
|
||||
title: 'DEVELOPMENT ENVIRONMENT SETUP'
|
||||
},
|
||||
{
|
||||
number: '04',
|
||||
title: 'PROGRAMMING'
|
||||
},
|
||||
{
|
||||
number: '05',
|
||||
title: 'TESTING THE RESULTS'
|
||||
},
|
||||
{
|
||||
number: '06',
|
||||
title: 'CODE SUPPORT AND SCALING'
|
||||
}
|
||||
],
|
||||
// Aria Labels for accessibility
|
||||
aria: {
|
||||
workflowSection: 'Workflow Process',
|
||||
stepNumber: 'Step {{number}}',
|
||||
stepDescription: 'Workflow step: {{title}}'
|
||||
},
|
||||
// Screen Reader texts
|
||||
screenReader: {
|
||||
stepProgress: 'Step {{number}} of {{total}}: {{title}}'
|
||||
}
|
||||
} as const;
|
||||
// Type for type safety
|
||||
export type WorkflowTranslations = typeof workflow;
|
||||
@@ -0,0 +1,41 @@
|
||||
// src/i18n/locales/en/pages/contact/contactform.ts
|
||||
export const contactform = {
|
||||
// Form Header
|
||||
title: 'Contact Me',
|
||||
description: 'Have questions or want to collaborate? Send me a message.',
|
||||
// Form Fields
|
||||
name: {
|
||||
label: 'Name',
|
||||
placeholder: 'Your full name'
|
||||
},
|
||||
email: {
|
||||
label: 'Email',
|
||||
placeholder: 'your.email@example.com'
|
||||
},
|
||||
message: {
|
||||
label: 'Message',
|
||||
placeholder: 'Your message to me...'
|
||||
},
|
||||
// Button
|
||||
submit: 'Send Message',
|
||||
// Status Messages
|
||||
successMessage: 'Thank you for your message! I will get back to you as soon as possible.',
|
||||
errorMessage: 'Sorry, there was an error sending your message. Please try again later.',
|
||||
// Error Messages
|
||||
errors: {
|
||||
nameRequired: 'Please enter your name',
|
||||
emailRequired: 'Please enter your email address',
|
||||
emailInvalid: 'Please enter a valid email address',
|
||||
messageRequired: 'Please enter a message'
|
||||
},
|
||||
// Aria Labels
|
||||
aria: {
|
||||
form: 'Contact form',
|
||||
submitting: 'Sending form',
|
||||
successAlert: 'Success message',
|
||||
errorAlert: 'Error message'
|
||||
}
|
||||
} as const;
|
||||
|
||||
// Type for type safety
|
||||
export type ContactFormTranslations = typeof contactform;
|
||||
@@ -0,0 +1,27 @@
|
||||
// src/i18n/locales/en/pages/contact/contactinfo.ts
|
||||
export const contactinfo = {
|
||||
// Header
|
||||
title: 'Contact Information',
|
||||
description: 'You can reach me through the following channels',
|
||||
// Contact Method Labels
|
||||
addressLabel: 'Office Location',
|
||||
phoneLabel: 'Phone',
|
||||
emailLabel: 'Email',
|
||||
// Contact Information
|
||||
address: 'Rotdornallee, Köln',
|
||||
phone: '+49 175 695 0979',
|
||||
email: 'info@damjan-savic.com',
|
||||
// Additional Information
|
||||
availabilityNote: 'Business hours: Monday to Friday, 9:00 AM - 5:00 PM',
|
||||
// Aria Labels
|
||||
aria: {
|
||||
contactCard: 'Contact information card',
|
||||
addressLink: 'Open location in Google Maps',
|
||||
phoneLink: 'Call',
|
||||
emailLink: 'Send email',
|
||||
externalLink: 'Opens in new tab'
|
||||
}
|
||||
} as const;
|
||||
|
||||
// Type for type safety
|
||||
export type ContactInfoTranslations = typeof contactinfo;
|
||||
@@ -0,0 +1,71 @@
|
||||
// src/i18n/locales/en/pages/contact/index.ts
|
||||
export const contact = {
|
||||
breadcrumb: "CONTACT",
|
||||
hero: {
|
||||
title: "Let's Work Together",
|
||||
subtitle: "Have a project in mind? Let's discuss how we can help you achieve your goals."
|
||||
},
|
||||
contactInfo: {
|
||||
title: "Contact Information",
|
||||
description: "You can reach me through the following channels",
|
||||
address: {
|
||||
label: "Office Location",
|
||||
value: "Rotdornallee, Köln"
|
||||
},
|
||||
phone: {
|
||||
label: "Phone",
|
||||
value: "+49 175 695 0979"
|
||||
},
|
||||
email: {
|
||||
label: "Email",
|
||||
value: "info@damjan-savic.com"
|
||||
},
|
||||
availabilityNote: "Business hours: Monday to Friday, 9:00 AM - 5:00 PM",
|
||||
aria: {
|
||||
contactCard: "Contact information card",
|
||||
addressLink: "Open location in Google Maps",
|
||||
phoneLink: "Call",
|
||||
emailLink: "Send email",
|
||||
externalLink: "Opens in new tab"
|
||||
}
|
||||
},
|
||||
contactForm: {
|
||||
title: "Contact Me",
|
||||
description: "Have questions or want to collaborate? Send me a message.",
|
||||
name: {
|
||||
label: "Name",
|
||||
placeholder: "Your full name"
|
||||
},
|
||||
email: {
|
||||
label: "Email",
|
||||
placeholder: "your.email@example.com"
|
||||
},
|
||||
message: {
|
||||
label: "Message",
|
||||
placeholder: "Your message to me..."
|
||||
},
|
||||
submit: "Send Message",
|
||||
successMessage: "Thank you for your message! I will get back to you as soon as possible.",
|
||||
errorMessage: "Sorry, there was an error sending your message. Please try again later.",
|
||||
errors: {
|
||||
nameRequired: "Please enter your name",
|
||||
emailRequired: "Please enter your email address",
|
||||
emailInvalid: "Please enter a valid email address",
|
||||
messageRequired: "Please enter a message"
|
||||
},
|
||||
aria: {
|
||||
form: "Contact form",
|
||||
submitting: "Sending form",
|
||||
successAlert: "Success message",
|
||||
errorAlert: "Error message"
|
||||
}
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type ContactTranslations = typeof contact;
|
||||
|
||||
export default {
|
||||
pages: {
|
||||
contact: contact
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
// src/i18n/locales/en/pages/dashboard.ts
|
||||
export const dashboard = {
|
||||
// SEO
|
||||
seo: {
|
||||
title: 'Analytics Dashboard',
|
||||
description: 'Website Analysis Dashboard'
|
||||
},
|
||||
// Main headings
|
||||
header: {
|
||||
title: 'Analytics Dashboard',
|
||||
activeUsers: 'active users'
|
||||
},
|
||||
// Metric cards
|
||||
metrics: {
|
||||
pageViews: {
|
||||
title: 'Page Views',
|
||||
perMinute: 'per minute'
|
||||
},
|
||||
uniqueVisitors: {
|
||||
title: 'Unique Visitors',
|
||||
currentlyActive: 'currently active'
|
||||
},
|
||||
conversions: {
|
||||
title: 'Conversions',
|
||||
rate: 'conversion rate'
|
||||
},
|
||||
timeOnSite: {
|
||||
title: 'Avg. Time on Site',
|
||||
bounceRate: 'bounce rate'
|
||||
}
|
||||
},
|
||||
// Top pages section
|
||||
topPages: {
|
||||
title: 'Top Pages',
|
||||
columns: {
|
||||
path: 'Path',
|
||||
views: 'Views',
|
||||
change: 'Change'
|
||||
}
|
||||
},
|
||||
// Event tracking section
|
||||
events: {
|
||||
title: 'Event Tracking',
|
||||
types: {
|
||||
contactForm: 'Contact Form Submission',
|
||||
portfolioView: 'Portfolio View',
|
||||
cvDownload: 'CV Download',
|
||||
blogRead: 'Blog Read'
|
||||
}
|
||||
},
|
||||
// Status messages
|
||||
status: {
|
||||
loading: 'Loading Dashboard...',
|
||||
error: 'Error loading data',
|
||||
notAuthenticated: 'Please sign in'
|
||||
},
|
||||
// Miscellaneous
|
||||
misc: {
|
||||
viewMore: 'View More',
|
||||
refresh: 'Refresh',
|
||||
period: {
|
||||
today: 'Today',
|
||||
yesterday: 'Yesterday',
|
||||
last7Days: 'Last 7 Days',
|
||||
last30Days: 'Last 30 Days',
|
||||
thisMonth: 'This Month',
|
||||
lastMonth: 'Last Month'
|
||||
}
|
||||
},
|
||||
// Trends and changes
|
||||
trends: {
|
||||
increase: 'Increase',
|
||||
decrease: 'Decrease',
|
||||
noChange: 'No Change'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// src/i18n/locales/en/pages/home/about.ts
|
||||
export const about = {
|
||||
title: 'About Me',
|
||||
content: 'As a digital transformation consultant, I help companies improve their processes through modern software. I develop solutions for connecting inventory management systems, programs for workflow automation, and RFID applications for better product tracking. At Ritter Digital GmbH, I work with JTL software and develop custom program solutions. Another project involved connecting Breuninger\'s systems with Apparel Magic via TB.One. My goal is to make companies more efficient through software.',
|
||||
image: {
|
||||
alt: 'Portrait of Damjan Savić'
|
||||
}
|
||||
} as const;
|
||||
export type AboutTranslations = typeof about;
|
||||
@@ -0,0 +1,77 @@
|
||||
// src/i18n/locales/en/pages/home/experience.ts
|
||||
export const experience = {
|
||||
title: 'Professional Experience',
|
||||
navigation: {
|
||||
prev: '←',
|
||||
next: '→'
|
||||
},
|
||||
positions: [
|
||||
{
|
||||
role: 'Consultant Digital Solutions & Developer',
|
||||
company: 'RITTER Gesellschaft für digitale Geschäftsprozesse mbH',
|
||||
period: '08/2023 - PRESENT',
|
||||
highlights: [
|
||||
'JTL WaWi, WMS and POS consulting & implementation',
|
||||
'Python-based automation solutions',
|
||||
'RFID systems and GUI development',
|
||||
'AI & Business Intelligence'
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'ERP Integration Specialist & E-Commerce Developer',
|
||||
company: 'Joyce & Girls',
|
||||
period: '01/2023 - 08/2023',
|
||||
highlights: [
|
||||
'ApparelMagic & TradeByte integration',
|
||||
'MariaDB middleware development',
|
||||
'Windows Server administration',
|
||||
'Shopify optimization'
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'RPA Developer & E-Commerce Manager',
|
||||
company: 'C&S Marketing',
|
||||
period: '08/2022 - 01/2023',
|
||||
highlights: [
|
||||
'Shopware 6 & JTL integration',
|
||||
'Python automation',
|
||||
'Google Ads optimization',
|
||||
'CMS management'
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'E-Commerce Developer',
|
||||
company: 'Brands Club GmbH',
|
||||
period: '08/2021 - 07/2022',
|
||||
highlights: [
|
||||
'Shopify & JTL integration',
|
||||
'Server administration',
|
||||
'Content production',
|
||||
'Marketing automation'
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'E-Commerce Developer',
|
||||
company: 'Feine Uhren Eupen',
|
||||
period: '01/2021 - 08/2021',
|
||||
highlights: [
|
||||
'Shopware 5 development',
|
||||
'Product photography',
|
||||
'Luxury watch segment sales',
|
||||
'E-Commerce management'
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'Online Marketing Manager',
|
||||
company: 'Ufer8, Ambis, Teatro & Die Halle Tor 2',
|
||||
period: '01/2018 - 12/2020',
|
||||
highlights: [
|
||||
'Social media content',
|
||||
'Event marketing',
|
||||
'Event organization',
|
||||
'Marketing automation'
|
||||
]
|
||||
}
|
||||
]
|
||||
} as const;
|
||||
export type ExperienceTranslations = typeof experience;
|
||||
@@ -0,0 +1,27 @@
|
||||
// src/i18n/locales/en/pages/home/expertise.ts
|
||||
export const expertise = {
|
||||
title: 'Areas of Expertise',
|
||||
areas: [
|
||||
{
|
||||
title: 'ERP Integration',
|
||||
description: 'Expert in JTL-Wawi implementation, configuration, and optimization. Specialized in inventory management systems and POS integration.',
|
||||
skills: ['JTL-Wawi', 'JTL-WMS', 'JTL-POS', 'Apparel Magic']
|
||||
},
|
||||
{
|
||||
title: 'Development',
|
||||
description: 'Full-stack development with focus on Python automation, React frontends, and database design.',
|
||||
skills: ['Python', 'React/Next.js', 'MariaDB', 'Docker']
|
||||
},
|
||||
{
|
||||
title: 'E-Commerce',
|
||||
description: 'Extensive experience with various e-commerce platforms and marketplace integrations.',
|
||||
skills: ['Shopify', 'Shopware', 'WooCommerce', 'API Integration']
|
||||
},
|
||||
{
|
||||
title: 'Digital Marketing',
|
||||
description: 'Strategic approach to digital marketing with focus on automation and analytics.',
|
||||
skills: ['Google Ads', 'Meta Ads', 'SEO', 'Analytics']
|
||||
}
|
||||
]
|
||||
} as const;
|
||||
export type ExpertiseTranslations = typeof expertise;
|
||||
@@ -0,0 +1,28 @@
|
||||
// src/i18n/locales/en/pages/home/hero.ts
|
||||
export const hero = {
|
||||
title: 'SOFTWARE ENGINEER',
|
||||
name: 'DAMJAN SAVIĆ',
|
||||
cta: 'GET IN TOUCH',
|
||||
image: {
|
||||
alt: 'Portrait of Damjan Savić'
|
||||
},
|
||||
social: {
|
||||
getInTouch: 'Get in touch',
|
||||
linkedin: {
|
||||
title: 'Visit my LinkedIn profile'
|
||||
},
|
||||
github: {
|
||||
title: 'Visit my GitHub profile'
|
||||
},
|
||||
email: {
|
||||
title: 'Send me an email'
|
||||
}
|
||||
},
|
||||
navigation: {
|
||||
experience: 'EXPERIENCE',
|
||||
skills: 'SKILLS',
|
||||
projects: 'PROJECTS',
|
||||
about: 'ABOUT'
|
||||
}
|
||||
} as const;
|
||||
export type HeroTranslations = typeof hero;
|
||||
@@ -0,0 +1,25 @@
|
||||
// src/i18n/locales/de/pages/home/index.ts
|
||||
import { hero } from './hero';
|
||||
import { about } from './about';
|
||||
import { experience } from './experience';
|
||||
import { expertise } from './expertise';
|
||||
import { skills } from './skills';
|
||||
import { projects } from './projects';
|
||||
export const home = {
|
||||
hero,
|
||||
about,
|
||||
experience,
|
||||
expertise,
|
||||
skills,
|
||||
projects
|
||||
} as const;
|
||||
export type HomeTranslations = typeof home;
|
||||
// Re-export individual sections for direct access
|
||||
export {
|
||||
hero,
|
||||
about,
|
||||
experience,
|
||||
expertise,
|
||||
skills,
|
||||
projects
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
// src/i18n/locales/en/pages/home/projects.ts
|
||||
export const projects = {
|
||||
title: 'Portfolio',
|
||||
viewAll: 'All Projects →',
|
||||
loading: 'Loading projects...',
|
||||
error: {
|
||||
loading: 'Error loading projects'
|
||||
}
|
||||
} as const;
|
||||
export type ProjectsTranslations = typeof projects;
|
||||
@@ -0,0 +1,41 @@
|
||||
// src/i18n/locales/en/pages/home/skills.ts
|
||||
export const skills = {
|
||||
title: 'Skills',
|
||||
skills: [
|
||||
{
|
||||
name: 'ERP',
|
||||
level: 90,
|
||||
description: 'Enterprise Resource Planning'
|
||||
},
|
||||
{
|
||||
name: 'Database',
|
||||
level: 85,
|
||||
description: 'Database development and administration'
|
||||
},
|
||||
{
|
||||
name: 'E-Commerce',
|
||||
level: 95,
|
||||
description: 'Online commerce and platforms'
|
||||
},
|
||||
{
|
||||
name: 'Python',
|
||||
level: 80,
|
||||
description: 'Python programming language'
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
level: 75,
|
||||
description: 'Frontend development with React'
|
||||
},
|
||||
{
|
||||
name: 'Server',
|
||||
level: 70,
|
||||
description: 'Server administration'
|
||||
}
|
||||
],
|
||||
aria: {
|
||||
skillLevel: 'Skill level',
|
||||
selectSkill: 'Select skill'
|
||||
}
|
||||
} as const;
|
||||
export type SkillsTranslations = typeof skills;
|
||||
@@ -0,0 +1,54 @@
|
||||
// src/i18n/locales/en/pages/login.ts
|
||||
export const login = {
|
||||
// SEO
|
||||
seo: {
|
||||
title: 'Login',
|
||||
description: 'Sign in to access the dashboard'
|
||||
},
|
||||
// Main heading
|
||||
header: {
|
||||
title: 'Sign in to Dashboard'
|
||||
},
|
||||
// Form
|
||||
form: {
|
||||
// Fields
|
||||
email: {
|
||||
label: 'Email Address',
|
||||
placeholder: 'your.email@example.com'
|
||||
},
|
||||
password: {
|
||||
label: 'Password',
|
||||
placeholder: 'Your password'
|
||||
},
|
||||
// Buttons
|
||||
submit: {
|
||||
default: 'Sign In',
|
||||
loading: 'Signing in...'
|
||||
},
|
||||
// Error messages
|
||||
errors: {
|
||||
default: 'An error has occurred',
|
||||
invalidCredentials: 'Invalid credentials',
|
||||
emailRequired: 'Please enter your email address',
|
||||
passwordRequired: 'Please enter your password',
|
||||
emailInvalid: 'Please enter a valid email address'
|
||||
}
|
||||
},
|
||||
// Links and help
|
||||
help: {
|
||||
forgotPassword: 'Forgot Password?',
|
||||
needHelp: 'Need Help?',
|
||||
contactSupport: 'Contact Support'
|
||||
},
|
||||
// Status messages
|
||||
status: {
|
||||
authenticating: 'Authenticating...',
|
||||
success: 'Successfully signed in',
|
||||
redirecting: 'Redirecting to dashboard...'
|
||||
},
|
||||
// Security notices
|
||||
security: {
|
||||
secureConnection: 'Secure Connection',
|
||||
privacyNote: 'Your data is transmitted encrypted'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
// src/i18n/locales/en/pages/notfound.ts
|
||||
export const notfound = {
|
||||
// SEO
|
||||
seo: {
|
||||
title: '404 - Page Not Found',
|
||||
description: 'The requested page does not exist.'
|
||||
},
|
||||
// Main content
|
||||
content: {
|
||||
errorCode: '404',
|
||||
title: 'Page Not Found',
|
||||
description: 'The page you are looking for does not exist or has been moved.',
|
||||
},
|
||||
// Action buttons
|
||||
actions: {
|
||||
goBack: 'Go Back',
|
||||
homePage: 'Home Page'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
// src/i18n/locales/en/pages/portfolio.ts
|
||||
export const portfolio = {
|
||||
// SEO and meta information
|
||||
seo: {
|
||||
title: 'Portfolio - Damjan Savić',
|
||||
description: 'Discover my projects in JTL integration, e-commerce, and digital transformation.'
|
||||
},
|
||||
// Main page
|
||||
main: {
|
||||
title: 'Portfolio',
|
||||
loading: 'Loading projects...',
|
||||
error: 'Error loading projects',
|
||||
noProjects: {
|
||||
title: 'No Projects Found',
|
||||
description: 'Currently, no projects are available. Please check back later.'
|
||||
}
|
||||
},
|
||||
// Project cards
|
||||
projectCard: {
|
||||
date: 'Date',
|
||||
technologies: 'Technologies',
|
||||
moreCount: 'more', // for '+X more'
|
||||
readMore: 'Learn More'
|
||||
},
|
||||
// Project details
|
||||
project: {
|
||||
navigation: {
|
||||
back: 'Back to Portfolio',
|
||||
projectDetails: 'PROJECT DETAILS'
|
||||
},
|
||||
metadata: {
|
||||
date: 'Date',
|
||||
client: 'Client',
|
||||
duration: 'Duration',
|
||||
repository: 'Repository',
|
||||
documentation: 'Documentation',
|
||||
technologies: 'Technologies',
|
||||
live: 'Live Version'
|
||||
},
|
||||
sections: {
|
||||
overview: 'Overview',
|
||||
challenge: 'Challenge',
|
||||
solution: 'Solution',
|
||||
results: 'Results',
|
||||
systemArchitecture: 'System Architecture',
|
||||
implementation: 'Implementation',
|
||||
technologies: 'Technologies Used'
|
||||
}
|
||||
},
|
||||
// Categories and filters
|
||||
categories: {
|
||||
all: 'All',
|
||||
'Data Science': 'Data Science',
|
||||
'AI Development': 'AI Development',
|
||||
'Integration': 'Integration',
|
||||
'Full-Stack Development': 'Full-Stack Development',
|
||||
'Data Processing': 'Data Processing',
|
||||
'Content Production': 'Content Production',
|
||||
'Machine Learning': 'Machine Learning',
|
||||
'Automation': 'Automation'
|
||||
},
|
||||
// Specific projects
|
||||
projects: {
|
||||
'ai-data-reader': {
|
||||
title: 'AI-Based Product Data Management from PDF Files',
|
||||
description: 'Development of an automated system for extracting and structuring product data from PDF documents'
|
||||
},
|
||||
'bi-vision': {
|
||||
title: 'AI Videos for BI Software',
|
||||
description: 'Development of AI-powered explainer videos for business intelligence dashboards'
|
||||
},
|
||||
'business-intelligence-ai': {
|
||||
title: 'AI for Business Intelligence',
|
||||
description: 'Integration of AI models into business intelligence systems for process optimization'
|
||||
},
|
||||
'claude-personal-assistant': {
|
||||
title: 'AI-Based Personal Assistant with Claude AI',
|
||||
description: 'Development of a personalized AI assistant for professional email communication'
|
||||
},
|
||||
'e-commerce-integration': {
|
||||
title: 'Integration with TradeByte',
|
||||
description: 'Integration of a Shopify store with JTL-WaWi for optimized inventory management'
|
||||
},
|
||||
'jtl-integration-project': {
|
||||
title: 'JTL Integration Reference Project',
|
||||
description: 'A complete warehouse system in Krefeld based on JTL'
|
||||
},
|
||||
'local-llm-training': {
|
||||
title: 'Training of Local AI Models',
|
||||
description: 'Development of a pipeline for training and fine-tuning local AI models'
|
||||
},
|
||||
'processautomation': {
|
||||
title: 'Automation with Python',
|
||||
description: 'Development of automation solutions for business processes'
|
||||
},
|
||||
'rfid-automation': {
|
||||
title: 'RFID Automation',
|
||||
description: 'Development of an automated system for creating and printing RFID labels'
|
||||
},
|
||||
'timetracking-software': {
|
||||
title: 'Digital Time Tracking',
|
||||
description: 'Development of a modern time tracking solution with Next.js, Flask, and MSSQL'
|
||||
}
|
||||
},
|
||||
// Status messages
|
||||
status: {
|
||||
loading: 'Loading...',
|
||||
error: 'An error has occurred',
|
||||
success: 'Successfully loaded'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
// src/i18n/locales/en/pages/privacy.ts
|
||||
export const privacy = {
|
||||
// SEO and Schema
|
||||
seo: {
|
||||
title: 'Privacy Policy',
|
||||
description: 'Privacy Policy for Damjan Savić\'s portfolio website'
|
||||
},
|
||||
schema: {
|
||||
name: 'Privacy Policy',
|
||||
description: 'Privacy Policy for Damjan Savić\'s portfolio website'
|
||||
},
|
||||
// Main content
|
||||
content: {
|
||||
title: 'Privacy Policy',
|
||||
lastUpdated: 'Last updated: March 10, 2024',
|
||||
// Navigation
|
||||
breadcrumbs: {
|
||||
currentTitle: 'Privacy Policy'
|
||||
},
|
||||
// Sections
|
||||
sections: {
|
||||
introduction: {
|
||||
title: '1. Introduction',
|
||||
content: 'This Privacy Policy explains how we collect, use, and protect your personal data when you use our website.'
|
||||
},
|
||||
dataCollection: {
|
||||
title: '2. Data Collection',
|
||||
providedInfo: {
|
||||
title: '2.1 Information You Provide',
|
||||
intro: 'When using our contact form, we collect:',
|
||||
items: [
|
||||
'Name',
|
||||
'Email address',
|
||||
'Message content'
|
||||
]
|
||||
},
|
||||
automaticInfo: {
|
||||
title: '2.2 Automatically Collected Information',
|
||||
intro: 'We automatically collect:',
|
||||
items: [
|
||||
'IP address',
|
||||
'Browser type',
|
||||
'Device information',
|
||||
'Pages visited',
|
||||
'Time spent on the website'
|
||||
]
|
||||
}
|
||||
},
|
||||
dataUsage: {
|
||||
title: '3. Use of Your Data',
|
||||
intro: 'We use your data for:',
|
||||
items: [
|
||||
'Responding to your inquiries',
|
||||
'Improving our website',
|
||||
'Analyzing usage patterns',
|
||||
'Fraud prevention'
|
||||
]
|
||||
},
|
||||
cookies: {
|
||||
title: '4. Cookies',
|
||||
intro: 'We use cookies for:',
|
||||
items: [
|
||||
'Storing your preferences',
|
||||
'Analyzing website traffic',
|
||||
'Understanding user behavior'
|
||||
]
|
||||
},
|
||||
security: {
|
||||
title: '5. Data Security',
|
||||
intro: 'We implement appropriate security measures to protect your personal data, including:',
|
||||
items: [
|
||||
'Data transmission encryption',
|
||||
'Regular security assessments',
|
||||
'Access controls',
|
||||
'Secure data storage'
|
||||
]
|
||||
},
|
||||
rights: {
|
||||
title: '6. Your Rights',
|
||||
intro: 'You have the right to:',
|
||||
items: [
|
||||
'Access your personal data',
|
||||
'Rectify the data',
|
||||
'Delete the data',
|
||||
'Object to data processing',
|
||||
'Data portability'
|
||||
]
|
||||
},
|
||||
contact: {
|
||||
title: '7. Contact Information',
|
||||
intro: 'For privacy-related inquiries, contact us at:',
|
||||
email: 'Email: privacy@damjan-savic.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// src/i18n/locales/en/pages/terms.ts
|
||||
export const terms = {
|
||||
// SEO and Schema
|
||||
seo: {
|
||||
title: 'Terms and Conditions',
|
||||
description: 'Terms and Conditions for Damjan Savić\'s portfolio website'
|
||||
},
|
||||
schema: {
|
||||
name: 'Terms and Conditions',
|
||||
description: 'Terms and Conditions for Damjan Savić\'s portfolio website'
|
||||
},
|
||||
// Main content
|
||||
content: {
|
||||
title: 'Terms and Conditions',
|
||||
lastUpdated: 'Last updated: March 10, 2024',
|
||||
// Navigation
|
||||
breadcrumbs: {
|
||||
currentTitle: 'Terms and Conditions'
|
||||
},
|
||||
// Sections
|
||||
sections: {
|
||||
agreement: {
|
||||
title: '1. Agreement to Terms',
|
||||
content: 'By accessing our website, you agree to be bound by these Terms and Conditions and to comply with all applicable laws and regulations.'
|
||||
},
|
||||
intellectualProperty: {
|
||||
title: '2. Intellectual Property Rights',
|
||||
content: 'All content on this website, including but not limited to text, graphics, logos, images, and software, is the property of Damjan Savić and is protected by intellectual property laws.'
|
||||
},
|
||||
userResponsibilities: {
|
||||
title: '3. User Responsibilities',
|
||||
intro: 'When using our website, you agree to:',
|
||||
items: [
|
||||
'Provide accurate information',
|
||||
'Use the website legally and ethically',
|
||||
'Not attempt unauthorized access',
|
||||
'Not disrupt the website\'s operation'
|
||||
]
|
||||
},
|
||||
liability: {
|
||||
title: '4. Limitation of Liability',
|
||||
content: 'We are not liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use or inability to use the website.'
|
||||
},
|
||||
changes: {
|
||||
title: '5. Changes to Terms',
|
||||
content: 'We reserve the right to modify these terms at any time. We will notify users of any material changes by posting the new Terms and Conditions on this page.'
|
||||
},
|
||||
governingLaw: {
|
||||
title: '6. Governing Law',
|
||||
content: 'These terms are governed by and construed in accordance with the laws of the Federal Republic of Germany, without regard to its conflict of law provisions.'
|
||||
},
|
||||
contact: {
|
||||
title: '7. Contact Information',
|
||||
intro: 'For questions about these Terms and Conditions, please contact us at:',
|
||||
email: 'Email: legal@damjan-savic.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// src/i18n/locales/en/portfolio/index.ts
|
||||
import { aiDataReader } from './projects/ai-data-reader';
|
||||
export const portfolio = {
|
||||
// Meta information for the portfolio page
|
||||
seo: {
|
||||
title: 'Portfolio - Damjan Savić',
|
||||
description: 'Discover my projects in JTL integration, e-commerce, and digital transformation.'
|
||||
},
|
||||
// Portfolio headings and filters
|
||||
sections: {
|
||||
title: 'Portfolio',
|
||||
subtitle: 'Selected projects and references',
|
||||
filterTitle: 'Categories',
|
||||
sortTitle: 'Sort'
|
||||
},
|
||||
// Filter options
|
||||
filters: {
|
||||
all: 'All',
|
||||
categories: {
|
||||
'Data Science': 'Data Science',
|
||||
'AI Development': 'AI Development',
|
||||
'Integration': 'Integration',
|
||||
'Full-Stack Development': 'Full-Stack Development',
|
||||
'Data Processing': 'Data Processing',
|
||||
'Content Production': 'Content Production',
|
||||
'Machine Learning': 'Machine Learning',
|
||||
'Automation': 'Automation'
|
||||
}
|
||||
},
|
||||
// Sort options
|
||||
sort: {
|
||||
date: 'Date',
|
||||
title: 'Title',
|
||||
category: 'Category'
|
||||
},
|
||||
// UI texts
|
||||
ui: {
|
||||
loading: 'Loading projects...',
|
||||
noProjects: 'No projects found',
|
||||
viewProject: 'View project',
|
||||
backToPortfolio: 'Back to portfolio',
|
||||
technologies: 'Technologies',
|
||||
client: 'Client',
|
||||
duration: 'Duration',
|
||||
visitWebsite: 'Visit website',
|
||||
viewRepository: 'View repository',
|
||||
viewDocumentation: 'Documentation'
|
||||
},
|
||||
// All projects
|
||||
projects: {
|
||||
'ai-data-reader': aiDataReader,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
// src/i18n/locales/en/portfolio/projects/ai-data-reader.ts
|
||||
export const aiDataReader = {
|
||||
meta: {
|
||||
slug: 'ai-data-reader',
|
||||
title: "AI-Based Product Data Management from PDF Files",
|
||||
description: "Development of an automated system for extracting and structuring product data from PDF documents",
|
||||
excerpt: "Integration of Claude AI for intelligent processing of product information and seamless JTL ERP integration.",
|
||||
date: "2024-02",
|
||||
category: "Data Processing",
|
||||
client: "Amazon Seller",
|
||||
duration: "2 months",
|
||||
url: "https://www.damjan-savic.com",
|
||||
repository: "",
|
||||
documentation: "",
|
||||
published: true,
|
||||
featured: true,
|
||||
technologies: ["Python", "FastAPI", "Claude AI", "PyPDF", "Pydantic", "JTL-Wawi"],
|
||||
tags: ["Data Extraction", "ERP", "Process Automation", "PDF Processing"]
|
||||
},
|
||||
content: {
|
||||
intro: "An AI-powered system for automated extraction of product data from PDF documents with direct integration into JTL inventory management systems, developed for maximum efficiency in product data management.",
|
||||
|
||||
challenge: {
|
||||
title: "The Challenge",
|
||||
description: "Manual processing of product data from PDF documents was time-consuming and error-prone.",
|
||||
points: [
|
||||
"Large volumes of PDF documents containing product information",
|
||||
"Time-intensive manual data entry",
|
||||
"Inconsistent data quality",
|
||||
"High personnel requirements"
|
||||
]
|
||||
},
|
||||
|
||||
solution: {
|
||||
title: "Our Solution",
|
||||
description: "Development of an AI-powered system for automated data extraction",
|
||||
content: "By leveraging Claude AI and modern Python technologies, we created a fully automated solution that processes PDF documents and integrates the extracted data directly into the JTL inventory management system.",
|
||||
points: [
|
||||
"Automated PDF processing",
|
||||
"AI-powered data extraction",
|
||||
"Intelligent data validation",
|
||||
"Seamless JTL integration"
|
||||
]
|
||||
},
|
||||
|
||||
technical: {
|
||||
title: "Technical Implementation",
|
||||
description: "The solution is based on a modular Python architecture with several core components:",
|
||||
points: [
|
||||
"Robust PDF text extraction with PyPDF",
|
||||
"AI-powered data extraction with Claude AI",
|
||||
"Validation through Pydantic Models",
|
||||
"JTL ERP integration via CSV export"
|
||||
],
|
||||
code: `class ProductData(BaseModel):
|
||||
artikel_nummer: str
|
||||
name: str
|
||||
hersteller: str
|
||||
inhaltsstoffe: List[str]
|
||||
allergene: List[str]
|
||||
naehrwerte: NaehrwertData
|
||||
nettogewicht: str
|
||||
vegan: bool
|
||||
vegetarisch: bool
|
||||
|
||||
def _extract_with_claude(self, prompt: str) -> Dict[str, Any]:
|
||||
'''Extracts structured data with Claude AI.'''
|
||||
response = self.client.messages.create(
|
||||
model='claude-3-sonnet-20240229',
|
||||
messages=[{
|
||||
'role': 'user',
|
||||
'content': prompt
|
||||
}]
|
||||
)
|
||||
return self._clean_json_string(response.content)`
|
||||
},
|
||||
|
||||
implementation: {
|
||||
title: "Implementation Process",
|
||||
description: "The automated workflow was implemented in several phases:",
|
||||
points: [
|
||||
"PDF batch processing from input directory",
|
||||
"Intelligent text recognition and structuring",
|
||||
"Multi-layer validation logic",
|
||||
"Robust error handling",
|
||||
"Integration with existing systems"
|
||||
]
|
||||
},
|
||||
|
||||
results: {
|
||||
title: "Results and Impact",
|
||||
description: "The implementation led to significant improvements in business processes:",
|
||||
points: [
|
||||
"90% time savings in product data management",
|
||||
"99% accuracy in data extraction",
|
||||
"Elimination of manual data entry",
|
||||
"Standardized product data quality",
|
||||
"Significantly reduced error rate"
|
||||
]
|
||||
},
|
||||
|
||||
conclusion: "The developed system has revolutionized product data management. By combining AI technology with automated data processing, we not only drastically increased efficiency but also significantly improved data quality. The project demonstrates impressively how modern AI solutions can solve practical business problems."
|
||||
}
|
||||
};
|
||||
|
||||
// Re-export for compatibility with the project import system
|
||||
export default aiDataReader;
|
||||
Reference in New Issue
Block a user