mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-05 11:00:56 +00:00
Merge pull request #176 from EETagent/admin_create_more_data
Admin create candidate more data
This commit is contained in:
commit
311d69c0ef
9 changed files with 139 additions and 27 deletions
|
|
@ -94,15 +94,25 @@ pub async fn create_candidate(
|
||||||
|
|
||||||
let plain_text_password = random_12_char_string();
|
let plain_text_password = random_12_char_string();
|
||||||
|
|
||||||
ApplicationService::create(&private_key, &db, form.application_id, &plain_text_password, form.personal_id_number.clone())
|
let (application, applications, personal_id_number) = ApplicationService::create(
|
||||||
|
&private_key,
|
||||||
|
&db,
|
||||||
|
form.application_id,
|
||||||
|
&plain_text_password,
|
||||||
|
form.personal_id_number.clone()
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
Json(
|
Json(
|
||||||
CreateCandidateResponse {
|
CreateCandidateResponse {
|
||||||
application_id: form.application_id,
|
application_id: application.id,
|
||||||
personal_id_number: form.personal_id_number,
|
field_of_study: application.field_of_study,
|
||||||
|
applications: applications.iter()
|
||||||
|
.map(|a| a.id)
|
||||||
|
.collect(),
|
||||||
|
personal_id_number,
|
||||||
password: plain_text_password,
|
password: plain_text_password,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ pub struct NewCandidateResponse {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateCandidateResponse {
|
pub struct CreateCandidateResponse {
|
||||||
pub application_id: i32,
|
pub application_id: i32,
|
||||||
|
pub field_of_study: String,
|
||||||
|
pub applications: Vec<i32>,
|
||||||
pub personal_id_number: String,
|
pub personal_id_number: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ impl ApplicationService {
|
||||||
application_id: i32,
|
application_id: i32,
|
||||||
plain_text_password: &String,
|
plain_text_password: &String,
|
||||||
personal_id_number: String,
|
personal_id_number: String,
|
||||||
) -> Result<application::Model, ServiceError> {
|
) -> Result<(application::Model, Vec<application::Model>, String), ServiceError> {
|
||||||
// Check if application id starts with 101, 102 or 103
|
// Check if application id starts with 101, 102 or 103
|
||||||
if !Self::is_application_id_valid(application_id) {
|
if !Self::is_application_id_valid(application_id) {
|
||||||
return Err(ServiceError::InvalidApplicationId);
|
return Err(ServiceError::InvalidApplicationId);
|
||||||
|
|
@ -50,7 +50,7 @@ impl ApplicationService {
|
||||||
application_id,
|
application_id,
|
||||||
admin_private_key,
|
admin_private_key,
|
||||||
db,
|
db,
|
||||||
personal_id_number,
|
&personal_id_number,
|
||||||
&pubkey,
|
&pubkey,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
|
|
@ -72,15 +72,31 @@ impl ApplicationService {
|
||||||
}
|
}
|
||||||
return Err(ServiceError::InternalServerError);
|
return Err(ServiceError::InternalServerError);
|
||||||
}
|
}
|
||||||
|
Ok(
|
||||||
Ok(application)
|
/* NewCandidateResponse {
|
||||||
|
current_application: application.id,
|
||||||
|
applications: applications
|
||||||
|
.iter()
|
||||||
|
.map(|a| a.id)
|
||||||
|
.collect::<Vec<i32>>(),
|
||||||
|
details_filled: false,
|
||||||
|
encrypted_by: Some(application.id),
|
||||||
|
field_of_study: application.field_of_study,
|
||||||
|
personal_id_number: personal_id_number,
|
||||||
|
} */
|
||||||
|
(
|
||||||
|
application,
|
||||||
|
applications,
|
||||||
|
personal_id_number,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_or_create_candidate_with_personal_id(
|
async fn find_or_create_candidate_with_personal_id(
|
||||||
application_id: i32,
|
application_id: i32,
|
||||||
admin_private_key: &String,
|
admin_private_key: &String,
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
personal_id_number: String,
|
personal_id_number: &String,
|
||||||
pubkey: &String,
|
pubkey: &String,
|
||||||
// enc_personal_id_number: &EncryptedString,
|
// enc_personal_id_number: &EncryptedString,
|
||||||
) -> Result<(candidate::Model, String), ServiceError> {
|
) -> Result<(candidate::Model, String), ServiceError> {
|
||||||
|
|
@ -98,7 +114,7 @@ impl ApplicationService {
|
||||||
|
|
||||||
let found_ids: Vec<&(i32, String)> = ids_decrypted
|
let found_ids: Vec<&(i32, String)> = ids_decrypted
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, id)| id == &personal_id_number)
|
.filter(|(_, id)| id == personal_id_number)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some((candidate_id, _)) = found_ids.first() {
|
if let Some((candidate_id, _)) = found_ids.first() {
|
||||||
|
|
@ -107,14 +123,14 @@ impl ApplicationService {
|
||||||
application_id,
|
application_id,
|
||||||
*candidate_id,
|
*candidate_id,
|
||||||
pubkey,
|
pubkey,
|
||||||
personal_id_number
|
personal_id_number.to_owned()
|
||||||
).await?
|
).await?
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let recipients = get_recipients(db, pubkey).await?;
|
let recipients = get_recipients(db, pubkey).await?;
|
||||||
|
|
||||||
let enc_personal_id_number = EncryptedString::new(
|
let enc_personal_id_number = EncryptedString::new(
|
||||||
&personal_id_number,
|
personal_id_number,
|
||||||
&recipients,
|
&recipients,
|
||||||
).await?;
|
).await?;
|
||||||
Ok(
|
Ok(
|
||||||
|
|
@ -345,6 +361,10 @@ impl ApplicationService {
|
||||||
Ok(
|
Ok(
|
||||||
CreateCandidateResponse {
|
CreateCandidateResponse {
|
||||||
application_id: id,
|
application_id: id,
|
||||||
|
field_of_study: application.field_of_study,
|
||||||
|
applications: applications.iter()
|
||||||
|
.map(|a| a.id)
|
||||||
|
.collect(),
|
||||||
personal_id_number,
|
personal_id_number,
|
||||||
password: new_password_plain,
|
password: new_password_plain,
|
||||||
}
|
}
|
||||||
|
|
@ -510,7 +530,7 @@ mod application_tests {
|
||||||
|
|
||||||
let secret_message = "trnka".to_string();
|
let secret_message = "trnka".to_string();
|
||||||
|
|
||||||
let application = ApplicationService::create(&"".to_string(), &db, 103100, &plain_text_password, "".to_string()).await.unwrap();
|
let application = ApplicationService::create(&"".to_string(), &db, 103100, &plain_text_password, "".to_string()).await.unwrap().0;
|
||||||
|
|
||||||
let encrypted_message =
|
let encrypted_message =
|
||||||
crypto::encrypt_password_with_recipients(&secret_message, &vec![&application.public_key])
|
crypto::encrypt_password_with_recipients(&secret_message, &vec![&application.public_key])
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub mod tests {
|
||||||
APPLICATION_ID,
|
APPLICATION_ID,
|
||||||
&plain_text_password,
|
&plain_text_password,
|
||||||
"0000001111".to_string()
|
"0000001111".to_string()
|
||||||
).await.unwrap();
|
).await.unwrap().0;
|
||||||
|
|
||||||
let candidate= ApplicationService::find_related_candidate(db, &application).await.unwrap();
|
let candidate= ApplicationService::find_related_candidate(db, &application).await.unwrap();
|
||||||
ParentService::create(db, candidate.id).await.unwrap();
|
ParentService::create(db, candidate.id).await.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ mod tests {
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap().0;
|
||||||
|
|
||||||
assert_eq!(application.id.to_owned(), 103151);
|
assert_eq!(application.id.to_owned(), 103151);
|
||||||
assert_ne!(application.password.to_owned(), SECRET.to_string());
|
assert_ne!(application.password.to_owned(), SECRET.to_string());
|
||||||
|
|
@ -66,7 +66,7 @@ mod tests {
|
||||||
async fn test_candidate_session_correct_password() {
|
async fn test_candidate_session_correct_password() {
|
||||||
let db = &get_memory_sqlite_connection().await;
|
let db = &get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap().0;
|
||||||
|
|
||||||
// correct password
|
// correct password
|
||||||
let session = ApplicationService::new_session(
|
let session = ApplicationService::new_session(
|
||||||
|
|
@ -88,7 +88,7 @@ mod tests {
|
||||||
async fn test_candidate_session_incorrect_password() {
|
async fn test_candidate_session_incorrect_password() {
|
||||||
let db = &get_memory_sqlite_connection().await;
|
let db = &get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap().0;
|
||||||
|
|
||||||
// incorrect password
|
// incorrect password
|
||||||
assert!(ApplicationService::new_session(
|
assert!(ApplicationService::new_session(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ApiError } from '$lib/@api';
|
import type { ApiError } from '$lib/@api';
|
||||||
import { apiCreateCandidate } from '$lib/@api/admin';
|
import { apiCreateCandidate } from '$lib/@api/admin';
|
||||||
|
import SelectField from '$lib/components/select/SelectField.svelte';
|
||||||
|
import TextField from '$lib/components/textfield/TextField.svelte';
|
||||||
import type { CreateCandidate, CreateCandidateLogin } from '$lib/stores/candidate';
|
import type { CreateCandidate, CreateCandidateLogin } from '$lib/stores/candidate';
|
||||||
|
import { isPersonalIdNumberValid } from '$lib/utils/personalIdFormat';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import Modal from '../../Modal.svelte';
|
import Modal from '../../Modal.svelte';
|
||||||
import IdField from '../../textfield/IdField.svelte';
|
import IdField from '../../textfield/IdField.svelte';
|
||||||
import NumberField from '../../textfield/NumberField.svelte';
|
import NumberField from '../../textfield/NumberField.svelte';
|
||||||
|
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||||
|
|
||||||
let isOpened = true;
|
let isOpened = true;
|
||||||
|
|
||||||
let applicationId: string = '';
|
let applicationId: string = '';
|
||||||
|
let citizenship: string = '';
|
||||||
let personalId: string = '';
|
let personalId: string = '';
|
||||||
|
let field: 'GYM' | 'IT' | 'KB' | 'Ev. č. nezadáno';
|
||||||
|
|
||||||
let login: CreateCandidateLogin;
|
let login: CreateCandidateLogin;
|
||||||
|
|
||||||
|
|
@ -18,13 +24,58 @@
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
$: {
|
||||||
|
let prefix = applicationId.slice(0, 3);
|
||||||
|
if (Number(prefix) === 101) {
|
||||||
|
field = 'GYM';
|
||||||
|
} else if (Number(prefix) === 102) {
|
||||||
|
field = 'IT';
|
||||||
|
} else if (Number(prefix) === 103) {
|
||||||
|
field = 'KB';
|
||||||
|
} else {
|
||||||
|
field = 'Ev. č. nezadáno';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createCandidate = async () => {
|
const createCandidate = async () => {
|
||||||
|
if (applicationId.length < 6) {
|
||||||
|
toast.push('Ev. číslo musí mít minimálně 6 znaků', {
|
||||||
|
theme: {
|
||||||
|
'--toastColor': 'mintcream',
|
||||||
|
'--toastBackground': '#b91c1c',
|
||||||
|
'--toastBarBackground': '#7f1d1d'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (citizenship === 'Česká republika') {
|
||||||
|
if (!isPersonalIdNumberValid(personalId)) {
|
||||||
|
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
|
||||||
|
theme: {
|
||||||
|
'--toastColor': 'mintcream',
|
||||||
|
'--toastBackground': '#b91c1c',
|
||||||
|
'--toastBarBackground': '#7f1d1d'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
const data: CreateCandidate = {
|
const data: CreateCandidate = {
|
||||||
applicationId: Number(applicationId),
|
applicationId: Number(applicationId),
|
||||||
personalIdNumber: personalId
|
personalIdNumber: personalId
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
login = await apiCreateCandidate(data);
|
login = await apiCreateCandidate(data);
|
||||||
|
toast.push(
|
||||||
|
`Uživatel ${data.applicationId} s rodným číslem ${data.personalIdNumber} byl vytvořen!`,
|
||||||
|
{
|
||||||
|
theme: {
|
||||||
|
'--toastColor': 'mintcream',
|
||||||
|
'--toastBackground': '#047857',
|
||||||
|
'--toastBarBackground': '#064e3b'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
dispatch('created');
|
dispatch('created');
|
||||||
error = '';
|
error = '';
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
|
|
@ -40,11 +91,18 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isOpened}
|
{#if isOpened}
|
||||||
|
<SvelteToast />
|
||||||
<Modal on:close={close}>
|
<Modal on:close={close}>
|
||||||
<div class="p-20">
|
<div class="p-20">
|
||||||
{#if login}
|
{#if login}
|
||||||
<h1 class="text-sspsBlue text-3xl font-semibold">{applicationId}</h1>
|
<h1 class="text-sspsBlue text-3xl font-semibold">Ev. č.: {applicationId}</h1>
|
||||||
<h1 class="text-sspsBlue text-3xl font-semibold">{login.password}</h1>
|
<h1 class="text-sspsBlue text-3xl font-semibold">R. č.: {login.personalIdNumber}</h1>
|
||||||
|
<h1 class="text-sspsBlue text-3xl font-semibold">Heslo: {login.password}</h1>
|
||||||
|
{#if login.applications.length > 1}
|
||||||
|
<h1 class="text-sspsBlue text-3xl font-semibold">
|
||||||
|
Slinkovaný s {login.applications.filter((a) => a != applicationId)}
|
||||||
|
</h1>
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<h1 class="text-sspsBlue text-3xl font-semibold">Registrace nového uchazeče</h1>
|
<h1 class="text-sspsBlue text-3xl font-semibold">Registrace nového uchazeče</h1>
|
||||||
{#if error}
|
{#if error}
|
||||||
|
|
@ -55,10 +113,28 @@
|
||||||
<span class="block sm:inline">{error}</span>
|
<span class="block sm:inline">{error}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<h3 class="my-4">Evidenčni číslo přihlášky</h3>
|
<div>
|
||||||
<NumberField bind:value={applicationId} />
|
<h3 class="my-4">Evidenční číslo přihlášky (
|
||||||
<h3 class="my-4">Rodné číslo</h3>
|
<span class="font-bold">{`Obor: ${field}`}</span>)
|
||||||
<IdField bind:value={personalId} />
|
</h3>
|
||||||
|
<NumberField bind:value={applicationId} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="my-4">Občanství</h3>
|
||||||
|
<SelectField
|
||||||
|
bind:value={citizenship}
|
||||||
|
placeholder="Občanství"
|
||||||
|
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
|
||||||
|
/>
|
||||||
|
<h3 class="my-4">Rodné číslo</h3>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#if citizenship === 'Česká republika'}
|
||||||
|
<IdField bind:value={personalId} />
|
||||||
|
{:else}
|
||||||
|
<TextField bind:value={personalId} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
on:click={createCandidate}
|
on:click={createCandidate}
|
||||||
class="bg-sspsBlue hover:bg-sspsBlueDark mt-6 w-full rounded-lg p-3 text-xl font-semibold text-white transition-colors duration-300"
|
class="bg-sspsBlue hover:bg-sspsBlueDark mt-6 w-full rounded-lg p-3 text-xl font-semibold text-white transition-colors duration-300"
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ export interface BaseCandidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateCandidateLogin extends CreateCandidate {
|
export interface CreateCandidateLogin extends CreateCandidate {
|
||||||
|
applications: [];
|
||||||
|
fieldOfStudy: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -349,11 +349,12 @@
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
|
// @ts-ignore
|
||||||
if (
|
if (
|
||||||
$typedErrors['candidate']['firstSchool']['name'] ||
|
$typedErrors['candidate']['firstSchool']['name'] ||
|
||||||
$typedErrors['candidate']['firstSchool']['field'] ||
|
$typedErrors['candidate']['firstSchool']['field'] ||
|
||||||
$typedErrors['candidate']['secondSchool']['name'] ||
|
$typedErrors['candidate']['secondSchool']['name'] ||
|
||||||
$typedErrors['candidate']['secondSchool']['field']
|
$typedErrors['candidate']['secondSchool']['field']
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -371,6 +372,7 @@
|
||||||
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
|
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO
|
||||||
/* $form.candidate.personalIdNumber = data.whoami.personalIdNumber;
|
/* $form.candidate.personalIdNumber = data.whoami.personalIdNumber;
|
||||||
const [birthdate, sex] = deriveBirthdateFromPersonalId(data.whoami.personalIdNumber);
|
const [birthdate, sex] = deriveBirthdateFromPersonalId(data.whoami.personalIdNumber);
|
||||||
$form.candidate.birthdate = birthdate;
|
$form.candidate.birthdate = birthdate;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue