Compare commits
merge into: william:main
william:main
william:master
pull from: william:master
william:main
william:master
No commits in common. 'main' and 'master' have entirely different histories.
24 changed files with 3 additions and 2219 deletions
-
24.gitignore
-
16Dockerfile
-
51Jenkinsfile
-
3README.md
-
13index.html
-
1666package-lock.json
-
26package.json
-
1public/vite.svg
-
BINsrc/assets/coffee.jpg
-
BINsrc/assets/leaves.jpg
-
BINsrc/assets/workspace.jpg
-
21src/components/Card.tsx
-
36src/components/ListGroup.tsx
-
57src/components/Navbar.tsx
-
44src/main.tsx
-
27src/pages/App.tsx
-
73src/pages/EventPage.tsx
-
78src/pages/LoginPage.tsx
-
17src/pages/NotFound.tsx
-
31src/pages/Showcase.tsx
-
1src/vite-env.d.ts
-
21tsconfig.json
-
9tsconfig.node.json
-
7vite.config.ts
@ -1,24 +0,0 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
|
|||
node_modules |
|||
dist |
|||
dist-ssr |
|||
*.local |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -1,16 +0,0 @@ |
|||
FROM node:22.13.1-bookworm AS build |
|||
WORKDIR /build |
|||
|
|||
COPY package.json package.json |
|||
COPY package-lock.json package-lock.json |
|||
RUN npm ci |
|||
|
|||
COPY public/ public |
|||
COPY src/ src |
|||
RUN npm run build |
|||
|
|||
FROM httpd:alpine |
|||
WORKDIR /usr/local/apache2/htdocs |
|||
COPY --from=build /build/build/ . |
|||
RUN chown -R www-data:www-data /usr/local/apache2/htdocs \ |
|||
&& sed -i "s/Listen 80/Listen \${PORT}/g" /usr/local/apache2/conf/httpd.conf |
@ -1,51 +0,0 @@ |
|||
pipeline { |
|||
agent any |
|||
|
|||
environment { |
|||
FIREBASE_TOKEN = '1//01qpBX7fJp_MkCgYIARAAGAESNwF-L9IrxNCVYfwDg6jE_bhT0-ja1yQGO1Wxn2mFd8cH1O15uC8Nv2b1NXpEp8-oQXdabUXLJqM' |
|||
} |
|||
|
|||
stages { |
|||
stage("Checkout and build react app") { |
|||
when { expression { true } } |
|||
steps { |
|||
git(url: 'http://git.abilityell.com/william/react-project.git', branch: 'main') |
|||
|
|||
withEnv(['CI=false']) { //ignore warning |
|||
bat 'npm install' |
|||
// bat 'npm i -g firebase-tools -f' |
|||
// bat 'npm run build' |
|||
bat 'npm run dev' |
|||
} |
|||
|
|||
// bat ''' |
|||
// echo "${FIREBASE_TOKEN}" |
|||
// npx firebase login --interactive --token "${FIREBASE_TOKEN}" |
|||
// npx firebase init |
|||
// echo %cd% |
|||
// dir |
|||
// ''' |
|||
} |
|||
} |
|||
stage("Deploy React App") { |
|||
when { expression { true } } |
|||
steps { |
|||
withCredentials([sshUserPrivateKey(credentialsId: 'docker-ssh', keyFileVariable: 'SSH_KEY')]) { |
|||
bat ''' |
|||
scp -i $SSH_KEY -o StrictHostKeyChecking=no -r build/* $DOCKER_HOST:/var/www/html/ |
|||
|
|||
ssh -i $SSH_KEY -o StrictHostKeyChecking=no $DOCKER_HOST 'chmod -R 755 /var/www/html/*' |
|||
''' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
post { |
|||
success { |
|||
echo "Pipeline succeeded! ✅" |
|||
} |
|||
failure { |
|||
echo "Pipeline failed! ❌" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
# react-project |
|||
|
|||
React project for testing jenkins. |
@ -1,13 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>My-Event-App</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="/src/main.tsx"></script> |
|||
</body> |
|||
</html> |
1666
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,26 +0,0 @@ |
|||
{ |
|||
"name": "vite-project", |
|||
"private": true, |
|||
"version": "0.0.0", |
|||
"type": "module", |
|||
"scripts": { |
|||
"dev": "vite", |
|||
"build": "tsc && vite build", |
|||
"preview": "vite preview" |
|||
}, |
|||
"dependencies": { |
|||
"axios": "^1.9.0", |
|||
"bootstrap": "^5.2.3", |
|||
"react": "^18.2.0", |
|||
"react-dom": "^18.2.0", |
|||
"react-hook-form": "^7.56.4", |
|||
"react-router-dom": "^7.6.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@types/react": "^18.0.27", |
|||
"@types/react-dom": "^18.0.10", |
|||
"@vitejs/plugin-react": "^3.1.0", |
|||
"typescript": "^4.9.3", |
|||
"vite": "^4.1.0" |
|||
} |
|||
} |
@ -1 +0,0 @@ |
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> |
Before Width: 960 | Height: 640 | Size: 154 KiB |
Before Width: 960 | Height: 641 | Size: 117 KiB |
Before Width: 960 | Height: 638 | Size: 130 KiB |
@ -1,21 +0,0 @@ |
|||
interface CardProps { |
|||
title: string; |
|||
image: string; |
|||
text: string; |
|||
} |
|||
|
|||
function Card({ title, image, text }: CardProps) { |
|||
return ( |
|||
<> |
|||
<div className="card w-25 ms-5"> |
|||
<img src={image} className="card-img-top" alt={title} /> |
|||
<div className="card-body"> |
|||
<h5 className="card-title">{title}</h5> |
|||
<p className="card-text">{text}</p> |
|||
</div> |
|||
</div> |
|||
</> |
|||
); |
|||
} |
|||
|
|||
export default Card; |
@ -1,36 +0,0 @@ |
|||
import { useState } from "react"; |
|||
|
|||
interface ListGroupProps { |
|||
items: string[]; |
|||
heading: string; |
|||
} |
|||
|
|||
function ListGroup({ items, heading }: ListGroupProps) { |
|||
const [selectedIndex, setSelectedIndex] = useState(-1); |
|||
|
|||
return ( |
|||
<> |
|||
<h1 className="mx-5">{heading}</h1> |
|||
{items.length === 0 && <p>No Events Found.</p>} |
|||
<ul className="list-group mx-5"> |
|||
{items.map((item, index) => ( |
|||
<li |
|||
className={ |
|||
selectedIndex === index |
|||
? "list-group-item active" |
|||
: "list-group-item" |
|||
} |
|||
key={item} |
|||
onClick={() => { |
|||
setSelectedIndex(index); |
|||
}} |
|||
> |
|||
{item} |
|||
</li> |
|||
))} |
|||
</ul> |
|||
</> |
|||
); |
|||
} |
|||
|
|||
export default ListGroup; |
@ -1,57 +0,0 @@ |
|||
const Navbar = () => { |
|||
return ( |
|||
<nav className="navbar navbar-expand-lg bg-dark"> |
|||
<div className="container-fluid"> |
|||
<a className="navbar-brand text-light" href="/"> |
|||
My-Event-App |
|||
</a> |
|||
<button |
|||
className="navbar-toggler" |
|||
type="button" |
|||
data-bs-toggle="collapse" |
|||
data-bs-target="#navbarNav" |
|||
aria-controls="navbarNav" |
|||
aria-expanded="false" |
|||
aria-label="Toggle navigation" |
|||
> |
|||
<span className="navbar-toggler-icon"></span> |
|||
</button> |
|||
<div className="collapse navbar-collapse" id="navbarNav"> |
|||
<ul className="navbar-nav"> |
|||
<li className="nav-item"> |
|||
<a className="nav-link text-light" href="/"> |
|||
Home |
|||
</a> |
|||
</li> |
|||
<li className="nav-item"> |
|||
<a className="nav-link text-light" href="/events"> |
|||
Events |
|||
</a> |
|||
</li> |
|||
<li className="nav-item"> |
|||
<a className="nav-link text-light" href="/showcase"> |
|||
Showcase |
|||
</a> |
|||
</li> |
|||
<li className="nav-item position-absolute top-25 end-0 me-2"> |
|||
<a className="nav-link text-light" href="/login"> |
|||
Log In |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</nav> |
|||
); |
|||
}; |
|||
|
|||
function displayName() { |
|||
const localData = localStorage.getItem("currentUser"); |
|||
if (localData) { |
|||
const userData = JSON.parse(localData); |
|||
return userData.email; |
|||
} |
|||
return "Log In"; |
|||
} |
|||
|
|||
export default Navbar; |
@ -1,44 +0,0 @@ |
|||
import React from "react"; |
|||
import ReactDOM from "react-dom/client"; |
|||
import App from "./pages/App"; |
|||
import EventPage from "./pages/EventPage"; |
|||
import NotFound from "./pages/NotFound"; |
|||
import "bootstrap/dist/css/bootstrap.css"; |
|||
import { createBrowserRouter, RouterProvider } from "react-router-dom"; |
|||
import Showcase from "./pages/Showcase"; |
|||
import LoginPage from "./pages/LoginPage"; |
|||
|
|||
const router = createBrowserRouter([ |
|||
{ |
|||
path: "/", |
|||
element: <App />, |
|||
errorElement: <NotFound />, |
|||
}, |
|||
{ |
|||
path: "/events", |
|||
element: <EventPage />, |
|||
}, |
|||
{ |
|||
path: "/showcase", |
|||
element: <Showcase />, |
|||
}, |
|||
{ |
|||
path: "/login", |
|||
element: <LoginPage />, |
|||
}, |
|||
]); |
|||
|
|||
localStorage.setItem( |
|||
"currentUser", |
|||
JSON.stringify({ |
|||
email: "apple@sixmail.com", |
|||
password: "checkmate", |
|||
loggedIn: false, |
|||
}) |
|||
); |
|||
|
|||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( |
|||
<React.StrictMode> |
|||
<RouterProvider router={router} /> |
|||
</React.StrictMode> |
|||
); |
@ -1,27 +0,0 @@ |
|||
import Navbar from "../components/Navbar"; |
|||
import leaves from "../assets/leaves.jpg"; |
|||
|
|||
function App() { |
|||
return ( |
|||
<div> |
|||
<Navbar /> |
|||
<br /> |
|||
<h2 className="text-center">Welcome to my website!</h2> |
|||
<br /> |
|||
<div className="text-center"> |
|||
This website contains a list of events and a showcase. |
|||
</div> |
|||
<div className="text-center">You can also log in to the website.</div> |
|||
<br /> |
|||
<div> |
|||
<img |
|||
src={leaves} |
|||
className="d-block w-80 mx-auto mb-2 c-img" |
|||
alt="Image of a book and leaves" |
|||
/> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
export default App; |
@ -1,73 +0,0 @@ |
|||
import { useState } from "react"; |
|||
import ListGroup from "../components/ListGroup"; |
|||
import Navbar from "../components/Navbar"; |
|||
|
|||
function EventPage() { |
|||
const [businessEvents, updateBusinessEvents] = useState([ |
|||
"Meeting @ 9am", |
|||
"Lunch with HR @ 1pm", |
|||
"Meeting @ 4pm", |
|||
]); |
|||
const [otherEvents, updateOtherEvents] = useState([ |
|||
"Interview with Y Company @ 6pm", |
|||
"Company Christmas Party @ 7pm", |
|||
]); |
|||
|
|||
// wait for document to load first
|
|||
document.addEventListener("DOMContentLoaded", function (event) { |
|||
const eventButton = document.getElementById( |
|||
"eventBtn" |
|||
) as HTMLButtonElement; |
|||
eventButton.onclick = function () { |
|||
const val = (document.getElementById("eventInput") as HTMLInputElement) |
|||
.value; |
|||
if (val.length > 0) { |
|||
updateBusinessEvents([...businessEvents, val]); |
|||
(document.getElementById("eventInput") as HTMLInputElement).value = ""; |
|||
} |
|||
}; |
|||
|
|||
const otherButton = document.getElementById( |
|||
"otherBtn" |
|||
) as HTMLButtonElement; |
|||
otherButton.onclick = function () { |
|||
const val = (document.getElementById("otherInput") as HTMLInputElement) |
|||
.value; |
|||
if (val.length > 0) { |
|||
updateOtherEvents([...otherEvents, val]); |
|||
(document.getElementById("otherInput") as HTMLInputElement).value = ""; |
|||
} |
|||
}; |
|||
}); |
|||
|
|||
return ( |
|||
<div> |
|||
<Navbar /> |
|||
<br /> |
|||
<ListGroup items={businessEvents} heading={"Business Events"} /> |
|||
<br /> |
|||
<div className="mb-3"> |
|||
<label className="ms-5"> |
|||
Add a Business Event: |
|||
<input id="eventInput" className="ms-3" /> |
|||
<button id="eventBtn" className="ms-3 btn btn-outline-primary"> |
|||
Add Event |
|||
</button> |
|||
</label> |
|||
</div> |
|||
<ListGroup items={otherEvents} heading={"Other Events"} /> |
|||
<br /> |
|||
<div className="mb-3"> |
|||
<label className="ms-5"> |
|||
Add an Other Event: |
|||
<input id="otherInput" className="ms-3" /> |
|||
<button id="otherBtn" className="ms-3 btn btn-outline-primary"> |
|||
Add Event |
|||
</button> |
|||
</label> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
export default EventPage; |
@ -1,78 +0,0 @@ |
|||
import Navbar from "../components/Navbar"; |
|||
import { useForm } from "react-hook-form"; |
|||
|
|||
// LOGIN NOT WORKING
|
|||
|
|||
function LoginPage() { |
|||
const { |
|||
register, |
|||
handleSubmit, |
|||
formState: { errors }, |
|||
} = useForm(); |
|||
|
|||
const onSubmit = (data: any) => { |
|||
const localData = localStorage.getItem("currentUser"); |
|||
if (localData) { |
|||
const userData = JSON.parse(localData); |
|||
if ( |
|||
userData.password === data.password && |
|||
userData.email === data.email |
|||
) { |
|||
localStorage.setItem( |
|||
"currentUser", |
|||
JSON.stringify({ |
|||
email: "apple@sixmail.com", |
|||
password: "checkmate", |
|||
loggedIn: true, |
|||
}) |
|||
); |
|||
console.log("You are now logged in."); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<div> |
|||
<Navbar /> |
|||
<br /> |
|||
<div |
|||
className="container d-flex |
|||
align-items-center |
|||
justify-content-center" |
|||
> |
|||
<form className="w-50" onSubmit={handleSubmit(onSubmit)}> |
|||
<div className="mb-3"> |
|||
<label htmlFor="emailInput" className="form-label"> |
|||
Email Address |
|||
</label> |
|||
<input |
|||
type="email" |
|||
{...(register("email"), { required: true })} |
|||
className="form-control" |
|||
id="emailInput" |
|||
aria-describedby="emailHelp" |
|||
/> |
|||
</div> |
|||
<div className="mb-3"> |
|||
<label htmlFor="pwdInput" className="form-label"> |
|||
Password |
|||
</label> |
|||
<input |
|||
type="password" |
|||
{...register("password")} |
|||
className="form-control" |
|||
id="pwdInput" |
|||
/> |
|||
</div> |
|||
<button type="submit" className="btn btn-primary"> |
|||
Submit |
|||
</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
function handleSubmit() {} |
|||
|
|||
export default LoginPage; |
@ -1,17 +0,0 @@ |
|||
import Navbar from "../components/Navbar"; |
|||
|
|||
function NotFound() { |
|||
return ( |
|||
<div> |
|||
<Navbar /> |
|||
<br /> |
|||
<h1 className="ms-2">Error 404: Page Not Found</h1> |
|||
<br /> |
|||
<div className="ms-2"> |
|||
Use the navbar at the top of the screen to go back to the website. |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
export default NotFound; |
@ -1,31 +0,0 @@ |
|||
import Navbar from "../components/Navbar"; |
|||
import Card from "../components/Card"; |
|||
import leaves from "../assets/leaves.jpg"; |
|||
import coffee from "../assets/coffee.jpg"; |
|||
import desk from "../assets/workspace.jpg"; |
|||
|
|||
function Showcase() { |
|||
const images = [leaves, coffee, desk]; |
|||
const titles = ["Leaf & Book", "Coffee & Notes", "Desk"]; |
|||
const texts = [ |
|||
"A leaf on top of a book.", |
|||
"A cup of coffee paired with a notebook.", |
|||
"A pleasant image of a workspace.", |
|||
]; |
|||
|
|||
return ( |
|||
<div> |
|||
<Navbar /> |
|||
<br /> |
|||
<h1 className="mx-3">Showcase</h1> |
|||
<br /> |
|||
<div className="row"> |
|||
<Card image={images[0]} title={titles[0]} text={texts[0]} /> |
|||
<Card image={images[1]} title={titles[1]} text={texts[1]} /> |
|||
<Card image={images[2]} title={titles[2]} text={texts[2]} /> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
export default Showcase; |
@ -1 +0,0 @@ |
|||
/// <reference types="vite/client" />
|
@ -1,21 +0,0 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "ESNext", |
|||
"useDefineForClassFields": true, |
|||
"lib": ["DOM", "DOM.Iterable", "ESNext"], |
|||
"allowJs": false, |
|||
"skipLibCheck": true, |
|||
"esModuleInterop": false, |
|||
"allowSyntheticDefaultImports": true, |
|||
"strict": true, |
|||
"forceConsistentCasingInFileNames": true, |
|||
"module": "ESNext", |
|||
"moduleResolution": "Node", |
|||
"resolveJsonModule": true, |
|||
"isolatedModules": true, |
|||
"noEmit": true, |
|||
"jsx": "react-jsx" |
|||
}, |
|||
"include": ["src"], |
|||
"references": [{ "path": "./tsconfig.node.json" }] |
|||
} |
@ -1,9 +0,0 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"composite": true, |
|||
"module": "ESNext", |
|||
"moduleResolution": "Node", |
|||
"allowSyntheticDefaultImports": true |
|||
}, |
|||
"include": ["vite.config.ts"] |
|||
} |
@ -1,7 +0,0 @@ |
|||
import { defineConfig } from 'vite' |
|||
import react from '@vitejs/plugin-react' |
|||
|
|||
// https://vitejs.dev/config/
|
|||
export default defineConfig({ |
|||
plugins: [react()], |
|||
}) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue