diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index daf38d2..38ad567 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,7 +5,7 @@ on: workflows: ['Tests'] types: [completed] branches: - - never-for-now-wip + - main workflow_dispatch: jobs: @@ -34,8 +34,12 @@ jobs: run: | aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 932061877711.dkr.ecr.us-east-1.amazonaws.com + - name: Sync production.env configuration file + run: | + aws s3 cp s3://jobshop.works/production.env ./src/production.env + - name: Build, tag and push latest docker images - run: docker buildx bake --push + run: ENV=prod docker buildx bake --push - name: Trigger redeployment from current deployment run: | diff --git a/docker-bake.hcl b/docker-bake.hcl index 89f77ce..30aeacd 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -32,4 +32,7 @@ target "app" { tags = [ "932061877711.dkr.ecr.us-east-1.amazonaws.com/simpletaskmanager/app:${TAG}" ] + ARGS = { + ENV = "${ENV}" + } } diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile index 836aa04..d5520f2 100644 --- a/docker/app.Dockerfile +++ b/docker/app.Dockerfile @@ -1,5 +1,6 @@ FROM 932061877711.dkr.ecr.us-east-1.amazonaws.com/simpletaskmanager/php AS app +ARG ENV=prod WORKDIR /var/www/html # Install composer bin @@ -12,6 +13,8 @@ RUN COMPOSER_ALLOW_SUPERUSER=1 composer install # Start building frontend assets FROM node:20-alpine AS node +ARG ENV=prod + COPY --from=app /var/www/html /var/www/html/ WORKDIR /var/www/html @@ -39,8 +42,8 @@ WORKDIR /var/www/html RUN chown -R www-data:www-data /var/www/html \ && chmod -R 755 /var/www/html/storage \ - && chmod -R 755 /var/www/html/bootstrap/cache - + && chmod -R 755 /var/www/html/bootstrap/cache \ + && chmod -R 755 /var/www/html/bootstrap RUN php artisan clear-compiled \ && composer dump-autoload diff --git a/src/composer.json b/src/composer.json index b518c15..b007d93 100755 --- a/src/composer.json +++ b/src/composer.json @@ -17,6 +17,7 @@ "laravel/sanctum": "^4.0", "laravel/tinker": "^2.10.1", "laravel/wayfinder": "^0.1.6", + "livewire/livewire": "^3.6", "maestroerror/laragent": "^0.4.1", "owen-it/laravel-auditing": "^14.0", "predis/predis": "^3.1", diff --git a/src/composer.lock b/src/composer.lock index 21f9999..1c6f4e0 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9d23622e03c3b166ac09ca9476c70d76", + "content-hash": "fc1135ce4ab93a47a57a9912759cea62", "packages": [ { "name": "amphp/amp", @@ -3372,6 +3372,82 @@ ], "time": "2024-12-08T08:18:47+00:00" }, + { + "name": "livewire/livewire", + "version": "v3.6.4", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "ef04be759da41b14d2d129e670533180a44987dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/ef04be759da41b14d2d129e670533180a44987dc", + "reference": "ef04be759da41b14d2d129e670533180a44987dc", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0|^11.0|^12.0", + "illuminate/routing": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/validation": "^10.0|^11.0|^12.0", + "laravel/prompts": "^0.1.24|^0.2|^0.3", + "league/mime-type-detection": "^1.9", + "php": "^8.1", + "symfony/console": "^6.0|^7.0", + "symfony/http-kernel": "^6.2|^7.0" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^10.15.0|^11.0|^12.0", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^8.21.0|^9.0|^10.0", + "orchestra/testbench-dusk": "^8.24|^9.1|^10.0", + "phpunit/phpunit": "^10.4|^11.5", + "psy/psysh": "^0.11.22|^0.12" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Livewire": "Livewire\\Livewire" + }, + "providers": [ + "Livewire\\LivewireServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v3.6.4" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2025-07-17T05:12:15+00:00" + }, { "name": "maestroerror/laragent", "version": "0.4.1", diff --git a/src/config/larabug.php b/src/config/larabug.php new file mode 100644 index 0000000..adfb36f --- /dev/null +++ b/src/config/larabug.php @@ -0,0 +1,145 @@ + env('LB_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | Project key + |-------------------------------------------------------------------------- + | + | This is your project key which you receive when creating a project + | Retrieve your key from https://www.larabug.com + | + */ + + 'project_key' => env('LB_PROJECT_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | Environment setting + |-------------------------------------------------------------------------- + | + | This setting determines if the exception should be send over or not. + | + */ + + 'environments' => [ + 'production', + ], + + /* + |-------------------------------------------------------------------------- + | Project version + |-------------------------------------------------------------------------- + | + | Set the project version, default: null. + | For git repository: shell_exec("git log -1 --pretty=format:'%h' --abbrev-commit") + | + */ + 'project_version' => null, + + /* + |-------------------------------------------------------------------------- + | Lines near exception + |-------------------------------------------------------------------------- + | + | How many lines to show near exception line. The more you specify the bigger + | the displayed code will be. Max value can be 50, will be defaulted to + | 12 if higher than 50 automatically. + | + */ + + 'lines_count' => 12, + + /* + |-------------------------------------------------------------------------- + | Prevent duplicates + |-------------------------------------------------------------------------- + | + | Set the sleep time between duplicate exceptions. This value is in seconds, default: 60 seconds (1 minute) + | + */ + + 'sleep' => 60, + + /* + |-------------------------------------------------------------------------- + | Skip exceptions + |-------------------------------------------------------------------------- + | + | List of exceptions to skip sending. + | + */ + + 'except' => [ + 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException', + ], + + /* + |-------------------------------------------------------------------------- + | Key filtering + |-------------------------------------------------------------------------- + | + | Filter out these variables before sending them to LaraBug + | + */ + + 'blacklist' => [ + '*authorization*', + '*password*', + '*token*', + '*auth*', + '*verification*', + '*credit_card*', + 'cardToken', // mollie card token + '*cvv*', + '*iban*', + '*name*', + '*email*' + ], + + /* + |-------------------------------------------------------------------------- + | Release git hash + |-------------------------------------------------------------------------- + | + | + */ + + // 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')), + + /* + |-------------------------------------------------------------------------- + | Server setting + |-------------------------------------------------------------------------- + | + | This setting allows you to change the server. + | + */ + + 'server' => env('LB_SERVER', 'https://www.larabug.com/api/log'), + + /* + |-------------------------------------------------------------------------- + | Verify SSL setting + |-------------------------------------------------------------------------- + | + | Enables / disables the SSL verification when sending exceptions to LaraBug + | Never turn SSL verification off on production instances + | + */ + 'verify_ssl' => env('LB_VERIFY_SSL', true), + +]; diff --git a/src/config/logging.php b/src/config/logging.php index 03033d1..3b56110 100755 --- a/src/config/logging.php +++ b/src/config/logging.php @@ -57,7 +57,7 @@ 'stack' => [ 'driver' => 'stack', - 'channels' => explode(',', env('LOG_STACK', 'single,larabug')), + 'channels' => explode(',', env('LOG_STACK', 'stderr,larabug')), 'ignore_exceptions' => false, ], diff --git a/src/resources/js/pages/projects/board.tsx b/src/resources/js/pages/projects/board.tsx index 65b54c1..c8c3f6a 100755 --- a/src/resources/js/pages/projects/board.tsx +++ b/src/resources/js/pages/projects/board.tsx @@ -23,11 +23,11 @@ export default function Board({ columns, project }: { columns: Shared.Data.Board }, { title: project.title, - href: route('projects.show', project.id), + href: route('projects.show', project.id as string), }, { title: 'Board', - href: route('projects.board', project.id), + href: route('projects.board', project.id as string), }, ]; diff --git a/src/resources/js/pages/welcome.tsx b/src/resources/js/pages/welcome.tsx index e575a56..3317622 100755 --- a/src/resources/js/pages/welcome.tsx +++ b/src/resources/js/pages/welcome.tsx @@ -1,95 +1,616 @@ import { type SharedData } from '@/types'; -import { Head, Link, usePage } from '@inertiajs/react'; -import BetaLogo from '../../images/jobshop.png'; -import ScreenBoard from '../../images/screen-board.png'; -import ScreenInbox from '../../images/screen-inbox.png'; -import ScreenProjects from '../../images/screen-projects.png'; -import ScreenCalendar from '../../images/screens-calendar.png'; -import ScreenChat from '../../images/screens-chat.png'; - -export default function Welcome() { +import { Link, usePage } from '@inertiajs/react'; +import { useEffect, useRef, useState } from 'react'; + +const useInView = (options) => { + const ref = useRef(null); + const [inView, setInView] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting) { + setInView(true); + if (ref.current) { + observer.unobserve(ref.current); + } + } + }, options); + + if (ref.current) { + observer.observe(ref.current); + } + + return () => { + if (ref.current) { + observer.disconnect(); + } + }; + }, [options]); + + return [ref, inView]; +}; + +// --- SVG Icon Components (HeroIcons & Custom) --- + +const MenuIcon = () => ( + + + +); + +const XIcon = () => ( + + + +); + +const CheckCircleIcon = ({ className }) => ( + + + +); + +const ChevronDownIcon = ({ className }) => ( + + + +); + +const SunIcon = ({ className }) => ( + + + +); + +const MoonIcon = ({ className }) => ( + + + +); + +const AnimatedAppPreview = ({ theme }) => ( + + {/* Background and Frame */} + + + + + + + + + + {/* Sidebar */} + + + + {/* Sidebar links */} + + + + + + {/* Main Content Area - Kanban Board */} + + {/* Columns */} + + + + + {/* Column Headers */} + + To Do + + + In Progress + + + Done + + + {/* Static Cards */} + + + + + {/* Animated Card */} + + + + + + + {/* Animated Checkmark on Done Card */} + + + + + + + {/* Animated cursor */} + + + + +); + +// --- Main App Component --- + +export default function App() { const { auth } = usePage().props; + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const [heroLoaded, setHeroLoaded] = useState(false); + const [theme, setTheme] = useState('light'); + + useEffect(() => { + // Check for saved theme in localStorage or system preference + const savedTheme = localStorage.getItem('theme'); + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + if (savedTheme) { + setTheme(savedTheme); + } else if (prefersDark) { + setTheme('dark'); + } + }, []); + + useEffect(() => { + // Apply theme class to root element + if (theme === 'dark') { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + localStorage.setItem('theme', theme); + }, [theme]); + + const toggleTheme = () => { + setTheme(theme === 'light' ? 'dark' : 'light'); + }; + + useEffect(() => { + // Trigger hero animation shortly after component mounts + const timer = setTimeout(() => setHeroLoaded(true), 100); + return () => clearTimeout(timer); + }, []); + + const features = [ + { name: 'Intuitive Task Management', description: 'Easily create, assign, and track tasks with a clean interface designed for speed.' }, + { name: 'Project-Based Organization', description: 'Group related tasks into projects to keep your work organized and focused.' }, + { name: 'Kanban Board View', description: 'Visualize your workflow with a drag-and-drop Kanban board to seamlessly track progress.' }, + { name: 'Calendar View', description: 'Stay on top of deadlines with a clear calendar view of all your upcoming tasks.' }, + { name: 'AI-Powered Assistance', description: 'Leverage artificial intelligence to automate tasks, generate insights, and work smarter.' }, + { name: 'Customizable Workflows', description: "Adapt the app to your team's unique process, not the other way around." }, + ]; + + const faqs = [ + { + question: 'Is JobShop really free?', + answer: 'Yes! JobShop is currently free to use for teams of any size. We plan to introduce optional paid plans for advanced features in the future, but the core functionality will remain free.', + }, + { + question: 'Can I change my plan later?', + answer: 'Absolutely. When we introduce premium plans, you will be able to upgrade, downgrade, or cancel at any time from your account settings.', + }, + { + question: 'What makes JobShop different?', + answer: "JobShop focuses on a clean, intuitive user experience while providing powerful features. We prioritize speed, collaboration, and flexibility to fit your team's workflow.", + }, + { + question: 'Is my data secure?', + answer: 'Data security is our top priority. We use industry-standard encryption and security protocols to ensure your information is always safe and protected.', + }, + ]; + + const [openFaq, setOpenFaq] = useState(null); + + const toggleFaq = (index) => { + setOpenFaq(openFaq === index ? null : index); + }; + + // Refs for scroll animations + const [featuresRef, featuresInView] = useInView({ threshold: 0.1 }); + const [testimonialRef, testimonialInView] = useInView({ threshold: 0.2 }); + const [ctaRef, ctaInView] = useInView({ threshold: 0.2 }); + const [faqRef, faqInView] = useInView({ threshold: 0.2 }); return ( - <> - - - - -
-
- - -
-
-

