Compare commits

...

5 Commits

Author SHA1 Message Date
aminecmi
695746a507 Drone setup.
Some checks failed
continuous-integration/drone/push Build is failing
2022-09-29 15:09:17 +02:00
aminecmi
0d90e4c166 Fix. 2022-09-29 14:54:56 +02:00
aminecmi
ebed0c208c Things will build. 2022-09-29 14:54:11 +02:00
aminecmi
42b7167eec Final style. 2022-09-29 14:53:02 +02:00
aminecmi
5f1b0016f2 Initialize project using Create React App 2022-09-29 14:52:20 +02:00
43 changed files with 29173 additions and 1 deletions

107
.drone.yml Normal file
View File

@ -0,0 +1,107 @@
kind: pipeline
type: docker
name: analyseAndBuild
steps:
- name: code-analysis
image: sonarsource/sonar-scanner-cli
detach: true
failure: ignore
commands:
- sonar-scanner -Dsonar.projectKey=cv -Dsonar.sources=. -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN
environment:
SONAR_HOST_URL:
from_secret: sonarScannerHostUrl
SONAR_LOGIN:
from_secret: sonarScannerLogin
- name: lintAndBuild
image: node:14
commands:
- npm install
- npm run lint
- npm run build:prod
environment:
VUE_APP_API_BASE_URL:
from_secret: baseurl
- name: saveNodeModules
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
rebuild: true
mount:
- ./node_modules
cache_key: [ DRONE_REPO_NAME, DRONE_BRANCH ]
trigger:
event:
- push
- pull_request
volumes:
- name: cache
host:
path: /tmp/cache
---
kind: pipeline
type: docker
name: Deploy
steps:
- name: getNodeModules
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
restore: true
mount:
- ./node_modules
cache_key: [ DRONE_REPO_NAME, DRONE_BRANCH ]
- name: build
image: node:14
commands:
- npm run build:prod
environment:
VUE_APP_API_BASE_URL:
from_secret: baseurl
- name: scpFiles
image: appleboy/drone-scp
settings:
host: amine-louveau.fr
username: ubuntu
key:
from_secret: privateKey
port: 22
target: /home/ubuntu/courses
source: dist/*
- name: deploy
image: appleboy/drone-ssh
settings:
host: amine-louveau.fr
user: ubuntu
key:
from_secret: privateKey
command_timeout: 2m
script:
- cd /home/ubuntu/courses
- mv dist/* ./
- sudo chown www-data:www-data ./*
- sudo rm -rf /var/www/amine/courses/*
- sudo mv ./* /var/www/amine/courses/
trigger:
event:
- promote
target:
- production
volumes:
- name: cache
host:
path: /tmp/cache

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

5
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

13
.idea/cv.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/dictionaries Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectDictionaryState">
<dictionary name="amine" />
</component>
</project>

6
.idea/encodings.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/cv.iml" filepath="$PROJECT_DIR$/.idea/cv.iml" />
</modules>
</component>
</project>

View File

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

4
.idea/watcherTasks.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Sass;SCSS" />
</project>

0
.scannerwork/.sonar_lock Normal file
View File

View File

@ -0,0 +1,6 @@
projectKey=cv
serverUrl=http://18.0.0.7:9000
serverVersion=9.6.0.59041
dashboardUrl=http://18.0.0.7:9000/dashboard?id=cv
ceTaskId=AYOJWrb2HpE7RRVya8HT
ceTaskUrl=http://18.0.0.7:9000/api/ce/task?id=AYOJWrb2HpE7RRVya8HT

View File

@ -1,3 +1,3 @@
# cv
A CV template, inspired by https://techrez.io/resume/andrew-lu
A CV template, inspired by https://techrez.io/resume/andrew-lu

28477
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

54
package.json Normal file
View File

@ -0,0 +1,54 @@
{
"name": "cv",
"version": "0.1.0",
"private": true,
"dependencies": {
"@headlessui/react": "^1.7.2",
"@heroicons/react": "^2.0.11",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/lodash": "^4.14.186",
"@types/node": "^16.11.62",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.8.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.7",
"autoprefixer": "^10.4.12",
"postcss": "^8.4.16",
"sass": "^1.55.0",
"tailwindcss": "^3.1.8"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

27
public/index.html Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

25
src/Assets/scss/CV.scss Normal file
View File

@ -0,0 +1,25 @@
.card {
@apply container p-3 bg-gray-200 rounded-md mt-8 mb-8 drop-shadow;
}
.icon {
@apply w-5 inline-block mr-2;
}
.tag {
@apply mb-2 rounded-full p-3 pt-1 pb-1 text-white shadow mr-1 prose prose-sm;
}
.details {
& > * {
padding: 20px 0;
}
& > *:first-child {
padding-top: 0;
}
& > *:last-child {
padding-bottom: 0;
}
}

71
src/CV.tsx Normal file
View File

@ -0,0 +1,71 @@
import React, {useEffect} from 'react';
import Header from './Components/Header';
import './Assets/scss/CV.scss'
import Jobs from './Components/Jobs';
import Education from './Components/Education';
import SideProjects from './Components/SideProjects';
import Languages from './Components/Languages';
import Interests from './Components/Interests';
import Skills from './Components/Skills';
import {Tag, TagCategory} from './Model/tag';
import {Lang} from './Model/lang';
import {EducationYear} from './Model/education';
import {SideProjectCats} from './Model/side-project';
import {JobAtCompany} from './Model/job-at-company';
import {Interest} from './Model/interests';
import {Profile} from './Model/profile';
function CV() {
const tags: Tag[] = []
const langs: Lang[] = []
const ed: EducationYear[] = []
const projs: SideProjectCats[] = []
const jobs: JobAtCompany[] = []
const interests: Interest[] = []
const profile: Profile = {
"name": "",
"title": "",
"quote": "",
"picture": ""
}
document.title = profile.name;
useEffect(() => {
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
// @ts-ignore
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
}
// @ts-ignore
link.href = profile.picture
}, []);
return (
<div className="CV">
<Header tags={tags} profile={profile}/>
<div className="card flex divide-x divide-gray-600">
<div className="basis-3/4 pr-2 divide-y divide-gray-400 details">
<Jobs jobs={jobs}/>
<Education education={ed}/>
<SideProjects projs={projs}/>
</div>
<div className="basis-1/4 pl-2 divide-y divide-gray-400 details">
<Skills tags={tags}/>
<Languages langs={langs}/>
<Interests interests={interests}/>
</div>
</div>
</div>
);
}
export default CV;

View File

@ -0,0 +1,17 @@
import React, {ComponentProps} from 'react';
import {AcademicCapIcon, BriefcaseIcon} from '@heroicons/react/24/outline';
import {EducationYear} from '../Model/education';
function Education(props: ComponentProps<any>) {
return (<div className="prose max-w-none">
<h3><AcademicCapIcon className="icon"/> Formations</h3>
{props.education.map((e: EducationYear) => <div key={e.endDate} className="mb-5">
<div className="flex justify-between w-full"><h4 className="m-0">{e.school}</h4>
<div>{e.endDate}</div>
</div>
<div>{e.name}</div>
</div>)}
</div>);
}
export default Education;

43
src/Components/Header.tsx Normal file
View File

@ -0,0 +1,43 @@
import React, {ComponentProps} from 'react';
import {CodeBracketIcon, EnvelopeIcon, PhoneIcon, UserPlusIcon} from '@heroicons/react/24/outline';
import {getTagColor, Tag} from '../Model/tag';
import {Profile} from '../Model/profile';
function Header(props: ComponentProps<any>) {
const tags = props.tags.filter((t: Tag) => t.workThing)
const profile: Profile = props.profile
return (<div
className="card flex flex-wrap-reverse">
<div className="basis-1/4">
<img className="w-60 rounded-full shadow-lg lg:divide-x divide-gray-600" src={profile.picture}
/>
</div>
<div className="basis-2/4 prose p-3 prose-sm grow">
<h1>{profile.name}</h1>
<h2>{profile.title}</h2>
<blockquote>{profile.quote}</blockquote>
<div className="prose flex flex-wrap justify-between"> {/* Tags */}
{tags.map((t: Tag) => <button key={t.name} className={"tag " + getTagColor(t)}>{t.name}</button>)}
</div>
</div>
<div className="basis-1/4 lg:divide-y divide-gray-400 prose p-3 grow">
<div className="text-center lg:text-left">
<h3 className="mt-2 mb-1">Me contacter</h3>
<div><EnvelopeIcon className="icon"/><a className="" href={"mailto:" + profile.email}>Par
mail</a></div>
<div><PhoneIcon className="icon"/><a className="" href={"tel:" + profile.phone}>Par
téléphone</a></div>
<h3 className="mt-2 mb-1">Me connaitre</h3>
<div><UserPlusIcon className="icon"/><a className=""
href={profile.linkedin}
target="_blank">Mon linkedin</a></div>
<div><CodeBracketIcon className="icon"/><a className=""
href={profile.source} target="_blank">Mon
code</a></div>
</div>
</div>
</div>);
}
export default Header;

