Skip to content
This repository was archived by the owner on Feb 4, 2024. It is now read-only.

Commit 22e374a

Browse files
committed
Initial commit.
0 parents  commit 22e374a

17 files changed

+4675
-0
lines changed

.editorconfig

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
indent_style = space
8+
indent_size = 2

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Intellij Idea
2+
/.idea/
3+
4+
# Yarn
5+
/node_modules/
6+
/dist/

LICENSE

+674
Large diffs are not rendered by default.

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Kotlin Jobs Builder
2+
3+
```bash
4+
yarn run start
5+
xdg-open ./dist/index.html
6+
```

app/index.html

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8"/>
5+
<meta name="viewport"
6+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7+
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
8+
<title>Kotlin Jobs Builder</title>
9+
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png"/>
10+
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"/>
11+
<link rel="icon" type="image/png" sizes="16x16" href="./favicon-16x16.png"/>
12+
<link rel="icon" href="./favicon.ico"/>
13+
<link rel="manifest" href="./site.webmanifest"/>
14+
<link rel="stylesheet" type="text/css" href="./bulma.min.css"/>
15+
</head>
16+
<body>
17+
<div id="root"></div>
18+
</body>
19+
</html>
3.67 KB
Loading
10 KB
Loading

app/kotlin_jobs/apple-touch-icon.png

3.28 KB
Loading

app/kotlin_jobs/favicon-16x16.png

329 Bytes
Loading

app/kotlin_jobs/favicon-32x32.png

537 Bytes
Loading

app/kotlin_jobs/favicon.ico

15 KB
Binary file not shown.

app/kotlin_jobs/site.webmanifest

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "Kotlin Jobs Builder",
3+
"short_name": "Kotlin Jobs",
4+
"icons": [
5+
{
6+
"src": "/kotlin_jobs/android-chrome-192x192.png",
7+
"sizes": "192x192",
8+
"type": "image/png"
9+
},
10+
{
11+
"src": "/kotlin_jobs/android-chrome-512x512.png",
12+
"sizes": "512x512",
13+
"type": "image/png"
14+
}
15+
],
16+
"theme_color": "#000000",
17+
"background_color": "#ffffff",
18+
"display": "standalone"
19+
}

