Skip to content

Commit

Permalink
tailwind usage + persistence feature WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kondaurovDev committed Feb 18, 2025
1 parent 8972459 commit f41d571
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 217 deletions.
4 changes: 2 additions & 2 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ build:
PROJECT=tg-bot-playground vite build
PROJECT=cv-maker vite build

dev:
dev-tg:
PROJECT=tg-bot-playground vite

dev-cv-maker:
dev-cv:
PROJECT=cv-maker vite

gen-openapi-ui:
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
"@edge-runtime/vm": "^5.0.0",
"@effect-ak/tg-bot-client": "^0.5.1",
"@monaco-editor/loader": "^1.4.0",
"@preact/preset-vite": "^2.10.1",
"@tailwindcss/vite": "^4.0.6",
"@types/alpinejs": "^3.13.11",
"@types/node": "^22.10.5",
"alpinejs": "^3.14.8",
"effect": "^3.12.6",
"monaco-editor": "^0.52.2",
"vite": "^6.0.11",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.3",
"preact": "^10.25.4",
"preact-render-to-string": "^6.5.13",
"prettier": "^3.4.2",
"puppeteer": "^24.0.0",
"@preact/preset-vite": "^2.10.1"
"tailwindcss": "^4.0.6",
"vite": "^6.0.11",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.3"
}
}
334 changes: 314 additions & 20 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/common/editor/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ export const createAndBindEditor = async (
model: textModel,
contextmenu: false,
minimap: {
enabled: false
enabled: false,
},
lineNumbers: "off"
});

