parent
62961363f3
commit
f257bce4b0
@ -15,8 +15,8 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
|
|||||||
repositories { mavenCentral() }
|
repositories { mavenCentral() }
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.postgresql:postgresql")
|
implementation("org.postgresql:postgresql")
|
||||||
implementation("org.springframework.security:spring-security-crypto:6.0.3")
|
implementation("org.springframework.security:spring-security-crypto:6.0.3")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-mustache")
|
implementation("org.springframework.boot:spring-boot-starter-mustache")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
@ -32,20 +32,14 @@ dependencies {
|
|||||||
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
|
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
|
||||||
}
|
}
|
||||||
|
|
||||||
springBoot {
|
springBoot { mainClass.set("com.andr3h3nriqu3s.applications.ApplicationsApplicationKt") }
|
||||||
mainClass.set("com.andr3h3nriqu3s.applications.ApplicationsApplicationKt")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions { freeCompilerArgs.addAll("-Xjsr305=strict") }
|
||||||
freeCompilerArgs.addAll("-Xjsr305=strict")
|
|
||||||
}
|
|
||||||
|
|
||||||
jvmToolchain(17)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> { useJUnitPlatform() }
|
tasks.withType<Test> { useJUnitPlatform() }
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> { useJUnitPlatform() }
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
@ -42,6 +42,7 @@ data class Application(
|
|||||||
var create_time: String,
|
var create_time: String,
|
||||||
var flairs: List<Flair>,
|
var flairs: List<Flair>,
|
||||||
var views: List<View>,
|
var views: List<View>,
|
||||||
|
var events: List<Event>,
|
||||||
) {
|
) {
|
||||||
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 {
|
||||||
@ -64,6 +65,7 @@ data class Application(
|
|||||||
rs.getString("create_time"),
|
rs.getString("create_time"),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
|
emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,6 +96,7 @@ class ApplicationsController(
|
|||||||
val applicationService: ApplicationService,
|
val applicationService: ApplicationService,
|
||||||
val flairService: FlairService,
|
val flairService: FlairService,
|
||||||
val viewService: ViewService,
|
val viewService: ViewService,
|
||||||
|
val eventService: EventService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping(path = ["/cv/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
@GetMapping(path = ["/cv/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
@ -106,6 +109,7 @@ class ApplicationsController(
|
|||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
viewService.create(application.id)
|
viewService.create(application.id)
|
||||||
|
eventService.create(application.id, EventType.View)
|
||||||
}
|
}
|
||||||
|
|
||||||
val flairs = application.flairs.map { it.toFlairSimple() }
|
val flairs = application.flairs.map { it.toFlairSimple() }
|
||||||
@ -141,6 +145,7 @@ class ApplicationsController(
|
|||||||
"",
|
"",
|
||||||
emptyList(),
|
emptyList(),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
|
emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!applicationService.createApplication(user, application)) {
|
if (!applicationService.createApplication(user, application)) {
|
||||||
@ -268,6 +273,7 @@ class ApplicationsController(
|
|||||||
"",
|
"",
|
||||||
emptyList(),
|
emptyList(),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
|
emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,21 +317,13 @@ class ApplicationsController(
|
|||||||
throw NotFound()
|
throw NotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (application.status == info.status) {
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
application.status = info.status
|
application.status = info.status
|
||||||
val status_string = "${info.status}"
|
|
||||||
var status_history = application.status_history.split(",").filter { it.length >= 1 }
|
|
||||||
if (status_history.indexOf(status_string) == -1) {
|
|
||||||
status_history = status_history.plus("${info.status}")
|
|
||||||
}
|
|
||||||
|
|
||||||
application.status_history = status_history.joinToString(",") { it }
|
applicationService.updateStatus(application)
|
||||||
|
|
||||||
if (info.status == 4) {
|
|
||||||
val sdf = SimpleDateFormat("dd/MM/yyyy hh:mm:ss")
|
|
||||||
application.application_time = sdf.format(Date())
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationService.update(application)
|
|
||||||
|
|
||||||
return application
|
return application
|
||||||
}
|
}
|
||||||
@ -460,7 +458,8 @@ class ApplicationsController(
|
|||||||
class ApplicationService(
|
class ApplicationService(
|
||||||
val db: JdbcTemplate,
|
val db: JdbcTemplate,
|
||||||
val flairService: FlairService,
|
val flairService: FlairService,
|
||||||
val viewService: ViewService
|
val viewService: ViewService,
|
||||||
|
val eventService: EventService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public fun findApplicationByUrl(user: UserDb, url: String, unique_url: String?): Application? {
|
public fun findApplicationByUrl(user: UserDb, url: String, unique_url: String?): Application? {
|
||||||
@ -510,6 +509,7 @@ class ApplicationService(
|
|||||||
|
|
||||||
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
||||||
application.views = viewService.listFromApplicationId(application.id)
|
application.views = viewService.listFromApplicationId(application.id)
|
||||||
|
application.events = eventService.listFromApplicationId(application.id).toList()
|
||||||
|
|
||||||
return application
|
return application
|
||||||
}
|
}
|
||||||
@ -524,7 +524,7 @@ class ApplicationService(
|
|||||||
|
|
||||||
var application = applications[0]
|
var application = applications[0]
|
||||||
|
|
||||||
// Views are not needed for this request
|
// Views / Events are not needed for this request
|
||||||
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
application.flairs = flairService.listFromLinkApplicationId(application.id)
|
||||||
|
|
||||||
return application
|
return application
|
||||||
@ -555,6 +555,8 @@ class ApplicationService(
|
|||||||
application.application_time,
|
application.application_time,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
eventService.create(application.id, EventType.Creation)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,10 +596,39 @@ class ApplicationService(
|
|||||||
return iter.toList()
|
return iter.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the stauts on the application object before giving it to this function
|
||||||
|
public fun updateStatus(application: Application): Application {
|
||||||
|
|
||||||
|
val status_string = "${application.status}"
|
||||||
|
var status_history = application.status_history.split(",").filter { it.length >= 1 }
|
||||||
|
if (status_history.indexOf(status_string) == -1) {
|
||||||
|
status_history = status_history.plus(status_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
application.status_history = status_history.joinToString(",") { it }
|
||||||
|
|
||||||
|
if (application.status == 4) {
|
||||||
|
val sdf = SimpleDateFormat("dd/MM/yyyy hh:mm:ss")
|
||||||
|
application.application_time = sdf.format(Date())
|
||||||
|
}
|
||||||
|
|
||||||
|
eventService.create(application.id, EventType.StatusUpdate, application.status)
|
||||||
|
|
||||||
|
db.update(
|
||||||
|
"update applications set status=?, status_history=?, application_time=? where id=?",
|
||||||
|
application.status,
|
||||||
|
application.status_history,
|
||||||
|
application.application_time,
|
||||||
|
application.id,
|
||||||
|
)
|
||||||
|
return application
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this does not update status
|
||||||
public fun update(application: Application): Application {
|
public fun update(application: Application): Application {
|
||||||
// I don't want ot update create_time
|
// I don't want ot update create_time
|
||||||
db.update(
|
db.update(
|
||||||
"update applications set url=?, original_url=?, unique_url=?, title=?, user_id=?, extra_data=?, payrange=?, status=?, company=?, recruiter=?, message=?, linked_application=?, status_history=?, application_time=? where id=?",
|
"update applications set url=?, original_url=?, unique_url=?, title=?, user_id=?, extra_data=?, payrange=?, company=?, recruiter=?, message=?, linked_application=? where id=?",
|
||||||
application.url,
|
application.url,
|
||||||
application.original_url,
|
application.original_url,
|
||||||
application.unique_url,
|
application.unique_url,
|
||||||
@ -605,13 +636,10 @@ class ApplicationService(
|
|||||||
application.user_id,
|
application.user_id,
|
||||||
application.extra_data,
|
application.extra_data,
|
||||||
application.payrange,
|
application.payrange,
|
||||||
application.status,
|
|
||||||
application.company,
|
application.company,
|
||||||
application.recruiter,
|
application.recruiter,
|
||||||
application.message,
|
application.message,
|
||||||
application.linked_application,
|
application.linked_application,
|
||||||
application.status_history,
|
|
||||||
application.application_time,
|
|
||||||
application.id,
|
application.id,
|
||||||
)
|
)
|
||||||
return application
|
return application
|
||||||
|
117
api/src/main/kotlin/com/andr3h3nriqu3s/applications/Events.kt
Normal file
117
api/src/main/kotlin/com/andr3h3nriqu3s/applications/Events.kt
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package com.andr3h3nriqu3s.applications
|
||||||
|
|
||||||
|
import java.sql.ResultSet
|
||||||
|
import java.sql.Timestamp
|
||||||
|
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
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
|
||||||
|
enum class EventType(val value: Int) {
|
||||||
|
Creation(0),
|
||||||
|
StatusUpdate(1),
|
||||||
|
View(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Event(
|
||||||
|
var id: String,
|
||||||
|
var application_id: String,
|
||||||
|
var event_type: Int,
|
||||||
|
var new_status: Int?,
|
||||||
|
var time: Timestamp
|
||||||
|
) {
|
||||||
|
companion object : RowMapper<Event> {
|
||||||
|
override public fun mapRow(rs: ResultSet, rowNum: Int): Event {
|
||||||
|
return Event(
|
||||||
|
rs.getString("id"),
|
||||||
|
rs.getString("application_id"),
|
||||||
|
rs.getInt("event_type"),
|
||||||
|
rs.getInt("new_status"),
|
||||||
|
rs.getTimestamp("time"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@ControllerAdvice
|
||||||
|
@RequestMapping("/api/events")
|
||||||
|
class EventController(
|
||||||
|
val sessionService: SessionService,
|
||||||
|
val applicationService: ApplicationService,
|
||||||
|
val eventService: EventService
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping(path = ["/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
|
public fun getCV(@PathVariable id: String, @RequestHeader("token") token: String): List<Event> {
|
||||||
|
val user = sessionService.verifyTokenThrow(token)
|
||||||
|
|
||||||
|
val application = applicationService.findApplicationById(user, id)
|
||||||
|
|
||||||
|
if (application == null) {
|
||||||
|
throw NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
return application.events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note I decided that events are read+delete only
|
||||||
|
@Service
|
||||||
|
public class EventService(val db: JdbcTemplate) {
|
||||||
|
|
||||||
|
public fun listFromApplicationId(id: String): Iterable<Event> =
|
||||||
|
db.query("select * from events where application_id=? order by time asc;", arrayOf(id), Event)
|
||||||
|
|
||||||
|
public fun getById(id: String): Event? {
|
||||||
|
val items = db.query("select * from events where id=?;", arrayOf(id), Event)
|
||||||
|
if (items.size == 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return items.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun deleteById(id: String): Event {
|
||||||
|
val event = this.getById(id)
|
||||||
|
if (event == null) {
|
||||||
|
throw NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
db.update("delete from events where id=?", id)
|
||||||
|
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun create(
|
||||||
|
application_id: String,
|
||||||
|
event_type: EventType,
|
||||||
|
new_status: Int? = null
|
||||||
|
): Event {
|
||||||
|
val id = UUID.randomUUID().toString()
|
||||||
|
|
||||||
|
if (event_type == EventType.StatusUpdate && new_status == null) {
|
||||||
|
throw Exception("When event_type == StatusUpdate new_status must be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_event =
|
||||||
|
Event(id, application_id, event_type.value, new_status, Timestamp(Date().getTime()))
|
||||||
|
|
||||||
|
db.update(
|
||||||
|
"insert into events (id, application_id, event_type, new_status) values (?, ?, ? ,?)",
|
||||||
|
new_event.id,
|
||||||
|
new_event.application_id,
|
||||||
|
new_event.event_type,
|
||||||
|
new_event.new_status,
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_event
|
||||||
|
}
|
||||||
|
}
|
@ -49,3 +49,21 @@ create table if not exists flair_link (
|
|||||||
application_id text not null,
|
application_id text not null,
|
||||||
flair_id text not null
|
flair_id text not null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table if not exists events (
|
||||||
|
id text primary key,
|
||||||
|
application_id text not null,
|
||||||
|
--
|
||||||
|
-- Event Types
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Creation(0),
|
||||||
|
-- StatusUpdate(1),
|
||||||
|
-- Page(2)
|
||||||
|
event_type integer not null,
|
||||||
|
|
||||||
|
-- This only matters when event_type == 1
|
||||||
|
new_status integer,
|
||||||
|
|
||||||
|
time timestamp default current_timestamp
|
||||||
|
);
|
||||||
|
@ -15,6 +15,18 @@ export const ApplicationStatus = Object.freeze({
|
|||||||
InterviewStep1: 8
|
InterviewStep1: 8
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ApplicationStatusIconMaping: Record<AsEnum<typeof ApplicationStatus>, string> = Object.freeze({
|
||||||
|
0: 'clock',
|
||||||
|
1: 'search',
|
||||||
|
2: 'trash3',
|
||||||
|
3: 'fire',
|
||||||
|
4: 'send',
|
||||||
|
5: 'hourglass-bottom',
|
||||||
|
6: 'list-check',
|
||||||
|
7: 'link-45deg',
|
||||||
|
8: 'person'
|
||||||
|
});
|
||||||
|
|
||||||
export const ApplicationStatusMaping: Record<AsEnum<typeof ApplicationStatus>, string> = Object.freeze({
|
export const ApplicationStatusMaping: Record<AsEnum<typeof ApplicationStatus>, string> = Object.freeze({
|
||||||
0: 'To Apply',
|
0: 'To Apply',
|
||||||
1: 'Working On It',
|
1: 'Working On It',
|
||||||
@ -33,6 +45,20 @@ export type View = {
|
|||||||
time: string;
|
time: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EventType = Object.freeze({
|
||||||
|
Creation: 0,
|
||||||
|
StatusUpdate: 1,
|
||||||
|
View: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ApplicationEvent = {
|
||||||
|
id: string,
|
||||||
|
application_id: string,
|
||||||
|
event_type: AsEnum<typeof EventType>,
|
||||||
|
new_status: number,
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
|
||||||
export type Application = {
|
export type Application = {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -52,6 +78,7 @@ export type Application = {
|
|||||||
status_history: string;
|
status_history: string;
|
||||||
flairs: Flair[];
|
flairs: Flair[];
|
||||||
views: View[];
|
views: View[];
|
||||||
|
events: ApplicationEvent[];
|
||||||
};
|
};
|
||||||
|
|
||||||
function createApplicationStore() {
|
function createApplicationStore() {
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
class="card p-2 my-2 bg-slate-100 w-full text-left"
|
class="card p-2 my-2 bg-slate-100 w-full text-left"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
item.views = await get(`view/${item.id}`);
|
item.views = await get(`view/${item.id}`);
|
||||||
|
item.events = await get(`events/${item.id}`);
|
||||||
|
|
||||||
applicationStore.loadItem = item;
|
applicationStore.loadItem = item;
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
|
@ -94,7 +94,9 @@
|
|||||||
{ApplicationStatusMaping[item.status]}
|
{ApplicationStatusMaping[item.status]}
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
<span class="text-violet-600 overflow-hidden whitespace-nowrap block max-w-full">
|
<span
|
||||||
|
class="text-violet-600 overflow-hidden whitespace-nowrap block max-w-full"
|
||||||
|
>
|
||||||
{item.url}
|
{item.url}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
121
site/src/routes/work-area/Timeline.svelte
Normal file
121
site/src/routes/work-area/Timeline.svelte
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
type Application,
|
||||||
|
type ApplicationEvent,
|
||||||
|
ApplicationStatus,
|
||||||
|
EventType,
|
||||||
|
ApplicationStatusIconMaping,
|
||||||
|
|
||||||
|
ApplicationStatusMaping
|
||||||
|
|
||||||
|
} from '$lib/ApplicationsStore.svelte';
|
||||||
|
|
||||||
|
let { application }: { application: Application } = $props();
|
||||||
|
|
||||||
|
let events: (ApplicationEvent & { timeDiff: string })[] = $state([]);
|
||||||
|
|
||||||
|
function calcDiff(time: number): string {
|
||||||
|
// millis to secs
|
||||||
|
time = Math.floor(time / 1000);
|
||||||
|
|
||||||
|
const days = Math.floor(time / (24 * 60 * 60));
|
||||||
|
if (days == 1) {
|
||||||
|
return '1 Day';
|
||||||
|
} else if (days > 0) {
|
||||||
|
return `${days} Days`;
|
||||||
|
}
|
||||||
|
|
||||||
|
time = time % (24 * 60 * 60);
|
||||||
|
|
||||||
|
const hours = Math.floor(time / (60 * 60));
|
||||||
|
if (hours > 20) {
|
||||||
|
return '1 Day';
|
||||||
|
} else if (hours > 2) {
|
||||||
|
return `${hours} Hours`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
let status: number | undefined = undefined;
|
||||||
|
|
||||||
|
let lastEvent: ApplicationEvent | undefined = undefined;
|
||||||
|
|
||||||
|
let _events: typeof events = [];
|
||||||
|
|
||||||
|
let checkArray: (number | undefined)[] = [
|
||||||
|
ApplicationStatus.WorkingOnIt,
|
||||||
|
ApplicationStatus.ToApply
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let event of application.events) {
|
||||||
|
let time = '';
|
||||||
|
|
||||||
|
if (lastEvent) {
|
||||||
|
let d1 = new Date(lastEvent.time).getTime();
|
||||||
|
let d2 = new Date(event.time).getTime();
|
||||||
|
time = calcDiff(d2 - d1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.event_type === EventType.Creation) {
|
||||||
|
status = ApplicationStatus.ToApply;
|
||||||
|
}
|
||||||
|
if (event.event_type !== EventType.StatusUpdate) {
|
||||||
|
_events.push({ ...event, timeDiff: time });
|
||||||
|
lastEvent = event;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (checkArray.includes(status) && checkArray.includes(event.new_status) && lastEvent?.event_type !== EventType.Creation ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = event.new_status;
|
||||||
|
_events.push({ ...event, timeDiff: time });
|
||||||
|
lastEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
events = _events;
|
||||||
|
});
|
||||||
|
|
||||||
|
let endable: number[] = [
|
||||||
|
ApplicationStatus.Expired,
|
||||||
|
ApplicationStatus.Ignore,
|
||||||
|
ApplicationStatus.ApplyedButSaidNo,
|
||||||
|
ApplicationStatus.LinkedApplication
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if events.length > 0}
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<div class="flex p-5 items-center">
|
||||||
|
{#each events as event, i}
|
||||||
|
{#if i === 0 && event.event_type !== EventType.Creation}
|
||||||
|
<div class="min-w-[70px] h-[15px] bg-blue-500 -mx-[10px]">
|
||||||
|
{event.timeDiff}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="shadow-sm shadow-violet-500 border rounded-full min-w-[50px] min-h-[50px] grid place-items-center bg-white z-10"
|
||||||
|
>
|
||||||
|
{#if event.event_type == EventType.Creation}
|
||||||
|
<span class="bi bi-plus"></span>
|
||||||
|
{:else if event.event_type == EventType.View}
|
||||||
|
<span class="bi bi-eye"></span>
|
||||||
|
{:else}
|
||||||
|
<span title={ApplicationStatusMaping[event.new_status]} class="bi bi-{ApplicationStatusIconMaping[event.new_status]}"></span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{#if i != events.length - 1 || !endable.includes(event.new_status)}
|
||||||
|
<div class="min-w-[70px] h-[13px] bg-blue-500 -mx-[10px] flex-grow">
|
||||||
|
{event.timeDiff}
|
||||||
|
</div>
|
||||||
|
{#if i == events.length - 1}
|
||||||
|
<!--div class="h-[15px] w-[15px] bg-blue-500 rotate-45"></div-->
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
@ -17,6 +17,7 @@
|
|||||||
import LinkApplication from './LinkApplication.svelte';
|
import LinkApplication from './LinkApplication.svelte';
|
||||||
import SearchApplication from './SearchApplication.svelte';
|
import SearchApplication from './SearchApplication.svelte';
|
||||||
import NewApplication from './NewApplication.svelte';
|
import NewApplication from './NewApplication.svelte';
|
||||||
|
import Timeline from './Timeline.svelte';
|
||||||
|
|
||||||
let activeItem: Application | undefined = $state();
|
let activeItem: Application | undefined = $state();
|
||||||
|
|
||||||
@ -279,20 +280,40 @@
|
|||||||
<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
|
||||||
|
class="finput"
|
||||||
|
id="title"
|
||||||
|
bind:value={activeItem.company}
|
||||||
|
onchange={save}
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="grow">
|
<fieldset class="grow">
|
||||||
<label class="flabel" for="title">Recruiter</label>
|
<label class="flabel" for="title">Recruiter</label>
|
||||||
<input class="finput" id="title" bind:value={activeItem.recruiter} onchange={save} />
|
<input
|
||||||
|
class="finput"
|
||||||
|
id="title"
|
||||||
|
bind:value={activeItem.recruiter}
|
||||||
|
onchange={save}
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</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>
|
||||||
{#if !activeItem.unique_url || showExtraData}
|
{#if !activeItem.unique_url || showExtraData}
|
||||||
<fieldset draggable="false" class="max-w-full min-w-0 overflow-hidden">
|
<fieldset draggable="false" class="max-w-full min-w-0 overflow-hidden">
|
||||||
@ -341,7 +362,11 @@
|
|||||||
</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>
|
<fieldset>
|
||||||
@ -391,7 +416,9 @@
|
|||||||
</button>
|
</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}
|
||||||
<div class="px-10"></div>
|
<div class="px-10"></div>
|
||||||
<button class="btn-primary" onclick={() => linkApplication.showModal()}>
|
<button class="btn-primary" onclick={() => linkApplication.showModal()}>
|
||||||
@ -400,7 +427,11 @@
|
|||||||
{#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>
|
||||||
{/if}
|
{/if}
|
||||||
<button class:btn-primary={drag} class:btn-danger={!drag} onclick={() => (drag = !drag)}>
|
<button
|
||||||
|
class:btn-primary={drag}
|
||||||
|
class:btn-danger={!drag}
|
||||||
|
onclick={() => (drag = !drag)}
|
||||||
|
>
|
||||||
👋
|
👋
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@ -411,6 +442,7 @@
|
|||||||
🔬
|
🔬
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<Timeline application={activeItem} />
|
||||||
</div>
|
</div>
|
||||||
{#if applicationStore.dragging}
|
{#if applicationStore.dragging}
|
||||||
<div
|
<div
|
||||||
@ -458,7 +490,9 @@
|
|||||||
|
|
||||||
{#if activeItem.status === ApplicationStatus.WorkingOnIt}
|
{#if activeItem.status === ApplicationStatus.WorkingOnIt}
|
||||||
<!-- Repeated -->
|
<!-- Repeated -->
|
||||||
<DropZone icon="trash-fill text-danger" ondrop={() => remove()}>Delete it</DropZone>
|
<DropZone icon="trash-fill text-danger" ondrop={() => remove()}
|
||||||
|
>Delete it</DropZone
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if [ApplicationStatus.WorkingOnIt, ApplicationStatus.TasksToDo].includes(activeItem.status)}
|
{#if [ApplicationStatus.WorkingOnIt, ApplicationStatus.TasksToDo].includes(activeItem.status)}
|
||||||
@ -487,10 +521,9 @@
|
|||||||
>
|
>
|
||||||
Tasks To Do
|
Tasks To Do
|
||||||
</DropZone>
|
</DropZone>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if [ApplicationStatus.TasksToDo, ApplicationStatus.Applyed].includes(activeItem.status)}
|
|
||||||
|
|
||||||
|
{#if [ApplicationStatus.TasksToDo, ApplicationStatus.Applyed].includes(activeItem.status)}
|
||||||
<!-- Tasks to do -->
|
<!-- Tasks to do -->
|
||||||
<DropZone
|
<DropZone
|
||||||
icon="server text-confirm"
|
icon="server text-confirm"
|
||||||
@ -555,6 +588,12 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<SearchApplication application={activeItem} onreload={(item) => (activeItem = item)} />
|
<SearchApplication
|
||||||
|
application={activeItem}
|
||||||
|
onreload={async (item) => {
|
||||||
|
item.events = await get(`events/${item.id}`);
|
||||||
|
activeItem = item;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<NewApplication onreload={activate} />
|
<NewApplication onreload={activate} />
|
||||||
|
Loading…
Reference in New Issue
Block a user