added the views funcionality and other things
This commit is contained in:
parent
407b955950
commit
4aafb7e6f9
35
Dockerfile
35
Dockerfile
@ -1,6 +1,37 @@
|
|||||||
# vi: ft=dockerfile
|
# vi: ft=dockerfile
|
||||||
FROM docker.io/nginx
|
FROM docker.io/node:22-alpine3.19 as build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD site .
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM docker.io/nginx:1.27.1-alpine
|
||||||
|
|
||||||
|
RUN apk add openjdk17
|
||||||
|
|
||||||
|
RUN mkdir /app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD api/gradlew .
|
||||||
|
ADD api/gradle gradle
|
||||||
|
|
||||||
|
RUN ./gradlew
|
||||||
|
|
||||||
ADD nginx.proxy.conf /nginx.conf
|
ADD nginx.proxy.conf /nginx.conf
|
||||||
|
|
||||||
CMD ["nginx", "-c", "/nginx.conf", "-g", "daemon off;"]
|
RUN mkdir -p /www/page
|
||||||
|
|
||||||
|
ADD api /app
|
||||||
|
|
||||||
|
RUN ./gradlew bootJar
|
||||||
|
|
||||||
|
ADD entrypoint.sh /usr/bin
|
||||||
|
|
||||||
|
COPY --from=build /app/build/ /www/page/
|
||||||
|
|
||||||
|
ENTRYPOINT ["entrypoint.sh"]
|
||||||
|
@ -33,7 +33,10 @@ dependencies {
|
|||||||
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
|
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
|
||||||
}
|
}
|
||||||
|
|
||||||
//kotlin { compilerOptions { freeCompilerArgs.addAll("-Xjsr305=strict") } compiler { jvm { target = JavaLanguageVersion.of(17) } } }
|
springBoot {
|
||||||
|
mainClass.set("com.andr3h3nriqu3s.applications.ApplicationsApplicationKt")
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs.addAll("-Xjsr305=strict")
|
freeCompilerArgs.addAll("-Xjsr305=strict")
|
||||||
|
@ -7,5 +7,6 @@ import org.springframework.boot.runApplication
|
|||||||
class ApplicationsApplication
|
class ApplicationsApplication
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
runApplication<ApplicationsApplication>(*args)
|
runApplication<ApplicationsApplication>(*args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.andr3h3nriqu3s.applications
|
|||||||
import java.sql.ResultSet
|
import java.sql.ResultSet
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.collections.setOf
|
import kotlin.collections.setOf
|
||||||
|
import kotlin.collections.emptyList
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
@ -32,7 +33,9 @@ data class Application(
|
|||||||
var status: Int,
|
var status: Int,
|
||||||
var company: String,
|
var company: String,
|
||||||
var recruiter: String,
|
var recruiter: String,
|
||||||
|
var message: String,
|
||||||
var flairs: List<Flair>,
|
var flairs: List<Flair>,
|
||||||
|
var views: List<View>,
|
||||||
) {
|
) {
|
||||||
companion object : RowMapper<Application> {
|
companion object : RowMapper<Application> {
|
||||||
override public fun mapRow(rs: ResultSet, rowNum: Int): Application {
|
override public fun mapRow(rs: ResultSet, rowNum: Int): Application {
|
||||||
@ -48,7 +51,9 @@ data class Application(
|
|||||||
rs.getInt("status"),
|
rs.getInt("status"),
|
||||||
rs.getString("company"),
|
rs.getString("company"),
|
||||||
rs.getString("recruiter"),
|
rs.getString("recruiter"),
|
||||||
emptyList()
|
rs.getString("message"),
|
||||||
|
emptyList(),
|
||||||
|
emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +69,7 @@ data class FlairRequest(val id: String, val text: String)
|
|||||||
|
|
||||||
data class UpdateUrl(val id: String, val url: String)
|
data class UpdateUrl(val id: String, val url: String)
|
||||||
|
|
||||||
data class CVData(val company: String, val recruiter: String, val flairs: List<SimpleFlair>)
|
data class CVData(val company: String, val recruiter: String, val message: String, val flairs: List<SimpleFlair>)
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@ -72,24 +77,28 @@ data class CVData(val company: String, val recruiter: String, val flairs: List<
|
|||||||
class ApplicationsController(
|
class ApplicationsController(
|
||||||
val sessionService: SessionService,
|
val sessionService: SessionService,
|
||||||
val applicationService: ApplicationService,
|
val applicationService: ApplicationService,
|
||||||
val flairService: FlairService
|
val flairService: FlairService,
|
||||||
|
val viewService: ViewService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping(path = ["/cv/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
@GetMapping(path = ["/cv/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
public fun getCV(
|
public fun getCV(
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestHeader("token") token: String
|
@RequestHeader("token") token: String?
|
||||||
): CVData? {
|
): CVData? {
|
||||||
print("here!");
|
|
||||||
val user = sessionService.verifyToken(token);
|
val user = sessionService.verifyToken(token);
|
||||||
|
|
||||||
val application = applicationService.findApplicationByIdNoUser(id);
|
val application = applicationService.findApplicationByIdNoUser(id);
|
||||||
|
|
||||||
if (application == null) return null;
|
if (application == null) return null;
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
viewService.create(application.id)
|
||||||
|
}
|
||||||
|
|
||||||
val flairs = application.flairs.map {it.toFlairSimple()};
|
val flairs = application.flairs.map {it.toFlairSimple()};
|
||||||
|
|
||||||
return CVData(application.company, application.recruiter, flairs);
|
return CVData(application.company, application.recruiter, application.message, flairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path = ["/text"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
@PostMapping(path = ["/text"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
@ -204,7 +213,9 @@ class ApplicationsController(
|
|||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
emptyList()
|
"",
|
||||||
|
emptyList(),
|
||||||
|
emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +419,7 @@ class ApplicationsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
class ApplicationService(val db: JdbcTemplate, val flairService: FlairService, val viewService: ViewService) {
|
||||||
|
|
||||||
public fun findApplicationByUrl(user: UserDb, url: String, unique_url: String?): Application? {
|
public fun findApplicationByUrl(user: UserDb, url: String, unique_url: String?): Application? {
|
||||||
if (unique_url != null) {
|
if (unique_url != null) {
|
||||||
@ -456,6 +467,7 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
var application = applications[0]
|
var application = applications[0]
|
||||||
|
|
||||||
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
||||||
|
application.views = viewService.listFromApplicationId(application.id)
|
||||||
|
|
||||||
return application
|
return application
|
||||||
}
|
}
|
||||||
@ -469,6 +481,7 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
|
|
||||||
var application = applications[0]
|
var application = applications[0]
|
||||||
|
|
||||||
|
// Views are not needed for this request
|
||||||
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
||||||
|
|
||||||
return application
|
return application
|
||||||
@ -480,7 +493,7 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db.update(
|
db.update(
|
||||||
"insert into applications (id, url, original_url, unique_url, title, user_id, extra_data, payrange, status, company, recruiter) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
|
"insert into applications (id, url, original_url, unique_url, title, user_id, extra_data, payrange, status, company, recruiter, message) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
|
||||||
application.id,
|
application.id,
|
||||||
application.url,
|
application.url,
|
||||||
application.original_url,
|
application.original_url,
|
||||||
@ -491,7 +504,8 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
application.payrange,
|
application.payrange,
|
||||||
application.status,
|
application.status,
|
||||||
application.company,
|
application.company,
|
||||||
application.recruiter
|
application.recruiter,
|
||||||
|
application.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -517,7 +531,7 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
|
|
||||||
public fun update(application: Application): Application {
|
public fun update(application: Application): Application {
|
||||||
db.update(
|
db.update(
|
||||||
"update applications set url=?, original_url=?, unique_url=?, title=?, user_id=?, extra_data=?, payrange=?, status=?, company=?, recruiter=? where id=?",
|
"update applications set url=?, original_url=?, unique_url=?, title=?, user_id=?, extra_data=?, payrange=?, status=?, company=?, recruiter=?, message=? where id=?",
|
||||||
application.url,
|
application.url,
|
||||||
application.original_url,
|
application.original_url,
|
||||||
application.unique_url,
|
application.unique_url,
|
||||||
@ -528,6 +542,7 @@ class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {
|
|||||||
application.status,
|
application.status,
|
||||||
application.company,
|
application.company,
|
||||||
application.recruiter,
|
application.recruiter,
|
||||||
|
application.message,
|
||||||
application.id,
|
application.id,
|
||||||
)
|
)
|
||||||
return application
|
return application
|
||||||
|
@ -3,19 +3,16 @@ package com.andr3h3nriqu3s.applications
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.jdbc.core.JdbcTemplate
|
import org.springframework.jdbc.core.JdbcTemplate
|
||||||
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice
|
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RequestHeader
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
enum class UserLevel(val level: Int) {
|
enum class UserLevel(val level: Int) {
|
||||||
@ -167,7 +164,10 @@ data class Session(val token: String, val user_id: String)
|
|||||||
@Service
|
@Service
|
||||||
class SessionService(val db: JdbcTemplate) {
|
class SessionService(val db: JdbcTemplate) {
|
||||||
|
|
||||||
fun verifyToken(token: String): UserDb? {
|
fun verifyToken(token: String?): UserDb? {
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var users =
|
var users =
|
||||||
db
|
db
|
||||||
.query(
|
.query(
|
||||||
|
71
api/src/main/kotlin/com/andr3h3nriqu3s/applications/View.kt
Normal file
71
api/src/main/kotlin/com/andr3h3nriqu3s/applications/View.kt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package com.andr3h3nriqu3s.applications
|
||||||
|
|
||||||
|
import java.sql.ResultSet
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.UUID
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate
|
||||||
|
import org.springframework.jdbc.core.RowMapper
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
data class View(var id: String, var application_id: String, var time: Date) {
|
||||||
|
companion object : RowMapper<View> {
|
||||||
|
override public fun mapRow(rs: ResultSet, rowNum: Int): View {
|
||||||
|
return View(
|
||||||
|
rs.getString("id"),
|
||||||
|
rs.getString("application_id"),
|
||||||
|
rs.getDate("time"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ViewService(val db: JdbcTemplate) {
|
||||||
|
|
||||||
|
public fun listFromApplicationId(id: String): List<View> =
|
||||||
|
db.query("select * from views where application_id=?;", arrayOf(id), View).toList()
|
||||||
|
|
||||||
|
public fun getById(id: String): View? {
|
||||||
|
val items = db.query("select * from views where id=?;", arrayOf(id), View).toList()
|
||||||
|
if (items.size == 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun deleteById(id: String): View {
|
||||||
|
val view = this.getById(id)
|
||||||
|
if (view == null) {
|
||||||
|
throw NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
db.update("delete from views where id=?", id)
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun update(view: View): View {
|
||||||
|
db.update(
|
||||||
|
"update views set application_id=?, time=? where id=?;",
|
||||||
|
view.application_id,
|
||||||
|
view.time,
|
||||||
|
view.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun create(application_id: String): View {
|
||||||
|
val id = UUID.randomUUID().toString()
|
||||||
|
|
||||||
|
var new_view = View(id, application_id, Date())
|
||||||
|
|
||||||
|
db.update(
|
||||||
|
"insert into views (id, application_id) values (?, ?)",
|
||||||
|
new_view.id,
|
||||||
|
new_view.application_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_view
|
||||||
|
}
|
||||||
|
}
|
@ -19,18 +19,25 @@ create table if not exists applications (
|
|||||||
company text,
|
company text,
|
||||||
recruiter text,
|
recruiter text,
|
||||||
title text,
|
title text,
|
||||||
|
mesasge text default '',
|
||||||
user_id text,
|
user_id text,
|
||||||
extra_data text,
|
extra_data text,
|
||||||
status integer
|
status integer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table if not exists views (
|
||||||
|
id text primary key,
|
||||||
|
application_id text not null,
|
||||||
|
time timestamp default current_timestamp
|
||||||
|
);
|
||||||
|
|
||||||
create table if not exists flair (
|
create table if not exists flair (
|
||||||
id text primary key,
|
id text primary key,
|
||||||
user_id text not null,
|
user_id text not null,
|
||||||
color text default '#ff0000',
|
color text default '#ff0000',
|
||||||
name text default 'New Flair',
|
name text default 'New Flair',
|
||||||
expr text default 'flair'
|
expr text default 'flair',
|
||||||
description text default '',
|
description text default ''
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists flair_link (
|
create table if not exists flair_link (
|
||||||
|
7
entrypoint.sh
Executable file
7
entrypoint.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd /app
|
||||||
|
./gradlew bootRun &
|
||||||
|
|
||||||
|
nginx -c /nginx.conf -g "daemon off;"
|
||||||
|
|
@ -3,10 +3,11 @@ events {
|
|||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
|
proxy_read_timeout 600;
|
||||||
|
proxy_connect_timeout 600;
|
||||||
|
proxy_send_timeout 600;
|
||||||
|
|
||||||
proxy_read_timeout 600;
|
include mime.types;
|
||||||
proxy_connect_timeout 600;
|
|
||||||
proxy_send_timeout 600;
|
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default upgrade;
|
default upgrade;
|
||||||
@ -19,11 +20,7 @@ http {
|
|||||||
client_max_body_size 5G;
|
client_max_body_size 5G;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_http_version 1.1;
|
root /www/page;
|
||||||
proxy_pass http://localhost:5173;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
@ -34,16 +31,5 @@ http {
|
|||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection $connection_upgrade;
|
proxy_set_header Connection $connection_upgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /glassdoor {
|
|
||||||
|
|
||||||
rewrite ^/glassdoor(.*)$ $1 break;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_pass https://glassdoor.com;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,5 +32,8 @@
|
|||||||
"typescript-eslint": "^8.0.0-alpha.20",
|
"typescript-eslint": "^8.0.0-alpha.20",
|
||||||
"vite": "^5.0.3"
|
"vite": "^5.0.3"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"@sveltejs/adapter-static": "^3.0.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
3241
site/pnpm-lock.yaml
3241
site/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -72,3 +72,19 @@ @layer components {
|
|||||||
@apply bg-white rounded-lg drop-shadow-lg;
|
@apply bg-white rounded-lg drop-shadow-lg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@print {
|
||||||
|
@page :footer {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
@page :header {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@page {
|
||||||
|
size: auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" moznomarginboxes mozdisallowselectionprint>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
@ -22,6 +22,12 @@ export const ApplicationStatusMaping: Record<
|
|||||||
5: 'Expired'
|
5: 'Expired'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type View = {
|
||||||
|
id: string,
|
||||||
|
application_id: string,
|
||||||
|
time: string,
|
||||||
|
}
|
||||||
|
|
||||||
export type Application = {
|
export type Application = {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -35,6 +41,8 @@ export type Application = {
|
|||||||
recruiter: string;
|
recruiter: string;
|
||||||
company: string;
|
company: string;
|
||||||
flairs: Flair[];
|
flairs: Flair[];
|
||||||
|
message: string;
|
||||||
|
views: View[];
|
||||||
};
|
};
|
||||||
|
|
||||||
function createApplicationStore() {
|
function createApplicationStore() {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<div class="flex flex-col h-[100vh]">
|
<div class="flex flex-col h-[100vh]">
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div class="w-full px-4 grow h-full gap-3 flex flex-col">
|
<div class="w-full px-4 grow h-full gap-3 flex flex-col">
|
||||||
<div class="flex h-3/5 gap-3">
|
<div class="flex h-4/5 gap-3">
|
||||||
<ApplicationsList />
|
<ApplicationsList />
|
||||||
<WorkArea />
|
<WorkArea />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { get } from '$lib/utils';
|
import { get } from '$lib/utils';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import Flair from '../flair/Flair.svelte';
|
|
||||||
|
|
||||||
let id: string | undefined | null;
|
let id: string | undefined | null;
|
||||||
|
|
||||||
@ -20,28 +19,35 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Application = {
|
type Application = {
|
||||||
|
recruiter: string;
|
||||||
|
message: string;
|
||||||
|
company: string;
|
||||||
flairs: SimpleFlair[];
|
flairs: SimpleFlair[];
|
||||||
};
|
};
|
||||||
|
|
||||||
let application: Application | undefined = $state(undefined);
|
let application: Application | undefined = $state(undefined);
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
|
if (!id) return;
|
||||||
try {
|
try {
|
||||||
application = await get(`application/cv/${id}`);
|
application = await get(`application/cv/${id}`);
|
||||||
|
|
||||||
application.flairs.sort((a, b) => {
|
if (!application) {
|
||||||
if (a.description && b.description) {
|
return;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
if (a.description) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (b.description) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
|
|
||||||
|
application.flairs.sort((a, b) => {
|
||||||
|
if (a.description && b.description) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a.description) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b.description) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('TODO show this to the user', e);
|
console.log('TODO show this to the user', e);
|
||||||
}
|
}
|
||||||
@ -53,7 +59,7 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="flex items-center w-full flex-col">
|
<div class="flex items-center w-full flex-col">
|
||||||
<div class="py-10 w-[210mm]">
|
<div class="py-10 w-[190mm]">
|
||||||
<div class="bg-white rounded-lg p-3">
|
<div class="bg-white rounded-lg p-3">
|
||||||
<div class="w-full flex">
|
<div class="w-full flex">
|
||||||
<h1 class="text-black text-5xl">Andre Henriques</h1>
|
<h1 class="text-black text-5xl">Andre Henriques</h1>
|
||||||
@ -61,6 +67,13 @@
|
|||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="px-1">
|
<li class="px-1">
|
||||||
|
{#if id}
|
||||||
|
<a class="underline" href="https://www.andr3h3nriqu3s.com/cv?id={id}">andr3h3nriqu3s.com</a>
|
||||||
|
{:else}
|
||||||
|
<a class="underline" href="https://www.andr3h3nriqu3s.com/cv">andr3h3nriqu3s.com</a>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
<li class="px-1">
|
||||||
<a class="underline" href="mailto:contact@andr3h3nriqu3s.com"
|
<a class="underline" href="mailto:contact@andr3h3nriqu3s.com"
|
||||||
>contact@andr3h3nriqu3s.com</a
|
>contact@andr3h3nriqu3s.com</a
|
||||||
>
|
>
|
||||||
@ -88,39 +101,72 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if application}
|
{#if application}
|
||||||
<h2 class="text-white p-3 text-4xl">
|
<h2 class="text-white p-3 text-4xl">
|
||||||
👋 Hello
|
👋 Hello
|
||||||
{#if application.recruiter}
|
{#if application.recruiter}
|
||||||
<span class="font-bold">{application.recruiter}</span> @ <span class="font-bold">{application.company}</span>
|
<span class="font-bold">{application.recruiter}</span> @
|
||||||
{:else if application.company}
|
<span class="font-bold">{application.company}</span>
|
||||||
recruiter @ <span class="font-bold">{application.company}</span>
|
{:else if application.company}
|
||||||
{/if}
|
recruiter @ <span class="font-bold">{application.company}</span>
|
||||||
</h2>
|
{/if}
|
||||||
<div class="p-3 bg-white w-[210mm] rounded-lg">
|
</h2>
|
||||||
<h1>Your Ad / My skills</h1>
|
|
||||||
<div class="flex flex-wrap gap-2 py-2">
|
{#if application.message}
|
||||||
{#each application.flairs as flair}
|
<div class="p-3 bg-white w-[190mm] rounded-lg">
|
||||||
<div class="min-w-0 {flair.description ? 'flex-grow w-full' : ''}">
|
<h1>A small message from me</h1>
|
||||||
<div class="p-2 rounded-lg" style="background: {flair.color};">
|
<div class="py-2">
|
||||||
{flair.name}
|
{@html application.message.split('\n').join('<br>')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-1"></div>
|
||||||
|
{/if}
|
||||||
|
{#if application.flairs.length > 0}
|
||||||
|
<div class="p-3 bg-white w-[190mm] rounded-lg">
|
||||||
|
<h1>Your Ad & My skills</h1>
|
||||||
|
<div class="flex flex-wrap gap-2 py-2">
|
||||||
|
{#each application.flairs as flair}
|
||||||
|
<div class="min-w-0 {flair.description ? 'flex-grow w-full' : ''}">
|
||||||
{#if flair.description}
|
{#if flair.description}
|
||||||
<div class="bg-white my-1 p-1 rounded-md">{flair.description}</div>
|
<div
|
||||||
|
class="p-2 rounded-lg forced-color-adjust-none"
|
||||||
|
style="background: {flair.color};"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="rounded-lg print:p-2 print:inline-block"
|
||||||
|
style="background: {flair.color}; print-color-adjust: exact !important;"
|
||||||
|
>
|
||||||
|
{flair.name}
|
||||||
|
</div>
|
||||||
|
<span class="hidden print:inline">:</span>
|
||||||
|
<div class="bg-white my-1 print:inline p-1 rounded-md">
|
||||||
|
{flair.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="p-2 rounded-lg forced-color-adjust-none"
|
||||||
|
style="background: {flair.color}; print-color-adjust: exact !important;"
|
||||||
|
>
|
||||||
|
{flair.name}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<div class="p-5"></div>
|
<div class="p-2"></div>
|
||||||
<div class="px-5 w-[210mm]">
|
<div class="w-[190mm]">
|
||||||
<h2 class="pb-2 text-4xl font-bold text-white">Work Expericence</h2>
|
<h2 class="pb-2 px-2 print:px-5 text-4xl print:text-3xl font-bold text-white">
|
||||||
|
Work Expericence
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-2"></div>
|
<div class="p-2"></div>
|
||||||
|
|
||||||
<div class="w-[100vw] flex items-center flex-col">
|
<div class="w-[100vw] flex items-center flex-col">
|
||||||
<div class="p-3 bg-white w-[210mm] rounded-lg">
|
<div class="p-3 bg-white w-[190mm] rounded-lg">
|
||||||
<h1>Senior Software Developer @ Planum Solucoes</h1>
|
<h1>Senior Software Developer @ Planum Solucoes</h1>
|
||||||
<div class="ml-5">
|
<div class="ml-5">
|
||||||
<h2>4 year - May 2020 - Present</h2>
|
<h2>4 year - May 2020 - Present</h2>
|
||||||
@ -140,7 +186,7 @@
|
|||||||
<div class="p-2"></div>
|
<div class="p-2"></div>
|
||||||
|
|
||||||
<div class="w-[100vw] flex items-center flex-col">
|
<div class="w-[100vw] flex items-center flex-col">
|
||||||
<div class="text-black w-[210mm] bg-white p-4 rounded-lg">
|
<div class="text-black w-[190mm] bg-white p-4 rounded-lg">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h1>Associate Devops Engineer @ Sky UK</h1>
|
<h1>Associate Devops Engineer @ Sky UK</h1>
|
||||||
@ -176,11 +222,11 @@
|
|||||||
|
|
||||||
<div class="p-5"></div>
|
<div class="p-5"></div>
|
||||||
|
|
||||||
<div class="bg-white p-3 text-black rounded-lg w-[210mm]">
|
<div class="bg-white p-3 text-black rounded-lg w-[190mm]">
|
||||||
<h2 class="pb-2 text-3xl">Education</h2>
|
<h2 class="pb-2 text-3xl">Education</h2>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h1>University of Surrey</h1>
|
<h1>Bachelors of science in Computer Science @ University of Surrey</h1>
|
||||||
<div class="ml-5">
|
<div class="ml-5">
|
||||||
<h2>July 2020 - June 2024</h2>
|
<h2>July 2020 - June 2024</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@ -85,7 +85,9 @@
|
|||||||
Expr: {item.expr}
|
Expr: {item.expr}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
<button class="btn-danger" onclick={preventDefault(() => remove(item.id))}> Remove </button>
|
<button class="btn-danger" onclick={preventDefault(() => remove(item.id))}>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if item.id == edit?.id}
|
{#if item.id == edit?.id}
|
||||||
<form onsubmit={preventDefault(update)} class="shadow-inner p-2 rounded-xl">
|
<form onsubmit={preventDefault(update)} class="shadow-inner p-2 rounded-xl">
|
||||||
@ -103,7 +105,11 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="flabel" for="description">Description</label>
|
<label class="flabel" for="description">Description</label>
|
||||||
<textarea required id="description" class="finput w-full" bind:value={edit.description}
|
<textarea
|
||||||
|
required
|
||||||
|
id="description"
|
||||||
|
class="finput w-full"
|
||||||
|
bind:value={edit.description}
|
||||||
></textarea>
|
></textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="btns w-full">
|
<div class="btns w-full">
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import ExtractTextDialog from './ExtractTextDialog.svelte';
|
import ExtractTextDialog from './ExtractTextDialog.svelte';
|
||||||
import Flair from '../flair/Flair.svelte';
|
import Flair from '../flair/Flair.svelte';
|
||||||
import NewUrlDialog from './NewUrlDialog.svelte';
|
import NewUrlDialog from './NewUrlDialog.svelte';
|
||||||
import DropZone from './DropZone.svelte';
|
import DropZone from './DropZone.svelte';
|
||||||
import { userStore } from '$lib/UserStore.svelte';
|
import { userStore } from '$lib/UserStore.svelte';
|
||||||
|
|
||||||
let activeItem: Application | undefined = $state();
|
let activeItem: Application | undefined = $state();
|
||||||
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
if (item.status === 0) {
|
if (item.status === 0) {
|
||||||
openWindow(item.url);
|
openWindow(item.url);
|
||||||
openCV(item.id);
|
openCV(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -118,8 +118,8 @@
|
|||||||
function setExtData() {
|
function setExtData() {
|
||||||
if (!lastExtData || !activeItem) return;
|
if (!lastExtData || !activeItem) return;
|
||||||
activeItem.title = lastExtData.jobTitle;
|
activeItem.title = lastExtData.jobTitle;
|
||||||
activeItem.company = lastExtData.company;
|
activeItem.company = lastExtData.company;
|
||||||
activeItem.payrange = lastExtData.money;
|
activeItem.payrange = lastExtData.money;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
save();
|
save();
|
||||||
lastExtData = undefined;
|
lastExtData = undefined;
|
||||||
@ -215,23 +215,43 @@
|
|||||||
{statusMapping}
|
{statusMapping}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<fieldset class="grow">
|
<fieldset class="grow">
|
||||||
<label class="flabel" for="title">Company</label>
|
<label class="flabel" for="title">Company</label>
|
||||||
<input class="finput" id="title" bind:value={activeItem.company} onchange={save} />
|
<input
|
||||||
</fieldset>
|
class="finput"
|
||||||
<fieldset class="grow">
|
id="title"
|
||||||
<label class="flabel" for="title">Recruiter</label>
|
bind:value={activeItem.company}
|
||||||
<input class="finput" id="title" bind:value={activeItem.recruiter} onchange={save} />
|
onchange={save}
|
||||||
</fieldset>
|
/>
|
||||||
</div>
|
</fieldset>
|
||||||
|
<fieldset class="grow">
|
||||||
|
<label class="flabel" for="title">Recruiter</label>
|
||||||
|
<input
|
||||||
|
class="finput"
|
||||||
|
id="title"
|
||||||
|
bind:value={activeItem.recruiter}
|
||||||
|
onchange={save}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="flabel" for="title">Title</label>
|
<label class="flabel" for="title">Title</label>
|
||||||
<input class="finput" id="title" bind:value={activeItem.title} onchange={save} />
|
<input
|
||||||
|
class="finput"
|
||||||
|
id="title"
|
||||||
|
bind:value={activeItem.title}
|
||||||
|
onchange={save}
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="flabel" for="payrange">Pay Range</label>
|
<label class="flabel" for="payrange">Pay Range</label>
|
||||||
<input class="finput" id="payrange" bind:value={activeItem.payrange} onchange={save} />
|
<input
|
||||||
|
class="finput"
|
||||||
|
id="payrange"
|
||||||
|
bind:value={activeItem.payrange}
|
||||||
|
onchange={save}
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset
|
<fieldset
|
||||||
draggable="false"
|
draggable="false"
|
||||||
@ -271,9 +291,31 @@
|
|||||||
</div>
|
</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="flabel" for="extra">Extra Info</label>
|
<label class="flabel" for="extra">Extra Info</label>
|
||||||
<textarea class="finput" id="extra" bind:value={activeItem.extra_data} onchange={save}
|
<textarea
|
||||||
|
class="finput"
|
||||||
|
id="extra"
|
||||||
|
bind:value={activeItem.extra_data}
|
||||||
|
onchange={save}
|
||||||
></textarea>
|
></textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label class="flabel" for="extra">Message</label>
|
||||||
|
<textarea
|
||||||
|
draggable={false}
|
||||||
|
class="finput"
|
||||||
|
id="extra"
|
||||||
|
bind:value={activeItem.message}
|
||||||
|
onchange={save}
|
||||||
|
></textarea>
|
||||||
|
</fieldset>
|
||||||
|
{#if activeItem.views.length > 0}
|
||||||
|
<h1 class="text-sm">Non Loggedin Views Time ({activeItem.views.length})</h1>
|
||||||
|
{#each activeItem.views as view}
|
||||||
|
<div>
|
||||||
|
{view.time}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex btns">
|
<div class="flex btns">
|
||||||
<button
|
<button
|
||||||
@ -284,9 +326,7 @@
|
|||||||
>
|
>
|
||||||
Open
|
Open
|
||||||
</button>
|
</button>
|
||||||
<button class="btn-primary" onclick={openCV}>
|
<button class="btn-primary" onclick={() => openCV()}> Open CV </button>
|
||||||
Open CV
|
|
||||||
</button>
|
|
||||||
<button class="btn-primary" onclick={() => extractTokens.showModal()}>
|
<button class="btn-primary" onclick={() => extractTokens.showModal()}>
|
||||||
Extract Flair
|
Extract Flair
|
||||||
</button>
|
</button>
|
||||||
@ -294,7 +334,9 @@
|
|||||||
<button class="btn-primary" onclick={() => setExtData()}> Ext Data </button>
|
<button class="btn-primary" onclick={() => setExtData()}> Ext Data </button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if activeItem.original_url == null}
|
{#if activeItem.original_url == null}
|
||||||
<button class="btn-primary" onclick={() => changeUrl.showModal()}> Update Url </button>
|
<button class="btn-primary" onclick={() => changeUrl.showModal()}>
|
||||||
|
Update Url
|
||||||
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if activeItem.original_url != null}
|
{#if activeItem.original_url != null}
|
||||||
<button class="btn-danger" onclick={resetUrl}> Reset Url </button>
|
<button class="btn-danger" onclick={resetUrl}> Reset Url </button>
|
||||||
@ -303,49 +345,61 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if applicationStore.dragging}
|
{#if applicationStore.dragging}
|
||||||
<div class="flex w-full flex-grow rounded-lg p-1 gap-2">
|
<div class="flex w-full flex-grow rounded-lg p-1 gap-2">
|
||||||
|
|
||||||
<!-- Do nothing -->
|
<!-- Do nothing -->
|
||||||
<DropZone icon="box-arrow-down" ondrop={() => {
|
<DropZone
|
||||||
moveStatus(ApplicationStatus.ToApply);
|
icon="box-arrow-down"
|
||||||
}}>
|
ondrop={() => {
|
||||||
To apply
|
moveStatus(ApplicationStatus.ToApply);
|
||||||
</DropZone>
|
}}
|
||||||
|
>
|
||||||
|
To apply
|
||||||
|
</DropZone>
|
||||||
|
|
||||||
<!-- Ignore -->
|
<!-- Ignore -->
|
||||||
<DropZone icon="trash-fill" ondrop={() => {
|
<DropZone
|
||||||
moveStatus(ApplicationStatus.Ignore);
|
icon="trash-fill"
|
||||||
}}>
|
ondrop={() => {
|
||||||
Ignore it
|
moveStatus(ApplicationStatus.Ignore);
|
||||||
</DropZone>
|
}}
|
||||||
|
>
|
||||||
|
Ignore it
|
||||||
|
</DropZone>
|
||||||
|
|
||||||
<!-- Expired -->
|
<!-- Expired -->
|
||||||
<DropZone icon="clock-fill text-orange-500" ondrop={() => {
|
<DropZone
|
||||||
if (activeItem && activeItem.status === ApplicationStatus.Expired) {
|
icon="clock-fill text-orange-500"
|
||||||
moveStatus(ApplicationStatus.ToApply);
|
ondrop={() => {
|
||||||
} else {
|
if (activeItem && activeItem.status === ApplicationStatus.Expired) {
|
||||||
moveStatus(ApplicationStatus.Expired);
|
moveStatus(ApplicationStatus.ToApply);
|
||||||
}
|
} else {
|
||||||
}}>
|
moveStatus(ApplicationStatus.Expired);
|
||||||
Mark as expired
|
}
|
||||||
</DropZone>
|
}}
|
||||||
|
>
|
||||||
|
Mark as expired
|
||||||
|
</DropZone>
|
||||||
|
|
||||||
<!-- Repeated -->
|
<!-- Repeated -->
|
||||||
<DropZone icon="trash-fill text-danger" ondrop={() => remove()}>
|
<DropZone icon="trash-fill text-danger" ondrop={() => remove()}>Delete it</DropZone>
|
||||||
Delete it
|
|
||||||
</DropZone>
|
|
||||||
|
|
||||||
<!-- Applyed -->
|
<!-- Applyed -->
|
||||||
<DropZone icon="server text-confirm" ondrop={async () => {
|
<DropZone
|
||||||
await moveStatus(ApplicationStatus.Applyed);
|
icon="server text-confirm"
|
||||||
applicationStore.loadAplyed(true);
|
ondrop={async () => {
|
||||||
}}>
|
await moveStatus(ApplicationStatus.Applyed);
|
||||||
Apply
|
applicationStore.loadAplyed(true);
|
||||||
</DropZone>
|
}}
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</DropZone>
|
||||||
|
|
||||||
<!-- Rejected -->
|
<!-- Rejected -->
|
||||||
<DropZone icon="fire text-danger" ondrop={() => moveStatus(ApplicationStatus.ApplyedButSaidNo)}>
|
<DropZone
|
||||||
I was rejeted :(
|
icon="fire text-danger"
|
||||||
</DropZone>
|
ondrop={() => moveStatus(ApplicationStatus.ApplyedButSaidNo)}
|
||||||
|
>
|
||||||
|
I was rejeted :(
|
||||||
|
</DropZone>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.3 KiB |
@ -1,4 +1,4 @@
|
|||||||
import adapter from '@sveltejs/adapter-auto';
|
import adapter from '@sveltejs/adapter-static';
|
||||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
Loading…
Reference in New Issue
Block a user