app/src/main.tsx

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import * as React from "react";
2+
import {useState} from "react";
3+
import * as ReactDOM from "react-dom";
4+
import {
5+
ErrorMessage,
6+
ErrorMessageProps,
7+
Field,
8+
FormikBag,
9+
FormikProps,
10+
withFormik
11+
} from "formik";
12+
13+
declare var _BUILD_TIME: string;
14+
15+
console.log("Build:", new Date(_BUILD_TIME).toLocaleString());
16+
17+
const initialValues: KotlinJobsFormValues = {
18+
title: "",
19+
company: "",
20+
location: "",
21+
occupation: "Full-time",
22+
type: "Office",
23+
salary: "",
24+
contact: "",
25+
description: ""
26+
};
27+
28+
// TypeScript FTW!!!
29+
type FormErrors = Partial<{ -readonly [key in keyof KotlinJobsFormValues]: string }>;
30+
31+
function validate(values: KotlinJobsFormValues): FormErrors {
32+
const errors: FormErrors = {};
33+
if (!values.title) errors.title = "Title is required";
34+
if (!values.location) errors.location = "Location is required";
35+
if (!values.company) errors.company = "Company is required";
36+
if (!values.contact) errors.contact = "Contact is required";
37+
if (!values.description) errors.description = "Description is required";
38+
if (values.description && values.description.length > 1500) errors.description = `The description must be less than 1500 characters. Currently it is ${values.description.length}`;
39+
return errors;
40+
}
41+
42+
function onSubmit(
43+
values: KotlinJobsFormValues,
44+
formikBag: FormikBag<BuildFormProps, KotlinJobsFormValues>
45+
): void {
46+
formikBag.props.callback(`Вакансия: ${values.title}
47+
Локация: ${values.location}
48+
Компания: ${values.company}
49+
Формат работы: ${values.type}
50+
Занятость: ${values.occupation}
51+
Зарплатная вилка: ${values.salary}
52+
53+
${values.description}
54+
55+
Контакт: @${values.contact}
56+
`);
57+
formikBag.setSubmitting(false);
58+
scrollToRendered();
59+
}
60+
61+
function scrollToRendered() {
62+
if (!window.scroll) return;
63+
64+
window.scroll({
65+
top: window.outerHeight,
66+
behavior: "smooth"
67+
});
68+
}
69+
70+
const BuildForm = withFormik<BuildFormProps, KotlinJobsFormValues>({
71+
mapPropsToValues: () => (initialValues),
72+
validate: validate,
73+
handleSubmit: (values, actions) => onSubmit(values, actions),
74+
displayName: "KotlinJobsBuildForm",
75+
})(BuildFormFields);
76+
77+
function BuildFormFields(props: FormikProps<KotlinJobsFormValues>) {
78+
const {
79+
handleSubmit,
80+
isSubmitting,
81+
isValid,
82+
dirty
83+
} = props;
84+
85+
return (
86+
<form onSubmit={handleSubmit}>
87+
<div className="field">
88+
<label className="label">Вакансия *</label>
89+
<div className="control">
90+
<Field component="input" className="input" type="text" name="title" placeholder="Senior Backend Engineer"
91+
autoFocus/>
92+
<ErrorHelper name="title"/>
93+
</div>
94+
</div>
95+
96+
<div className="field">
97+
<label className="label">Локация *</label>
98+
<div className="control">
99+
<Field className="input" type="text" name="location" placeholder="Украина, Киев"/>
100+
<ErrorHelper name="location"/>
101+
</div>
102+
</div>
103+
104+
<div className="field">
105+
<label className="label">Компания *</label>
106+
<div className="control">
107+
<Field className="input" type="text" name="company" placeholder="KoSolutions (https://kosolutions.com)"/>
108+
<ErrorHelper name="company"/>
109+
</div>
110+
</div>
111+
112+
<div className="field">
113+
<label className="label">Формат работы</label>
114+
<div className="control">
115+
<div className="select">
116+
<Field component="select" name="type">
117+
<option value="Office">Office</option>
118+
<option value="Remote">Remote</option>
119+
</Field>
120+
</div>
121+
<ErrorHelper name="type"/>
122+
</div>
123+
</div>
124+
125+
<div className="field">
126+
<label className="label">Занятость</label>
127+
<div className="control">
128+
<div className="select">
129+
<Field component="select" name="occupation">
130+
<option value="Full-time">Full-time</option>
131+
<option value="Part-time">Part-time</option>
132+
<option value="Contract">Contract</option>
133+
</Field>
134+
</div>
135+
<ErrorHelper name="occupation"/>
136+
</div>
137+
</div>
138+
139+
<div className="field">
140+
<label className="label">Зарплатная вилка *</label>
141+
<div className="control">
142+
<Field className="input" type="text" name="salary" placeholder="2000$-3000$"/>
143+
<ErrorHelper name="salary"/>
144+
</div>
145+
</div>
146+
147+
<div className="field">
148+
<label className="label">Ник в Telegram *</label>
149+
<div className="field-body">
150+
<div className="field has-addons">
151+
<p className="control">
152+
<a className="button is-static">
153+
@
154+
</a>
155+
</p>
156+
<p className="control">
157+
<Field className="input" type="text" name="contact" placeholder="Kotlin"/>
158+
<ErrorHelper name="contact"/>
159+
</p>
160+
</div>
161+
</div>
162+
</div>
163+
164+
<div className="field">
165+
<label className="label">Описание *</label>
166+
<div className="control">
167+
<Field component="textarea" name="description" className="textarea" placeholder="Описание в свободной форме"/>
168+
<ErrorHelper name="description"/>
169+
</div>
170+
</div>
171+
172+
<div className="field is-grouped">
173+
<div className="control">
174+
<button type="submit"
175+
className="button is-primary"
176+
disabled={isSubmitting || !isValid || !dirty}>
177+
Сгенерировать
178+
</button>
179+
</div>
180+
</div>
181+
</form>
182+
)
183+
}
184+
185+
function ErrorHelper(props: ErrorMessageProps) {
186+
return (
187+
<ErrorMessage {...props} render={errorMessage =>
188+
<span className="help is-danger">{errorMessage}</span>
189+
}/>
190+
)
191+
}
192+
193+
interface KotlinJobsFormValues {
194+
readonly title: string;
195+
readonly location: string;
196+
readonly company: string;
197+
readonly type: string;
198+
readonly occupation: string;
199+
readonly salary: string;
200+
readonly description: string;
201+
readonly contact: string;
202+
}
203+
204+
interface BuildFormProps {
205+
readonly callback: (text: string) => void;
206+
}
207+
208+
ReactDOM.render(
209+
<Container/>,
210+
document.getElementById("root")
211+
);
212+
213+
function Container() {
214+
const [text, setText] = useState("");
215+
216+
return (
217+
<div className="container">
218+
<div className="section">
219+
<div className="content">
220+
<h1>Kotlin Jobs Builder</h1>
221+
<p>
222+
Пожалуйста заполните все поля формы и
223+
отправьте сгенерирование описание вакансии
224+
<a href="https://t.me/HeapyHop"> @HeapyHop</a> или
225+
<a href="https://t.me/Harmonizr"> @Harmonizr</a>
226+
</p>
227+
</div>
228+
<div className="content">
229+
<BuildForm callback={setText}/>
230+
</div>
231+
<div className="content">
232+
{text && (
233+
<React.Fragment>
234+
<pre>
235+
{text}
236+
</pre>
237+
<p>Скопируйте этот фрагмент и отправьте одному из модераторов</p>
238+
</React.Fragment>
239+
)}
240+
</div>
241+
</div>
242+
</div>
243+
);
244+
}

package.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "kotlin-jobs",
3+
"author": {
4+
"email": "[email protected]",
5+
"name": "Ruslan Ibragimov",
6+
"url": "https://ibragimov.by"
7+
},
8+
"license": "GPL-3.0-only",
9+
"dependencies": {
10+
"@types/react": "^16.8.23",
11+
"@types/react-dom": "^16.8.4",
12+
"bulma": "^0.7.5",
13+
"copy-webpack-plugin": "^5.0.3",
14+
"formik": "^1.5.7",
15+
"html-webpack-plugin": "^3.2.0",
16+
"react": "^16.8.6",
17+
"react-dom": "^16.8.6",
18+
"ts-loader": "^6.0.4",
19+
"typescript": "^3.5.2",
20+
"webpack": "^4.35.2",
21+
"webpack-cli": "^3.3.5"
22+
},
23+
"scripts": {
24+
"prestart": "yarn install",
25+
"prebuild": "yarn install",
26+
"start": "webpack --mode development --watch --env.NODE_ENV development --env.SOURCE_MAP eval-source-map",
27+
"build": "webpack --mode production -p --env.NODE_ENV production --env.SOURCE_MAP source-map"
28+
}
29+
}

tsconfig.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"jsx": "react",
5+
"target": "es5",
6+
"module": "commonjs"
7+
}
8+
}

0 commit comments

Comments
 (0)