Free Simple Task Management

- - Try out our simple task management today by registering for free - - -
-
-
-

Organize Tasks by Projects

- - Try out our simple task management today by registering for free - +
+
- - -
-

Manage Tasks with Board View

- - Try out our simple task management today by registering for free - -
-
-

Task Deadlines & Calendar Views

- - Try out our simple task management today by registering for free - - -
-
-

Breakdown New Projects with AI

- - Try out our simple task management today by registering for free - - + + {/* FAQ Section */} +
+
+
+

+ Frequently asked questions +

+
+ {faqs.map((faq, index) => ( +
+
+ +
+
+
+

{faq.answer}

+
+
+
+ ))} +
+
+
-
+ + ); } diff --git a/src/resources/js/types/index.d.ts b/src/resources/js/types/index.d.ts index f9d0a01..2989559 100755 --- a/src/resources/js/types/index.d.ts +++ b/src/resources/js/types/index.d.ts @@ -1,5 +1,5 @@ import { LucideIcon } from 'lucide-react'; -import type { Config } from 'ziggy-js'; +import type { Config, Router } from 'ziggy-js'; export interface Auth { user: User; @@ -7,7 +7,7 @@ export interface Auth { export interface BreadcrumbItem { title: string; - href: string; + href: string | Router; } export interface NavGroup { diff --git a/src/vite.config.ts b/src/vite.config.ts index 3e8d658..6f41f49 100755 --- a/src/vite.config.ts +++ b/src/vite.config.ts @@ -7,14 +7,6 @@ import tailwindcss from '@tailwindcss/vite'; import { resolve } from 'node:path'; export default defineConfig({ - server: { - host: '0.0.0.0', - port: 3000, - hmr: { - host: '0.0.0.0', - port: 3000, - }, - }, plugins: [ laravel({ input: ['resources/js/app.tsx'],