View File

@ -0,0 +1,14 @@
import React, {ComponentProps} from 'react';
import {CalendarDaysIcon, FaceSmileIcon, LanguageIcon} from '@heroicons/react/24/outline';
import {Interest} from '../Model/interests';
function Interests(props: ComponentProps<any>) {
return (<div className="prose">
<h3><FaceSmileIcon className="icon"/> Centre d'intérets</h3>
<ul>
{props.interests.map((i: Interest) => <li key={i.theme}><strong>{i.theme}</strong> : {i.description}</li>)}
</ul>
</div>);
}
export default Interests;

16
src/Components/Job.tsx Normal file
View File

@ -0,0 +1,16 @@
import React, {ComponentProps} from 'react';
import {BriefcaseIcon} from '@heroicons/react/24/outline';
function Job(props: ComponentProps<any>) {
return (<div className="mb-5">
<div className="flex justify-between w-full"><h4 className="m-0">{props.jobTitle}</h4>
<div>{props.start} - {props.end}</div>
</div>
<h5>{props.company}</h5>
<ul>
{props.tasks.map((task: string) => <li key={task}>{task}</li>)}
</ul>
</div>);
}
export default Job;

14
src/Components/Jobs.tsx Normal file
View File

@ -0,0 +1,14 @@
import React, {ComponentProps} from 'react';
import {BriefcaseIcon} from '@heroicons/react/24/outline';
import Job from './Job';
import {JobAtCompany} from '../Model/job-at-company';
function Jobs(props: ComponentProps<any>) {
return (<div className="prose max-w-none">
<h3><BriefcaseIcon className="icon"/> Expériences professionnelles</h3>
{props.jobs.map((j: JobAtCompany) => <Job jobTitle={j.jobTitle} start={j.start} end={j.end} company={j.company} tasks={j.tasks} key={j.start}></Job>)}
</div>);
}
export default Jobs;

