Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI - Build and Test

on:
push:
branches:
- develop
pull_request:
branches:
- develop
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
name: Build and Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '23'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Type check
run: yarn type-check

- name: Build application
run: yarn build

# Uncomment when tests are available
# - name: Run tests
# run: yarn test
49 changes: 49 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Deploy to Production

on:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
environment:
name: production
url: https://djoufson.com

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push Docker image
run: |
docker build -t djoufson/website:latest -t djoufson/website:${{ github.sha }} .
docker push djoufson/website:latest
docker push djoufson/website:${{ github.sha }}

- name: Deploy to VPS
uses: appleboy/ssh-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USERNAME }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}
script: |
cd ${{ secrets.PROJECT_PATH }}
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d --force-recreate
docker image prune -f
docker logout
58 changes: 0 additions & 58 deletions .github/workflows/main_deploy.yaml

This file was deleted.

97 changes: 85 additions & 12 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,74 @@
/* @custom-variant dark (&:is(.dark *)); */
/* Add to your existing globals.css */

/* Prism.js syntax highlighting theme */
@import 'prism-themes/themes/prism-material-dark.css';
/* Highlight.js syntax highlighting theme - responsive to dark mode */
@import 'highlight.js/styles/github.css';

/* Light mode overrides for highlight.js */
.hljs {
background: transparent !important;
color: #1f2937 !important;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #991b1b !important;
}

.hljs-string,
.hljs-doctag {
color: #059669 !important;
}

.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #7c3aed !important;
}

.hljs-comment,
.hljs-quote {
color: #6b7280 !important;
}

.hljs-number,
.hljs-literal {
color: #1d4ed8 !important;
}

/* Dark mode overrides for highlight.js */
.dark .hljs {
background: transparent !important;
color: #e6edf3 !important;
}

.dark .hljs-keyword,
.dark .hljs-selector-tag,
.dark .hljs-subst {
color: #ff7b72 !important;
}

.dark .hljs-string,
.dark .hljs-doctag {
color: #a5c9ea !important;
}

.dark .hljs-title,
.dark .hljs-section,
.dark .hljs-selector-id {
color: #79c0ff !important;
}

.dark .hljs-comment,
.dark .hljs-quote {
color: #8b949e !important;
}

.dark .hljs-number,
.dark .hljs-literal {
color: #79c0ff !important;
}

/* Custom prose styles */
/* blog-content.css */
Expand All @@ -31,15 +97,15 @@
}

.prose h6 {
@apply text-sm font-medium mt-4 mb-2 text-gray-600;
@apply text-sm font-medium mt-4 mb-2 text-gray-600 dark:text-gray-400;
}

.prose p {
@apply text-base leading-relaxed mb-5 text-gray-800;
@apply text-base leading-relaxed mb-5 text-foreground;
}

.prose a {
@apply text-blue-600 underline hover:text-blue-800 transition-colors;
@apply text-blue-600 dark:text-blue-400 underline hover:text-blue-800 dark:hover:text-blue-300 transition-colors;
}

.prose ul {
Expand All @@ -55,15 +121,22 @@
}

.prose blockquote {
@apply border-l-4 border-gray-300 pl-4 italic text-gray-600 my-6;
@apply border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-600 dark:text-gray-400 my-6;
}

/* Code blocks with syntax highlighting */
.prose pre {
@apply bg-gray-900 text-white text-sm p-4 rounded-md overflow-x-auto my-6;
@apply text-sm p-4 rounded-lg overflow-x-auto my-6 bg-transparent border border-gray-200 dark:border-gray-700;
}

/* Inline code (not inside pre blocks) */
.prose :not(pre) > code {
@apply text-pink-600 dark:text-pink-400 bg-transparent px-2 py-1 rounded-md font-mono text-sm border border-gray-200 dark:border-gray-700;
}

.prose code {
@apply text-pink-600 bg-transparent px-1 py-0.5 rounded font-mono text-sm;
/* Ensure highlighted code blocks don't get inline code styling */
.prose pre code {
@apply bg-transparent text-inherit px-0 py-0;
}

.prose img {
Expand All @@ -75,15 +148,15 @@
}

.prose th {
@apply border-b-2 border-gray-300 px-4 py-2 font-semibold;
@apply border-b-2 border-gray-300 dark:border-gray-600 px-4 py-2 font-semibold;
}

.prose td {
@apply border-b border-gray-200 px-4 py-2;
@apply border-b border-gray-200 dark:border-gray-700 px-4 py-2;
}

.prose hr {
@apply my-8 border-gray-200;
@apply my-8 border-gray-200 dark:border-gray-700;
}

.container {
Expand Down
18 changes: 14 additions & 4 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Footer from "@/components/footer";
import Header from "@/components/header";
import GoogleAnalytics from "@/components/google/GoogleAnalytics";
import GoogleAdSense from "@/components/google/GoogleAdSense";
import BackToTop from "@/components/BackToTop";
import { ThemeProvider } from "@/components/theme-provider";

export const metadata: Metadata = {
title: "Djoufson's Amazing World 🌍",
Expand All @@ -16,15 +18,23 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="en" suppressHydrationWarning>
<head>
<GoogleAnalytics />
<GoogleAdSense />
</head>
<body>
<Header />
<main>{children}</main>
<Footer />
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<Header />
<main>{children}</main>
<Footer />
<BackToTop />
</ThemeProvider>
</body>
</html>
);
Expand Down
49 changes: 49 additions & 0 deletions components/BackToTop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { ChevronUp } from 'lucide-react';

export default function BackToTop() {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
const toggleVisibility = () => {
if (window.scrollY > 300) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};

window.addEventListener('scroll', toggleVisibility);

return () => window.removeEventListener('scroll', toggleVisibility);
}, []);

const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
};

return (
<div
className={`fixed bottom-8 right-8 z-50 transition-all duration-300 transform ${
isVisible
? 'opacity-100 translate-y-0 scale-100'
: 'opacity-0 translate-y-4 scale-95 pointer-events-none'
}`}
>
<Button
onClick={scrollToTop}
size="icon"
className="rounded-full bg-primary hover:bg-primary/90 shadow-lg hover:shadow-xl transition-all duration-300 hover:scale-110"
aria-label="Back to top"
>
<ChevronUp className="h-5 w-5" />
</Button>
</div>
);
}
Loading