return editor;
Expand Down
1 change: 1 addition & 0 deletions src/common/editor/make.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const makeJsonEditor = async () => {
if (!editor) return;

return {
editor,
monaco,
textModel: model
} as const;
Expand Down
10 changes: 5 additions & 5 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ export const fetchText = (path: string) =>
fetch(path).then(_ => _.text());

export const getMonacoLoader = () => {
if (!("monaco_loader" in window) || typeof window.monaco_loader != "object" || window.monaco_loader == null) {
console.warn("monaco loader is not available");
return;
}
if (!("monaco_loader" in window) || typeof window.monaco_loader != "object" || window.monaco_loader == null) {
console.warn("monaco loader is not available");
return;
}

return window.monaco_loader as MonacoLoader;
return window.monaco_loader as MonacoLoader;

}
50 changes: 26 additions & 24 deletions src/cv-maker/core/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function Resume(resume: ResumeObject) {
<div className="section-header">
<span id="label">Why I'm the Right Choice for "{coverLetter.position}"</span>
</div>
<div id="summary">
<div className="p-3 bg-so">
{coverLetter.content.map(line => <p dangerouslySetInnerHTML={{ __html: line }}></p>)}
</div>

Expand All @@ -24,7 +24,7 @@ export function Resume(resume: ResumeObject) {
<span id="label">Summary</span>
</div>

<div id="summary">
<div className="bg-so p-3">
{resume.me.expertSummary.map(s =>
<p dangerouslySetInnerHTML={{ __html: s }}></p>
)}
Expand All @@ -34,23 +34,25 @@ export function Resume(resume: ResumeObject) {
<span id="label">Skills</span>
</div>

<div id="skills">
<div className="flex flex-wrap gap-2">

{Object.entries(getSkills(resume)).map(([category, group]) =>
<div className="skill-group">
<span>{category}</span>
<span className="uppercase font-light">{category}</span>
<div className="group-list">
{group.map(t =>
<span className="stack-item">{t.technology.name}</span>
<span
className="bg-so p-1 text-sm ml-1 mt-1"
>{t.technology.name}</span>
)}
</div>
</div>
)}

</div>

<div className="section-header">
<span id="label">Employment</span>
<div className="section-header pt-1">
<span id="label">Employment history</span>
</div>

<div id="employment">
Expand All @@ -64,9 +66,10 @@ export function Resume(resume: ResumeObject) {

function Headline(resume: ResumeObject) {
return (
<span>
Software Engineer. <span id='expertise'>Expertise: {resume.me?.expertise.join("/")}</span>
</span>
<div className="flex gap-2 items-baseline">
<span className="text-2xl font-thin">Software Engineer</span>
<span className="text-base">Expertise: {resume.me?.expertise.join("/")}</span>
</div>
)
}

Expand Down Expand Up @@ -95,18 +98,18 @@ function ProjectStack(project: ProjectDetails) {

function ResumeHead(resume: ResumeObject) {
return (
<div id="head">
<div id="name">{resume.me.name}</div>
<div id="role">{Headline(resume)}</div>
<div id="head" className="pb-6">
<div className="text-5xl font-thin">{resume.me.name}</div>
<div className="text-lg font-light">{Headline(resume)}</div>
<div id="location">{resume.me?.location}</div>
<div id="contact">
<div id="email">
<div className="flex gap-1 text-sky-600 text-sm">
<div>
<span className="fa-regular fa-envelope"></span>
<a
href={`mailto:${resume.me.email}`}
> {resume.me.email}</a>
</div>
<div id="phone">
<div>
<span className="fa-solid fa-mobile-screen-button"></span>
<a
href={`tel:${resume.me.phone}`}
Expand All @@ -132,7 +135,7 @@ function ResumeHead(resume: ResumeObject) {

function CompanyProject(project: ProjectDetails) {
return (
<div className="project">
<div className="project no-break">
<div style={{ display: "flex" }}>
<div style={{ marginBottom: "3px" }}>
<b>Project: </b>
Expand All @@ -146,11 +149,9 @@ function CompanyProject(project: ProjectDetails) {
<span
style={{ display: "block" }}
><b>Stack: </b>{ProjectStack(project)}</span>
<ul>
<ul className="list-disc pt-2 pl-10">
{project.achivements.map(achivement =>
<div>
<li>{achivement.human ?? achivement.technical}</li>
</div>
<li>{achivement.human ?? achivement.technical}</li>
)}
</ul>
</div>
Expand All @@ -162,8 +163,10 @@ function EmploymentHistory(resume: ResumeObject) {
<div id="employment">
{resume.employmentHistory.map(company => {

const projects = [...company.projects].sort((a,b) => a.order - b.order).map(CompanyProject)

return (
<div className="company">
<div className="company border-b-1 border-gray-300 mb-2 pb-1">
<div style={{ "display": "flex" }}>
<span>{CompanyHeader(company)}</span>
<span
Expand All @@ -173,8 +176,7 @@ function EmploymentHistory(resume: ResumeObject) {
<span
style={{ display: "block", marginBottom: "5px" }}
>{CompanySubHeader(company)}</span>
{company.projects.map(CompanyProject)}
<hr />
{projects}
</div>
)
})}
Expand Down
12 changes: 12 additions & 0 deletions src/cv-maker/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,15 @@ export const parseJSON = (input: string | undefined) => {
return JSON.parse(input);
} catch (error) {}
}

export function debounce<T extends (...args: unknown[]) => void>(
func: T, wait: number
) {
let timeout: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
func(...args);
}, wait);
};
}
96 changes: 63 additions & 33 deletions src/cv-maker/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,97 @@

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
integrity="sha512-NhSC1YmyruXifcj/KFRWoC561YpHpc5Jtzgvbuzx5VozKpWvQ+4nXhPdFgmx8xqexRcpAglTj9sIBWINXa8x5w=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<script defer src="https://cdn.jsdelivr.net/npm/@monaco-editor/[email protected]/lib/umd/monaco-loader.min.js"></script>

<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>✍️</text></svg>">
<link rel="stylesheet" href="./style.css">
<script defer src="./main.ts" type="module"></script>
<title>CV Maker</title>
</head>

<body x-data style="display: flex; flex-direction: column;">
<body x-data="state" class="flex flex-col p-16 pt-8">
<!-- navigation -->
<div
class="no-print pb-2 self-center"
x-init="$watch('mode', value => $dispatch('mode-was-changed', value))"
>

<div style="padding-bottom: 20px;" class="no-print">
<label class="menu-item" :class="{ 'active': $store.state.mode === 'editor' }">
<input type="radio" value="editor" x-model="$store.state.mode" /> Editor
<label class="menu-item" :class="{ 'active': mode === 'editor' }">
<input type="radio" value="editor" x-model="mode" /> Editor
</label>

<label
class="menu-item"
:class="{ 'active': $store.state.mode === 'view' }"
x-show="!$store.state.editorHasError" x-transition
:class="{ 'active': mode === 'view' }"
x-show="!editorHasError" x-transition
>
<input
type="radio"
value="view"
x-model="$store.state.mode"
x-model="mode"
/> View
</label>

<select x-model="currentResume" @change="selectResume">
<template x-for="resume in availableResumes" :key="resume.id">
<option :value="resume.id" x-text="resume.name"></option>
</template>
</select>

</div>

<!-- code editor -->
<div x-show="$store.state.mode == 'editor'">
<div x-data="sections">
<template x-for="mode in sections">
<label
class="menu-item"
:class="{ 'active': $store.state.editorSection === mode.id }"
>
<input
type="radio"
:value="mode.id"
x-model="$store.state.editorSection"
@change="$dispatch('section-changed')"
/>
<span x-text="mode.label"></span>
</label>
</template>
<!-- code editor and code section -->
<div
class="flex gap-2 pb-2"
x-show="mode == 'editor'"
>
<div class="self-start">
<button
class="btn bg-sky-500 hover:bg-sky-700"
x-on:click="$dispatch('resize')"
>Save as</button>
<button
class="btn bg-[#DC382D] hover:bg-[#B93224]"
>Delete</button>
</div>

<div
class="self-end ml-auto"
>
<select
class="block bg-white border border-gray-300 py-2 px-3 rounded focus:outline-none focus:ring-0 focus:border-blue-300"
@change="$dispatch('section-changed')"
x-model="editorSection"
>
<template x-for="section in $store.sections" :key="section.id">
<option
:value="section.id"
:selected="editorSection === section.id"
x-text="section.label"
></option>
</template>
</select>
</div>

<div
id="code-editor"
></div>
</div>

<div x-show="$store.state.mode == 'view'">
<div x-html="$store.state.resumeHtml"></div>
<div
class="w-full h-full"
x-show="mode == 'editor'"
id="code-editor"
></div>

<div
class="self-center"
x-show="mode == 'view'"
>
<div
x-html="resumeHtml"
></div>
</div>

</body>
Expand Down
Loading

0 comments on commit f41d571

Please sign in to comment.