View File

@ -0,0 +1,14 @@
import React, {ComponentProps} from 'react';
import {CalendarDaysIcon, LanguageIcon} from '@heroicons/react/24/outline';
import {Lang} from '../Model/lang';
function Languages(props: ComponentProps<any>) {
return (<div className="prose">
<h3><LanguageIcon className="icon"/> Langues</h3>
<ul>
{props.langs.map((l: Lang) => <li key={l.lang}>{l.lang} : {l.level}</li>)}
</ul>
</div>);
}
export default Languages;

View File

@ -0,0 +1,23 @@
import React, {ComponentProps} from 'react';
import {BeakerIcon} from '@heroicons/react/24/outline';
import {SideProjectCats} from '../Model/side-project';
function SideProjects(props: ComponentProps<any>) {
return (<div className="prose">
<h3><BeakerIcon className="icon"/> Projets personnels</h3>
{props.projs.map((proj: SideProjectCats) => {
return (<div key={proj.category}>
<h4>{proj.category}</h4>
<ul>
{proj.projects.map(p => {
return (<li key={p.description}>
{p.title ? <div><a href={p.url}>{p.title}</a> {p.description}</div> : <div>{p.description}</div>}
</li>)
})}
</ul>
</div>)
})}
</div>);
}
export default SideProjects;

