Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
27af573
add: user model🎁
supercodestart Mar 27, 2024
c29bc54
adopt: dotenv📦
supercodestart Mar 28, 2024
e9721c6
config: google verification & gtm id✏️
supercodestart Mar 28, 2024
171f49f
adopt: `flowbite`, `flowbite-react`📦
supercodestart Mar 28, 2024
cbae099
config: tailwind for flowbite✅
supercodestart Mar 28, 2024
facf6e8
style: reset global✨
supercodestart Mar 28, 2024
e765fcd
add: i18n📓
supercodestart Mar 28, 2024
78d3f44
fix: frontend components & pages♻️
supercodestart Mar 28, 2024
d694eac
fix: database model♻️
supercodestart Mar 28, 2024
7ea8924
add: database migration♻️
supercodestart Mar 28, 2024
165778e
fix: front layout & navbar✅
supercodestart Mar 28, 2024
ae840fa
add: base structure for user management🚧
supercodestart Mar 28, 2024
3f31f57
adopt: `webpack`, `ts-loader`📦
supercodestart Mar 28, 2024
d969f73
fix: home page🐞
supercodestart Mar 28, 2024
cdebfad
fix: navbar styles as sticky🚧
supercodestart Mar 28, 2024
bcf0b73
config: vscode settings🚧
supercodestart Mar 29, 2024
7dbdc80
add: `breadcrumb`, `mytable`🚧
supercodestart Mar 29, 2024
25e85e3
chore: add i18n for user🚧
supercodestart Mar 29, 2024
1123fe3
fix: layouts🚧
supercodestart Mar 29, 2024
2767467
fix: admin navbar🚧
supercodestart Mar 29, 2024
a7dc517
fix: user page for sample🚧
supercodestart Mar 29, 2024
3d83b2d
fix: error param issues🐞
supercodestart Mar 29, 2024
1017215
Merge pull request #93 from james-gates-0212/92-add-user-management
supercodestart Mar 29, 2024
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
25 changes: 14 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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 = ""
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
"editor.formatOnSave": true
},
"editor.tabSize": 2,
"cSpell.words": ["MERN", "portfolio", "tailwindcss"]
"cSpell.words": ["flowbite", "hoverable", "MERN", "portfolio", "tailwindcss"]
}
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions src/admin/common/BreadCrumb.tsx
Original file line number Diff line number Diff line change
@@ -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<IBreadCrumbItem> }) {
return (
<Breadcrumb className="mb-5">
{(links || []).map((link: IBreadCrumbItem, i) => (
<Breadcrumb.Item key={`breadcrumb-item-${i}`}>
<Link href={link.href}>{link.name}</Link>
</Breadcrumb.Item>
))}
</Breadcrumb>
);
}
93 changes: 93 additions & 0 deletions src/admin/common/MyTable.tsx
Original file line number Diff line number Diff line change
@@ -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<IHeader>;
hoverable?: boolean;
rows: Array<IRow>;
}

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 (
<span
onClick={(evt) => {
evt.stopPropagation();
evt.preventDefault();
onClick && onClick.call(null);
}}
>
{content}
</span>
);
};

