diff --git a/.env.example b/.env.example index 6d63feb..135b773 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,15 @@ -DATABASE_DIALECT = "postgres" -DATABASE_HOST = "127.0.0.1" -DATABASE_PORT = "5432" -DATABASE_DATABASE = "defaultdb" -DATABASE_USERNAME = "postgres" -DATABASE_PASSWORD = "postgres" -DATABASE_SSL = "false" -DATABASE_SSL_CERT = "" -DATABASE_LOGGING = "false" +DATABASE_DIALECT = "postgres" +DATABASE_HOST = "127.0.0.1" +DATABASE_PORT = "5432" +DATABASE_DATABASE = "defaultdb" +DATABASE_USERNAME = "postgres" +DATABASE_PASSWORD = "postgres" +DATABASE_SSL = "false" +DATABASE_SSL_CERT = "" +DATABASE_LOGGING = "false" -HOST_URL = "http://localhost:3000" -BASE_PATH = "" +HOST_URL = "http://localhost:3000" +BASE_PATH = "" + +GOOGLE_VERIFICATION = "" +GOOGLE_TAG_MANAGER_ID = "" diff --git a/.vscode/settings.json b/.vscode/settings.json index b158a21..e63870f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,5 +22,5 @@ "editor.formatOnSave": true }, "editor.tabSize": 2, - "cSpell.words": ["MERN", "portfolio", "tailwindcss"] + "cSpell.words": ["flowbite", "hoverable", "MERN", "portfolio", "tailwindcss"] } diff --git a/package.json b/package.json index 7b830fd..c36cbd4 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,9 @@ "@types/react-vertical-timeline-component": "^3.3.6", "bcrypt": "^5.1.1", "cli-highlight": "^2.1.11", + "dotenv": "^16.4.5", + "flowbite": "^2.3.0", + "flowbite-react": "^0.7.5", "lodash": "^4.17.21", "mariadb": "^3.3.0", "mysql2": "^3.9.2", @@ -68,7 +71,10 @@ "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.11", "tailwindcss": "^3.3.0", - "typescript": "^5.4.3" + "ts-loader": "^9.5.1", + "typescript": "^5.4.3", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" }, "nextBundleAnalysis": { "budget": 358400, diff --git a/src/admin/common/BreadCrumb.tsx b/src/admin/common/BreadCrumb.tsx new file mode 100644 index 0000000..8f17bc2 --- /dev/null +++ b/src/admin/common/BreadCrumb.tsx @@ -0,0 +1,19 @@ +import { Breadcrumb } from 'flowbite-react'; +import Link from 'next/link'; + +interface IBreadCrumbItem { + href: string; + name: string; +} + +export default function BreadCrumb({ links }: { links: Array }) { + return ( + + {(links || []).map((link: IBreadCrumbItem, i) => ( + + {link.name} + + ))} + + ); +} diff --git a/src/admin/common/MyTable.tsx b/src/admin/common/MyTable.tsx new file mode 100644 index 0000000..057c011 --- /dev/null +++ b/src/admin/common/MyTable.tsx @@ -0,0 +1,93 @@ +'use client'; + +import { i18n } from '@/i18n'; +import { Checkbox, Table } from 'flowbite-react'; + +interface IHeader { + key: string; + label: string; + onClick?: Function; + order?: 'asc' | 'desc' | 'none'; +} + +interface ICell { + value: string; + onClick?: Function; + render?: Function; +} + +interface IRow { + [key: string]: ICell | number | string; +} + +interface ITables { + hasCheckBox?: boolean; + headers: Array; + hoverable?: boolean; + rows: Array; +} + +export default function MyTable(props: ITables) { + const { hoverable, headers, hasCheckBox, rows } = props; + + const headerKeys = (headers || []).map((header: IHeader) => header.key); + + const RenderCell = ({ props }: { props: ICell | number | string }) => { + const { value, onClick, render } = + typeof props === 'object' ? props : { value: props, onClick: undefined, render: undefined }; + const content = (render && render.call(null, value)) || value; + return ( + { + evt.stopPropagation(); + evt.preventDefault(); + onClick && onClick.call(null); + }} + > + {content} + + ); + }; + + return ( +
+ + + {hasCheckBox && ( + + + + )} + {(headers || []).map(({ key, label, onClick, order }: IHeader) => ( + { + evt.stopPropagation(); + evt.preventDefault(); + onClick && onClick.call(null, key, order); + }} + > + {i18n(label)} + + ))} + + + {(rows || []).map((row: IRow, i) => ( + + {hasCheckBox && ( + + + + )} + {headerKeys.map((key) => ( + + + + ))} + + ))} + +
+
+ ); +} diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 87d4df0..e4f17bd 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -1,8 +1,9 @@ import { SNLightBulb } from '@icongo/sn'; +import aboutImage from '@/app/assets/about.svg'; import Image from 'next/image'; +import RootLayout from '@/components/layouts/RootLayout'; import Skills from '@/components/Skills'; import type { Metadata } from 'next'; -import aboutImage from '@/app/assets/about.svg'; export const metadata: Metadata = { title: "James Gates | I'm glad to meet you", @@ -20,58 +21,60 @@ export const metadata: Metadata = { export default function Page() { return ( -
-
-
-

- I'm James Gates, really glad to meet{' '} - you -

+ +
+
+
+

+ I'm James Gates, really glad to meet{' '} + you +

-

- Hi, everyone, I'm James Gates.
- I'm Senior Full Stack Developer and{' '} - SEO Expert. -
-
I have 5+ years' experience for web development. During last years, I had earned many skills to - develop and manage a website and it now helps for a new project to develop in a high quality, rapidly. -
-
I like to work with a simple communication, a clean and optimized code convention, a high quality - development for collaboration and maintenance, and a keeping schedules for a project. I also love learning a - new thing from analyzing, discussing and resolving variable claims. -

-
+

+ Hi, everyone, I'm James Gates.
+ I'm Senior Full Stack Developer and{' '} + SEO Expert. +
+
I have 5+ years' experience for web development. During last years, I had earned many skills to + develop and manage a website and it now helps for a new project to develop in a high quality, rapidly. +
+
I like to work with a simple communication, a clean and optimized code convention, a high quality + development for collaboration and maintenance, and a keeping schedules for a project. I also love learning + a new thing from analyzing, discussing and resolving variable claims. +

+
-
- About +
+ About +
-
-
- - Skills -
+
+ + Skills +
- + -
-

- For many aspects, such as Requirement Analysis, Architect Design, Database Modeling, Front-end/Back-end Coding - and Maintenance, I support reliable and qualified service. -

-
    -
  • Fast Progress, Best Quality and Constant Report
  • -
  • Cooperative Idea Support and so on
  • -
-
-
+
+

+ For many aspects, such as Requirement Analysis, Architect Design, Database Modeling, Front-end/Back-end + Coding and Maintenance, I support reliable and qualified service. +

+
    +
  • Fast Progress, Best Quality and Constant Report
  • +
  • Cooperative Idea Support and so on
  • +
+
+ + ); } diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 0000000..f5f8444 --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,15 @@ +import NavBar from '@/components/admin/NavBar'; +import { Flowbite } from 'flowbite-react'; + +export default function AdminLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + +
{children}
+
+ ); +} diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx new file mode 100644 index 0000000..8939bb2 --- /dev/null +++ b/src/app/admin/user/page.tsx @@ -0,0 +1,50 @@ +'use client'; + +import { i18n } from '@/i18n'; +import BreadCrumb from '@/admin/common/BreadCrumb'; +import MyTable from '@/admin/common/MyTable'; + +export default function Page() { + return ( +
+ + ( + {value} + ), + }, + lastName: 'Gates', + email: 'pop.runner88@outlook.com', + }, + ]} + hasCheckBox + hoverable + /> +
+ ); +} diff --git a/src/app/assets/background.jpg b/src/app/assets/background.jpg deleted file mode 100644 index 45bf2d5..0000000 Binary files a/src/app/assets/background.jpg and /dev/null differ diff --git a/src/app/assets/background.webp b/src/app/assets/background.webp deleted file mode 100644 index 6f3d669..0000000 Binary files a/src/app/assets/background.webp and /dev/null differ diff --git a/src/app/experience/page.tsx b/src/app/experience/page.tsx index 4a18864..53b9ba7 100644 --- a/src/app/experience/page.tsx +++ b/src/app/experience/page.tsx @@ -1,5 +1,6 @@ -import type { Metadata } from 'next'; import Experience from '@/components/clients/Experience'; +import RootLayout from '@/components/layouts/RootLayout'; +import type { Metadata } from 'next'; export const metadata: Metadata = { title: 'James Gates | Experience & Education', @@ -16,15 +17,17 @@ export const metadata: Metadata = { export default function Page() { return ( -
-

- Experience - & - Education -

-
- -
-
+ +
+

+ Experience + & + Education +

+
+ +
+
+
); } diff --git a/src/app/globals.css b/src/app/globals.css index 08ef54c..39a9271 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,52 +2,8 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 225, 225, 225; - --background-rgb: 0, 0, 0; - --foreground-impact: #a2facf; -} - -*::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -*::-webkit-scrollbar-track { - background: transparent; - border-radius: 5px; - background: rgba(108, 101, 249, 0.1) !important; -} - -*::-webkit-scrollbar-thumb { - background-color: transparent; - border-radius: 5px; - background-color: rgba(109, 101, 249, 0.8) !important; -} - -*::-webkit-scrollbar-thumb:hover { - background-color: rgba(109, 101, 249, 1) !important; -} - -body { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - background-color: rgb(var(--background-rgb)); - background-image: url(./assets/background.webp); - background-position: center center; - background-repeat: no-repeat; - background-size: cover; - color: rgb(var(--foreground-rgb)); - font-family: var(--font-raleway); - min-height: 100vh; -} - @layer utilities { .text-balance { text-wrap: balance; } } - -.text-impact { - color: var(--foreground-impact); -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 23f5f9a..6748761 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,12 @@ +import '@/app/globals.css'; +import { classNames } from '@/components/Commons'; import { getConfig } from '@/config'; -import RootLayout from '@/components/layouts/RootLayout'; +import { GoogleTagManager } from '@next/third-parties/google'; +import { Raleway } from 'next/font/google'; +import { Suspense } from 'react'; +import Image from 'next/image'; +import PreImage from '@/app/assets/pre.svg'; +import ReactTooltip from '@/components/clients/ReactTooltip'; import type { Metadata, Viewport } from 'next'; const config = getConfig(); @@ -18,7 +25,7 @@ export const metadata: Metadata = { images: '/avatar.webp', }, verification: { - google: 'vSy6aBobFApUM2bc6BgZd2XYJQ8P3sFadIdTcEtClwY', + google: config.google.verification, }, icons: { icon: '/favicon.ico', @@ -37,6 +44,11 @@ export const metadata: Metadata = { }, }; +const raleway = Raleway({ + subsets: ['latin'], + display: 'swap', +}); + export const viewport: Viewport = { themeColor: 'black', width: 'device-width', @@ -44,10 +56,26 @@ export const viewport: Viewport = { userScalable: true, }; -export default function Layout({ +const Loading = () => ( +
+ Loading... +
+); + +export default function BaseLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - return {children}; + return ( + + + }> + {children} + + + + {Boolean(config.google.tag_manager_id) && } + + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 0477f75..373cb30 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,12 @@ import { classNames } from '@/components/Commons'; +import avatarImage from '@/app/assets/avatar.svg'; +import homeImage from '@/app/assets/home.svg'; import Image from 'next/image'; +import Link from 'next/link'; import LinkInfos from '@/infos/Links'; +import RootLayout from '@/components/layouts/RootLayout'; import style from './style.module.css'; import type { Metadata } from 'next'; -import Link from 'next/link'; -import homeImage from '@/app/assets/home.svg'; -import avatarImage from '@/app/assets/avatar.svg'; export const metadata: Metadata = { title: 'James Gates | Senior Full Stack Developer & SEO Expert', @@ -15,7 +16,7 @@ export const metadata: Metadata = { export default function Page() { return ( - <> +
@@ -30,19 +31,19 @@ export default function Page() {

My name is - James Gates + James Gates

  • - Senior Full Stack Developer + Senior Full Stack Developer
  • - SEO Expert + SEO Expert
  • - Continuously Learning + Continuously Learning
@@ -65,7 +66,7 @@ export default function Page() {

- Let me Introduce myself + Let me Introduce myself

As an accomplished Senior Full Stack Developer specializing in SEO and MERN Stack development, I offer a @@ -98,7 +99,9 @@ export default function Page() {

-

SEO Optimization Project for Website Enhancement

+

+ SEO Optimization Project for Website Enhancement +

Successfully improved SEO performance by identifying and resolving critical issues through a comprehensive technical audit, leading to enhanced website functionality and user experience. @@ -109,7 +112,7 @@ export default function Page() {

-

+

Web Application Development and Optimization Project

@@ -127,7 +130,7 @@ export default function Page() {

FIND ME ON

- Feel free to connect with me + Feel free to connect with me

    {LinkInfos.map(({ href, icon: { dark: DarkIcon }, label }, idx) => ( @@ -147,6 +150,6 @@ export default function Page() {
- + ); } diff --git a/src/components/admin/NavBar.tsx b/src/components/admin/NavBar.tsx new file mode 100644 index 0000000..9af52be --- /dev/null +++ b/src/components/admin/NavBar.tsx @@ -0,0 +1,27 @@ +'use client'; + +import { DarkThemeToggle, Navbar, NavbarBrand, NavbarCollapse, NavbarLink, NavbarToggle } from 'flowbite-react'; +import { i18n } from '@/i18n'; +import { usePathname } from 'next/navigation'; +import Link from 'next/link'; + +export default function NavBar() { + const current = usePathname(); + + return ( + + + {i18n('app.title')} + +
+ + +
+ + + {i18n('entities.user.menu')} + + +
+ ); +} diff --git a/src/components/clients/Experience.tsx b/src/components/clients/Experience.tsx index fab1851..ef9e4b7 100644 --- a/src/components/clients/Experience.tsx +++ b/src/components/clients/Experience.tsx @@ -17,7 +17,7 @@ export default function Experience() { visible={true} >

{title}

-

+

{company} diff --git a/src/components/clients/partials/NavBar.tsx b/src/components/clients/partials/NavBar.tsx index 5bec4f9..5ea1326 100644 --- a/src/components/clients/partials/NavBar.tsx +++ b/src/components/clients/partials/NavBar.tsx @@ -1,126 +1,32 @@ 'use client'; -import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'; -import { classNames } from '@/components/Commons'; -import { Disclosure, Menu, Transition } from '@headlessui/react'; -import { Fragment } from 'react'; + +import { DarkThemeToggle, Navbar, NavbarBrand, NavbarCollapse, NavbarLink, NavbarToggle } from 'flowbite-react'; +import { i18n } from '@/i18n'; import { usePathname } from 'next/navigation'; +import { VLLinkedin } from '@icongo/vl'; import Link from 'next/link'; -import LinkInfos from '@/infos/Links'; import NavItems from '@/infos/NavItems'; -import { VLLinkedin } from '@icongo/vl'; -import { SNUserMale } from '@icongo/sn'; export default function NavBar() { const current = usePathname(); return ( - - {({ open }) => ( - <> -
-
-
- - - Open main menu - {open ? ( - -
-
-
- -
-
-
- {NavItems.map((item) => ( - - {item.name} - - ))} -
-
-
-
- -
- - - Open user menu - - -
- - - {LinkInfos.map(({ href, icon: { dark: DarkIcon }, label }, idx) => ( - - {({ active }) => ( - - {label} - - )} - - ))} - - -
-
-
-
- - -
- {NavItems.map((item) => ( - - {item.name} - - ))} -
-
- - )} -
+ + + + {i18n('app.title')} + +
+ + +
+ + {NavItems.map((item) => ( + + {item.name} + + ))} + +
); } diff --git a/src/components/layouts/RootLayout.tsx b/src/components/layouts/RootLayout.tsx index f691521..4059e44 100644 --- a/src/components/layouts/RootLayout.tsx +++ b/src/components/layouts/RootLayout.tsx @@ -1,24 +1,6 @@ -import '@/app/globals.css'; -import { GoogleTagManager } from '@next/third-parties/google'; -import { Raleway } from 'next/font/google'; -import { Suspense } from 'react'; +import { Flowbite } from 'flowbite-react'; import Footer from '@/components/Footer'; -import Image from 'next/image'; import NavBar from '@/components/clients/partials/NavBar'; -import PreImage from '@/app/assets/pre.svg'; -import ReactTooltip from '@/components/clients/ReactTooltip'; -import Stars from '@/components/clients/backgrounds/Stars'; - -const Loading = () => ( -
- Loading... -
-); - -const raleway = Raleway({ - subsets: ['latin'], - display: 'swap', -}); export default function RootLayout({ children, @@ -26,19 +8,10 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - }> - -
- - {children} -
-
- -
- - - + + + {children} +