23
src/Components/Skills.tsx Normal file
View File

@ -0,0 +1,23 @@
import React, {ComponentProps} from 'react';
import {CalendarDaysIcon, CheckBadgeIcon, LanguageIcon} from '@heroicons/react/24/outline';
import {getTagColor, Tag, TagCategory} from '../Model/tag';
import {forIn, groupBy, map} from 'lodash';
function Skills(props: ComponentProps<any>) {
const tagsByCat = groupBy(props.tags, 'category')
return (<div className="prose">
<h3><CheckBadgeIcon className="icon"/> Compétences</h3>
<div className="flex flex-wrap justify-between">
{map(tagsByCat, (tags, cat) => {
return (<div key={cat}>
<h4 className="m-0 mb-1.5">{cat}</h4>
<div>
{tags.map((t: Tag) => <button key={t.name} className={"tag " + getTagColor(t)}>{t.name}</button>)}
</div>
</div>)
})}
</div>
</div>);
}
export default Skills;

5
src/Model/education.ts Normal file
View File

@ -0,0 +1,5 @@
export class EducationYear {
endDate!: string
school!: string
name!: string
}

4
src/Model/interests.ts Normal file
View File

@ -0,0 +1,4 @@
export class Interest {
theme!: string
description!: string
}

View File

@ -0,0 +1,7 @@
export class JobAtCompany {
jobTitle!: string
start!: string
end!: string
company!: string
tasks!: string[]
}

4
src/Model/lang.ts Normal file
View File

@ -0,0 +1,4 @@
export class Lang {
lang!: string
level!: string
}

10
src/Model/profile.ts Normal file
View File

@ -0,0 +1,10 @@
export class Profile {
name!: string
title!: string
quote!: string
picture!: string
email?: string
phone?: string
linkedin?: string
source?: string
}

10
src/Model/side-project.ts Normal file
View File

@ -0,0 +1,10 @@
export class SideProject {
title?: string
url?: string
description!: string
}
export class SideProjectCats {
category!: string
projects!: SideProject[];
}

29
src/Model/tag.ts Normal file
View File

@ -0,0 +1,29 @@
export function getTagColor(t: Tag) {
switch (t.category) {
case TagCategory.FRONT:
return "bg-rose-500"
case TagCategory.BACK:
return "bg-indigo-600"
case TagCategory.MOBILE:
return "bg-yellow-500"
case TagCategory.DB:
return "bg-emerald-500"
case TagCategory.OTHER:
return "bg-stone-300"
}
};
export enum TagCategory {
FRONT = 'Developpement Front',
BACK = 'Developpement Back',
MOBILE = 'Developpement Mobile',
DB = 'Bases de données',
OTHER = 'Autre'
}
export class Tag {
name: string = ""
workThing: Boolean = true
category!: TagCategory
}

3
src/index.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

19
src/index.tsx Normal file
View File

@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import CV from './CV';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<CV />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
src/react-app-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

15
src/reportWebVitals.ts Normal file
View File

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.ts Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

20
tailwind.config.js Normal file
View File

@ -0,0 +1,20 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
container: {
center: true,
},
screens: {
sm: '640px',
md: '768px',
lg: '1024px'
},
},
plugins: [
require('@tailwindcss/typography')
],
}

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}