return (
<div className="overflow-x-auto mb-5">
<Table className="text-md font-medium" hoverable={hoverable}>
<Table.Head>
{hasCheckBox && (
<Table.HeadCell className="p-4">
<Checkbox />
</Table.HeadCell>
)}
{(headers || []).map(({ key, label, onClick, order }: IHeader) => (
<Table.HeadCell
key={`table-header-${key}`}
onClick={(evt) => {
evt.stopPropagation();
evt.preventDefault();
onClick && onClick.call(null, key, order);
}}
>
{i18n(label)}
</Table.HeadCell>
))}
</Table.Head>
<Table.Body className="divide-y">
{(rows || []).map((row: IRow, i) => (
<Table.Row key={`table-row-${i}`} className="bg-white dark:border-gray-700 dark:bg-gray-800">
{hasCheckBox && (
<Table.Cell className="p-4">
<Checkbox />
</Table.Cell>
)}
{headerKeys.map((key) => (
<Table.Cell key={`table-row-${i}-${key}`}>
<RenderCell props={row[key]} />
</Table.Cell>
))}
</Table.Row>
))}
</Table.Body>
</Table>
</div>
);
}
99 changes: 51 additions & 48 deletions src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -20,58 +21,60 @@ export const metadata: Metadata = {

export default function Page() {
return (
<section className="about container mx-auto px-2 sm:px-6 lg:px-8">
<div className="grid md:grid-cols-12 mt-28">
<div className="md:col-span-7 flex flex-col items-center">
<h1 className="pb-20 text-center text-4xl">
I&apos;m <strong className="text-impact">James Gates</strong>, really glad to meet{' '}
<strong className="text-impact">you</strong>
</h1>
<RootLayout>
<section className="about container mx-auto px-2 sm:px-6 lg:px-8">
<div className="grid md:grid-cols-12 mt-28">
<div className="md:col-span-7 flex flex-col items-center">
<h1 className="pb-20 text-center text-4xl">
I&apos;m <strong className="text-black dark:text-white">James Gates</strong>, really glad to meet{' '}
<strong className="text-black dark:text-white">you</strong>
</h1>

<p className="text-xl leading-8">
Hi, everyone, I&apos;m <span className="text-impact">James Gates</span>.<br />
I&apos;m <span className="text-impact">Senior Full Stack Developer</span> and{' '}
<span className="text-impact">SEO Expert</span>.
<br />
<br />I have 5+ years&apos; 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.
<br />
<br />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.
</p>
</div>
<p className="text-xl leading-8">
Hi, everyone, I&apos;m <span className="text-black dark:text-white">James Gates</span>.<br />
I&apos;m <span className="text-black dark:text-white">Senior Full Stack Developer</span> and{' '}
<span className="text-black dark:text-white">SEO Expert</span>.
<br />
<br />I have 5+ years&apos; 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.
<br />
<br />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.
</p>
</div>

<div className="md:col-span-5 flex items-center justify-center">
<Image
src={aboutImage}
alt="About"
className="w-auto max-h-[450px] mt-16"
width={600}
height={529}
title="About"
loading="lazy"
/>
<div className="md:col-span-5 flex items-center justify-center">
<Image
src={aboutImage}
alt="About"
className="w-auto max-h-[450px] mt-16"
width={600}
height={529}
title="About"
loading="lazy"
/>
</div>
</div>
</div>

<div className="text-4xl mt-24 flex flex-row justify-center gap-4">
<SNLightBulb />
<strong className="text-impact">Skills</strong>
</div>
<div className="text-4xl mt-24 flex flex-row justify-center gap-4">
<SNLightBulb />
<strong className="text-black dark:text-white">Skills</strong>
</div>

<Skills />
<Skills />

<div className="text-xl leading-8 lg:w-1/2 px-10 mt-28 mx-auto">
<p>
For many aspects, such as Requirement Analysis, Architect Design, Database Modeling, Front-end/Back-end Coding
and Maintenance, I support reliable and qualified service.
</p>
<ul className="list-disc list-inside ml-5">
<li>Fast Progress, Best Quality and Constant Report</li>
<li>Cooperative Idea Support and so on</li>
</ul>
</div>
</section>
<div className="text-xl leading-8 lg:w-1/2 px-10 mt-28 mx-auto">
<p>
For many aspects, such as Requirement Analysis, Architect Design, Database Modeling, Front-end/Back-end
Coding and Maintenance, I support reliable and qualified service.
</p>
<ul className="list-disc list-inside ml-5">
<li>Fast Progress, Best Quality and Constant Report</li>
<li>Cooperative Idea Support and so on</li>
</ul>
</div>
</section>
</RootLayout>
);
}
15 changes: 15 additions & 0 deletions src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Flowbite>
<NavBar />
<section className="container mx-auto">{children}</section>
</Flowbite>
);
}
50 changes: 50 additions & 0 deletions src/app/admin/user/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<BreadCrumb
links={[
{ href: '/', name: 'Home' },
{ href: '#', name: i18n('entities.user.title') },
]}
/>
<MyTable
headers={[
{ key: 'id', label: 'entities.user.fields.id' },
{ key: 'fullName', label: 'entities.user.fields.fullName' },
{ key: 'firstName', label: 'entities.user.fields.firstName' },
{ key: 'lastName', label: 'entities.user.fields.lastName' },
{ key: 'email', label: 'entities.user.fields.email' },
]}
rows={[
{
id: 1,
fullName: 'James Gates',
firstName: 'James',
lastName: 'Gates',
email: 'james.gates.0212@gmail.com',
},
{
id: 2,
fullName: 'James Gates',
firstName: {
value: 'James',
render: (value) => (
<span className="whitespace-nowrap font-medium text-gray-900 dark:text-white">{value}</span>
),
},
lastName: 'Gates',
email: 'pop.runner88@outlook.com',
},
]}
hasCheckBox
hoverable
/>
</div>
);
}
Binary file removed src/app/assets/background.jpg
Binary file not shown.
Binary file removed src/app/assets/background.webp
Binary file not shown.
25 changes: 14 additions & 11 deletions src/app/experience/page.tsx
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -16,15 +17,17 @@ export const metadata: Metadata = {

export default function Page() {
return (
<section className="about container mx-auto px-2 sm:px-6 lg:px-8">
<h1 className="text-4xl leading-8 text-center mt-32">
<strong className="text-impact">Experience</strong>
<small> &amp; </small>
<strong className="text-impact">Education</strong>
</h1>
<div className="text-left mt-10">
<Experience />
</div>
</section>
<RootLayout>
<section className="about container mx-auto px-2 sm:px-6 lg:px-8">
<h1 className="text-4xl leading-8 text-center mt-32">
<strong className="text-black dark:text-white">Experience</strong>
<small> &amp; </small>
<strong className="text-black dark:text-white">Education</strong>
</h1>
<div className="text-left mt-10">
<Experience />
</div>
</section>
</RootLayout>
);
}
Loading