Commit 0458675d authored by H.M.C. Nadunithara Wijerathne's avatar H.M.C. Nadunithara Wijerathne

Merge branch 'IT19980096' into 'master'

resume analysis

See merge request !2
parents 9b30774e 55f24436
......@@ -11,8 +11,10 @@
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"axios": "^1.3.4",
"firebase": "^9.17.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-modal": "^3.16.1",
"react-redux": "^8.0.5",
"react-router-dom": "^6.4.5",
"react-scripts": "5.0.1",
......@@ -48,6 +50,7 @@
]
},
"devDependencies": {
"@types/react-modal": "^3.13.1",
"sass": "^1.56.2"
},
"proxy": "http://localhost:5000"
......
......@@ -26,7 +26,7 @@
integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
crossorigin="anonymous"
></script>
<title>React App</title>
<title>Smart Recruiter</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
......@@ -4,6 +4,7 @@ import { Routes, BrowserRouter as Router, Route } from "react-router-dom";
import Landing from "./views/Landing.view";
import Home from "./views/Home.view";
import Settings from "./views/Settings.view";
import Applications from "./views/Applications.view";
import AppState from "./components/AppState";
......@@ -19,6 +20,7 @@ function App() {
<Route element={<ProtectedRoutes />}>
<Route path="/home" element={<Home />} />
<Route path="/settings" element={<Settings />} />
<Route path="/applications" element={<Applications />} />
</Route>
</Routes>
<AppState />
......
import { ACTIONS } from ".";
import { ControlsType, Reducers, UpdatePasswordPayload } from "../types";
import {
ControlsType,
Reducers,
SignInPayload,
UpdatePasswordPayload,
} from "../types";
export const setAppState = (payload: Reducers["auth"]["appState"]) => ({
type: ACTIONS.SET_APP_STATE,
......@@ -15,3 +20,12 @@ export const changePassword = (payload: UpdatePasswordPayload) => ({
type: ACTIONS.CHANGE_PASSWORD,
payload,
});
export const signIn = (
payload: SignInPayload,
success: (state: string) => void
) => ({
type: ACTIONS.SIGN_IN,
payload,
success,
});
import { ACTIONS } from ".";
import { JobType } from "../types";
export const getJobs = (success?: () => void) => ({
type: ACTIONS.GET_JOBS,
success,
});
export const createJob = (payload: JobType, success?: () => void) => ({
type: ACTIONS.CREATE_JOB,
payload,
success,
});
export const updateJob = (payload: JobType, success?: () => void) => ({
type: ACTIONS.UPDATE_JOB,
payload,
success,
});
export enum ACTIONS {
SET_AUTH = "SET_AUTH",
SIGN_IN = "SIGN_IN",
SIGN_OUT_USER = "SIGN_OUT_USER",
UPDATE_KEYSTROKE_SETTINGS = "UPDATE_KEYSTROKE_SETTINGS",
CHANGE_PASSWORD = "CHANGE_PASSWORD",
SET_USER_TYPE = "SET_USER_TYPE",
UPDATE_CANDIDATE = "UPDATE_CANDIDATE",
APPLY_FOR_JOB = "APPLY_FOR_JOB",
SET_APP_STATE = "SET_APP_STATE",
SET_JOBS = "SET_JOBS",
GET_JOBS = "GET_JOBS",
CREATE_JOB = "CREATE_JOB",
UPDATE_JOB = "UPDATE_JOB",
}
import { ACTIONS } from ".";
import { CandidateType } from "../types";
export const updateCandidate = (payload: Partial<CandidateType>) => ({
type: ACTIONS.UPDATE_CANDIDATE,
payload,
});
export const applyForJob = (payload: string) => ({
type: ACTIONS.APPLY_FOR_JOB,
payload,
});
import { request } from "../lib/api";
import { ApplicationType, JobType } from "../types";
export default class CommonAPI {
static getJobs = () => request("<BASE_URL>/jobs", "GET");
static createJob = (payload: JobType) =>
request("<BASE_URL>/jobs", "POST", payload);
static updateJob = (payload: JobType) =>
request("<BASE_URL>/jobs", "PUT", payload);
static applyForJob = (payload: {
application: ApplicationType;
resumeUrl: string;
}) => request("<BASE_URL>/jobs/apply", "PUT", payload);
}
import { request } from "../lib/api";
import { CandidateType } from "../types";
export default class UserAPI {
static updateCandidate = (payload: Partial<CandidateType>) =>
request("<BASE_URL>/user/candidate", "POST", payload);
}
import { initializeApp } from "firebase/app";
import { getStorage, ref } from "firebase/storage";
export const BASE_URL = "http://localhost:5000";
export const DEFAULT_CONTROLS = {
standard: {
......@@ -10,3 +13,14 @@ export const DEFAULT_CONTROLS = {
use: true,
},
};
const FIREBASE_CONFIG = {
apiKey: "AIzaSyAVE9S4CZfUpUcib8DczFuzTrlWOQcqk80",
authDomain: "smart-recruiter-909f6.firebaseapp.com",
projectId: "smart-recruiter-909f6",
storageBucket: "smart-recruiter-909f6.appspot.com",
messagingSenderId: "512045119582",
appId: "1:512045119582:web:8751af70c3d75da0295ccf",
};
const app = initializeApp(FIREBASE_CONFIG);
export const fileStorage = getStorage(app);
import {
ApplicationType,
CandidateType,
OrganizationType,
Reducers,
USER_TYPE,
} from "../types";
export const logger = (log: any, ...optionalparams: any[]) => {
console.log(log, ...optionalparams);
};
......@@ -28,3 +36,30 @@ export function keyEvent(e: any) {
time: Date.now(),
};
}
export const getProfile = (
state: Reducers
): CandidateType | OrganizationType | {} => {
if (state.auth.userType === USER_TYPE.CANDIDATE) {
return state.auth.candidate;
}
return state.auth.organization;
};
export const getUserId = (state: Reducers): string => {
const profile: any = getProfile(state);
return profile._id as string;
};
export const getStatusColor = (status?: ApplicationType["status"]) => {
const color =
status === "Accepted"
? "text-bg-success"
: status === "Pending"
? "text-bg-warning"
: status === "Rejected"
? "text-bg-danger"
: "text-bg-secondary";
return color;
};
......@@ -21,6 +21,9 @@ const authReducer = (
case ACTIONS.SET_APP_STATE:
return { ...state, appState: payload };
case ACTIONS.SET_USER_TYPE:
return { ...state, userType: payload };
case ACTIONS.SIGN_OUT_USER:
return { ...state, token: null };
......
import { ACTIONS } from "../actions/index";
import { CommonReducer } from "../types";
const INITIAL_STATE: CommonReducer = {
jobs: [],
};
const commonReducer = (
state = INITIAL_STATE,
{ type, payload }: { type: ACTIONS; payload: any }
): CommonReducer => {
switch (type) {
case ACTIONS.SET_JOBS:
return { ...state, jobs: payload };
case ACTIONS.SIGN_OUT_USER:
return INITIAL_STATE;
default:
return state;
}
};
export default commonReducer;
......@@ -3,12 +3,19 @@ import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import authReducer from "../reducers/auth";
import commonReducer from "../reducers/common";
const AuthPersistConfig = {
storage,
key: "auth",
};
const CommonPersistConfig = {
storage,
key: "common",
};
export default combineReducers({
auth: persistReducer(AuthPersistConfig, authReducer),
common: persistReducer(CommonPersistConfig, commonReducer),
});
......@@ -6,9 +6,35 @@ import {
ControlsType,
KeystrokeResultType,
Reducers,
SignInPayload,
UpdatePasswordPayload,
USER_TYPE,
} from "../types";
function* signIn({
payload,
success,
}: {
type: typeof ACTIONS.SIGN_IN;
payload: SignInPayload;
success: (state: string) => void;
}) {
try {
const data: { user: any } = yield call(AuthAPI.signin, payload);
yield put({ type: ACTIONS.SET_USER_TYPE, payload: payload.userType });
const auth = {
...data,
organization:
payload.userType === USER_TYPE.ORGANIZATION ? data.user : {},
candidate: payload.userType === USER_TYPE.CANDIDATE ? data.user : {},
};
yield put({ type: ACTIONS.SET_AUTH, payload: auth });
success(data.user.state);
} catch (error) {
success("failed");
}
}
function* updateKeystrokeSettings({
payload,
}: {
......@@ -35,7 +61,7 @@ function* updateKeystrokeSettings({
} catch (error) {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED, msg: error },
payload: { state: APP_STATE.FAILED },
});
}
}
......@@ -59,12 +85,13 @@ function* changePassword({
} catch (error) {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED, msg: error },
payload: { state: APP_STATE.FAILED },
});
}
}
export default function* authSaga() {
yield takeLeading(ACTIONS.SIGN_IN, signIn);
yield takeLeading(ACTIONS.UPDATE_KEYSTROKE_SETTINGS, updateKeystrokeSettings);
yield takeLeading(ACTIONS.CHANGE_PASSWORD, changePassword);
}
import { takeLeading, call, put, select } from "redux-saga/effects";
import { ACTIONS } from "../actions";
import CommonAPI from "../apis/common";
import { APP_STATE, JobType, Reducers } from "../types";
function* getJobs({
success,
}: {
type: typeof ACTIONS.GET_JOBS;
success: () => void;
}) {
try {
const data: { jobs: JobType; success: boolean } = yield call(
CommonAPI.getJobs
);
if (data.success) {
yield put({ type: ACTIONS.SET_JOBS, payload: data.jobs });
}
success();
} catch (error) {
success();
}
}
function* createJob({
payload,
success,
}: {
type: typeof ACTIONS.CREATE_JOB;
payload: JobType;
success: () => void;
}) {
try {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.LOADING },
});
const data: { success: boolean; job: JobType } = yield call(
CommonAPI.createJob,
payload
);
if (data.success) {
const jobs: JobType[] = yield select(
(state: Reducers) => state.common.jobs
);
yield put({ type: ACTIONS.SET_JOBS, payload: [data.job, ...jobs] });
}
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.SUCCESS },
});
success();
} catch (error) {
success();
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED },
});
}
}
function* updateJob({
payload,
success,
}: {
type: typeof ACTIONS.UPDATE_JOB;
payload: JobType;
success: () => void;
}) {
try {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.LOADING },
});
const data: { success: boolean } = yield call(CommonAPI.updateJob, payload);
if (data.success) {
let jobs: JobType[] = yield select(
(state: Reducers) => state.common.jobs
);
const found = jobs.findIndex((_job) => _job._id === payload._id);
jobs[found] = { ...jobs[found], ...payload };
yield put({ type: ACTIONS.SET_JOBS, payload: [...jobs] });
}
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.SUCCESS },
});
success();
} catch (error) {
success();
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED },
});
}
}
export default function* authSaga() {
yield takeLeading(ACTIONS.GET_JOBS, getJobs);
yield takeLeading(ACTIONS.CREATE_JOB, createJob);
yield takeLeading(ACTIONS.UPDATE_JOB, updateJob);
}
import { all } from "redux-saga/effects";
import Auth from "./auth";
import User from "./user";
import Common from "./common";
export default function* rootSaga() {
yield all([Auth()]);
yield all([Auth(), User(), Common()]);
}
import { put, takeLeading, call, select } from "redux-saga/effects";
import { ACTIONS } from "../actions";
import CommonAPI from "../apis/common";
import UserAPI from "../apis/user";
import { getProfile } from "../lib/util";
import { APP_STATE, CandidateType, JobType, Reducers } from "../types";
function* updateCandidate({
payload,
}: {
type: typeof ACTIONS.UPDATE_CANDIDATE;
payload: Partial<CandidateType>;
}) {
try {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.LOADING },
});
yield call(UserAPI.updateCandidate, payload);
const candidate: CandidateType = yield select(getProfile);
yield put({
type: ACTIONS.SET_AUTH,
payload: { candidate: { ...candidate, ...payload } },
});
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.SUCCESS },
});
} catch (error) {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED },
});
}
}
function* applyForJob({
payload,
}: {
type: typeof ACTIONS.APPLY_FOR_JOB;
payload: string;
}) {
try {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.LOADING },
});
const profile: CandidateType = yield select(getProfile);
if (profile._id && profile.resume) {
const data: { applicationId: string; success: boolean } = yield call(
CommonAPI.applyForJob,
{
application: {
candidate: profile._id,
job: payload,
status: "Pending",
},
resumeUrl: profile.resume,
}
);
if (data.success) {
let jobs: JobType[] = yield select(
(state: Reducers) => state.common.jobs
);
const found = jobs.findIndex((_job) => _job._id === payload);
jobs[found] = {
...jobs[found],
applications: [
...(jobs[found]?.applications || []),
data.applicationId,
],
};
yield put({ type: ACTIONS.SET_JOBS, payload: [...jobs] });
}
}
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.SUCCESS },
});
} catch (error) {
yield put({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.FAILED },
});
}
}
export default function* authSaga() {
yield takeLeading(ACTIONS.UPDATE_CANDIDATE, updateCandidate);
yield takeLeading(ACTIONS.APPLY_FOR_JOB, applyForJob);
}
......@@ -93,6 +93,8 @@ export type CandidateType = {
dateOfBirth: string;
jobIds: string[];
profilePicture: string;
state: "INTIAL" | "READY";
resume?: string;
};
export type OrganizationType = {
......@@ -120,6 +122,33 @@ export type ControlsType = {
};
};
export type JobType = {
_id?: string;
title: string;
description: string;
primarySkills: string[];
secondarySkills: string[];
salary: {
min: number;
max: number;
currency: string;
};
applications?: string[];
organization: string;
};
export type ApplicationType = {
candidate: string;
job: string;
status: "Pending" | "Accepted" | "In progress" | "Rejected";
interview?: {
date: string;
time: string;
link: string;
videoRef?: string;
};
};
//PAYLOADS
export type SignUpPayload = {
passwords: string[];
......@@ -146,6 +175,41 @@ export type UpdatePasswordPayload = {
oldPassword: string;
};
export type JobPayloadType = {
_id?: string;
title: string;
description: string;
primarySkills: string[];
secondarySkills: string[];
salary: {
min: number;
max: number;
currency: string;
};
applications?: ApplicationType[] | ApplicationPayloadType[];
organization: OrganizationType[];
};
export type ApplicationPayloadType = {
candidate: CandidateType;
job: string;
status: "Pending" | "Accepted" | "In progress" | "Rejected";
interview?: {
date: string;
time: string;
link: string;
videoRef?: string;
};
score: {
primary: number;
primatyMatch?: string[];
secondary: number;
secondaryMatch?: string[];
similarity: number;
total: number;
};
};
//REDUCERS
export type AuthReducer = {
token: string | null;
......@@ -160,6 +224,11 @@ export type AuthReducer = {
} | null;
};
export type CommonReducer = {
jobs: JobType[] | JobPayloadType[];
};
export type Reducers = {
auth: AuthReducer;
common: CommonReducer;
};
......@@ -44,7 +44,6 @@
align-items: center;
justify-content: center;
color: white;
margin: auto;
&.lg {
height: 100px;
......@@ -132,3 +131,79 @@
);
}
}
.ReactModal__Overlay {
opacity: 0;
transition: all 300ms ease-in-out;
}
.ReactModal__Overlay--after-open {
opacity: 1;
}
.ReactModal__Overlay--before-close {
opacity: 0;
}
.skills {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.skill {
background-color: #058700;
padding: 2px 10px 3px 10px;
border-radius: 10px;
color: white;
margin-right: 5px;
margin-top: 1px;
&.secondary {
background-color: #f3d13b;
color: black;
}
&.display {
background-color: #e0dfdf;
color: black;
}
.span-btn {
font-size: 12px;
padding: 1px 0px 1px 6px;
cursor: pointer;
font-weight: bold;
}
}
.job-card,
.job-preview {
cursor: pointer;
h6,
h5 {
margin: 0;
}
label {
a {
color: gray;
}
}
.desc {
font-size: 12px;
line-height: 15px;
color: gray;
}
&.active {
background-color: #ffefab80;
}
}
.job-preview {
label {
font-weight: bold;
margin-top: 5px;
}
}
......@@ -10,7 +10,7 @@ const Avatar = ({
size?: "sm" | "md" | "lg";
}) => {
if (url) return <img src={url} alt={name} className={`avatar img ${size}`} />;
return <div className={`avatar ${size}`}>{name[0]}</div>;
return <div className={`avatar ${size}`}>{name[0].toUpperCase()}</div>;
};
export default Avatar;
import { getDownloadURL, ref, uploadBytesResumable } from "firebase/storage";
import React, { useState, useEffect, ChangeEvent } from "react";
import { useDispatch, useSelector } from "react-redux";
import { ACTIONS } from "../common/actions";
import { updateCandidate } from "../common/actions/user";
import { fileStorage } from "../common/config";
import { getProfile } from "../common/lib/util";
import { APP_STATE, CandidateType } from "../common/types";
import Avatar from "./Avatar";
const CandidateProfile = () => {
const dispath = useDispatch();
const candidate = useSelector(getProfile) as CandidateType;
const [profile, setProfile] = useState<CandidateType>(candidate);
const [resume, setResume] = useState<File | null>(null);
useEffect(() => {
if (candidate) {
setProfile(candidate);
}
}, [candidate]);
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name === "phone") {
setProfile({
...profile,
contacts: { ...profile.contacts, phone: e.target.value },
});
} else {
setProfile({
...profile,
[e.target.name]: e.target.value,
});
}
};
const onChangeAddress = (e: ChangeEvent<HTMLInputElement>) => {
setProfile({
...profile,
contacts: {
...profile.contacts,
address: {
...profile.contacts.address,
[e.target.name]: e.target.value,
},
},
});
};
const onUpdateCandidate = (payload: Partial<CandidateType>) => {
dispath(updateCandidate(payload));
};
const onSubmit = () => {
const { jobIds, state, _id, ...rest } = profile;
dispath({
type: ACTIONS.SET_APP_STATE,
payload: { state: APP_STATE.LOADING },
});
if (resume) {
const resumeRef = ref(fileStorage, `/resumes/${profile._id}.pdf`);
const uploadTask = uploadBytesResumable(resumeRef, resume);
uploadTask.on(
"state_changed",
(snapshot) => {
const percent = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
console.log(percent);
},
(err) => {
console.log("ERROR ", err);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((url) => {
console.log(url);
onUpdateCandidate({ ...rest, resume: url });
});
}
);
} else {
onUpdateCandidate(rest);
}
};
const onSelectResume = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setResume(e.target.files[0]);
}
};
return (
<div className="card p-4 mb-3">
<h5>Edit/Update profile</h5>
<div className="mb-3 row">
<label className="col-sm-3 col-form-label">Name</label>
<div className="col-sm-9">
<input
type="text"
className="form-control"
name="name"
value={profile.name}
onChange={onChange}
/>
</div>
</div>
<div className="mb-3 row">
<label className="col-sm-3 col-form-label">Address</label>
<div className="col-sm-9">
<input
type="text"
name="addressLine"
className="form-control mb-2"
placeholder="Address line"
value={profile.contacts?.address?.addressLine}
onChange={onChangeAddress}
/>
<input
type="text"
className="form-control mb-2"
placeholder="City"
name="city"
value={profile.contacts?.address?.city}
onChange={onChangeAddress}
/>
<input
type="text"
className="form-control mb-2"
placeholder="Country"
name="country"
value={profile.contacts?.address?.country}
onChange={onChangeAddress}
/>
</div>
</div>
<div className="mb-3 row">
<div className="col-sm-6">
<div className="row">
<label className="col-sm-4 col-form-label">Date of birth</label>
<div className="col-sm-8">
<input
type="date"
className="form-control"
name="dateOfBirth"
value={profile?.dateOfBirth}
onChange={onChange}
/>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="row">
<label className="col-sm-3 col-form-label">Phone</label>
<div className="col-sm-9">
<input
type="text"
className="form-control"
name="phone"
value={profile.contacts?.phone}
onChange={onChange}
/>
</div>
</div>
</div>
</div>
<div className="mb-3 row">
<div className="col-sm-6">
<div className="row">
<label className="col-sm-4 col-form-label">Resume</label>
<div className="col-sm-8">
{profile?.resume && (
<a
className="btn btn-light mb-2"
href={profile.resume}
target="_blank"
rel="noreferrer"
>
Preview
</a>
)}
<input
type="file"
className="form-control"
onChange={onSelectResume}
accept="application/pdf"
/>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="row">
<label className="col-sm-3 col-form-label">Self Intro</label>
<div className="col-sm-9">
<input type="file" className="form-control" name="oldPassword" />
</div>
</div>
</div>
</div>
<div className="mb-3 row">
<div className="col-sm-6">
<div className="row">
<div className="col-sm-4 ">
<label className="col-form-label">Profile picuture</label>
</div>
<div className="col-sm-8">
<Avatar
name={profile?.name}
url={profile?.profilePicture}
size="lg"
/>
<button
type="button"
className="btn btn-link"
style={{ padding: "0" }}
>
Upload picture
</button>
</div>
</div>
</div>
</div>
<div className="d-md-block">
<button
type="button"
className="btn btn-primary"
style={{ width: "300px", float: "right" }}
onClick={onSubmit}
>
Update Profile
</button>
</div>
</div>
);
};
export default CandidateProfile;
......@@ -189,14 +189,16 @@ const ChangePassword = () => {
</div>
</div>
<button
type="button"
className="btn btn-primary"
style={{ margin: "auto", width: "300px" }}
onClick={onSumit}
>
Save Password
</button>
<div className="d-md-block">
<button
type="button"
className="btn btn-primary"
style={{ width: "300px", float: "right" }}
onClick={onSumit}
>
Save Password
</button>
</div>
<Alert alert={alert} setAlert={setAlert} />
</div>
);
......
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getJobs } from "../common/actions/common";
import { JobPayloadType, JobType, Reducers, USER_TYPE } from "../common/types";
type OwnProps = {
jobs: JobType[] | JobPayloadType[];
onSelectJob?: (item: number) => void;
selectedIndex?: number;
};
const Jobs = ({ jobs, onSelectJob, selectedIndex }: OwnProps) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState<boolean>(true);
const userType = useSelector((state: Reducers) => state.auth.userType);
useEffect(() => {
dispatch(
getJobs(() => {
setIsLoading(false);
})
);
}, [dispatch]);
const renderStatus = () => {
if (isLoading) {
return <p style={{ textAlign: "center" }}>Loading jobs...</p>;
} else if (jobs.length === 0) {
if (userType === USER_TYPE.CANDIDATE) {
return <p style={{ textAlign: "center" }}>No jobs found yet!</p>;
} else {
return (
<p style={{ textAlign: "center" }}>
You don't have posted any jobs yet!
</p>
);
}
}
return null;
};
const renderJobs = (job: JobType | JobPayloadType, index: number) => {
const onClick = () => onSelectJob?.(index);
const className = `card p-3 job-card ${
index === selectedIndex && "active"
}`;
return (
<div className={className} key={job._id} onClick={onClick}>
<h6>{job.title}</h6>
<p>
Salary : {job.salary.min} - {job.salary.max} {job.salary.currency}{" "}
</p>
<p className="desc">{job.description.slice(0, 200)}...</p>
<p className="desc mt-2">
<label>Applicattions : </label>{" "}
<a href={`/applications?jobId=${job._id}`}>
({job.applications?.length}) Candidates
</a>
</p>
</div>
);
};
return (
<div>
{renderStatus()}
{jobs.map(renderJobs)}
</div>
);
};
export default Jobs;
import React, { useState, useRef } from "react";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { ACTIONS } from "../common/actions";
import AuthAPI from "../common/apis/auth";
import { signIn } from "../common/actions/auth";
import { keyEvent } from "../common/lib/util";
import { KeyDetails, SignInPayload, USER_TYPE } from "../common/types";
......@@ -43,22 +42,17 @@ const Login = ({ userType, setAlert }: OwnProps) => {
userType,
};
clearData();
AuthAPI.signin(data)
.then((res: any) => {
if (res.success) {
const payload = {
...res,
organization: userType === USER_TYPE.ORGANIZATION ? res.user : {},
candidate: userType === USER_TYPE.CANDIDATE ? res.user : {},
};
dispatch({ type: ACTIONS.SET_AUTH, payload });
dispatch(
signIn(data, (state) => {
if (state === "failed") {
setAlert("Attempt failed! Try again");
} else if (state === "INTIAL") {
navigate("/settings");
} else {
navigate("/home");
}
})
.catch((_) => {
setAlert("Attempt failed! Try again");
});
);
};
const onKeyDown = (e: any) => {
......
import React, { useState, ChangeEvent } from "react";
import { useDispatch, useSelector } from "react-redux";
import { createJob, updateJob } from "../../common/actions/common";
import { getUserId } from "../../common/lib/util";
import { JobType } from "../../common/types";
import CustomModal from "./Modal";
const initialJob: JobType = {
title: "",
description: "",
primarySkills: [],
secondarySkills: [],
salary: {
min: 0,
max: 0,
currency: "LKR",
},
organization: "",
};
type OwnProps = {
mode: "create" | "update";
defaultJob?: JobType;
};
const CreateUpdateJob = ({
mode = "create",
defaultJob = initialJob,
}: OwnProps) => {
const dispatch = useDispatch();
const userId = useSelector(getUserId);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [job, setJob] = useState<JobType>(defaultJob);
const [primarySkill, setPrimarySkill] = useState<string>("");
const [secondarySkill, setSecondarySkill] = useState<string>("");
const toggleModal = () => setIsOpen(!isOpen);
const onChangeSkills = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name === "primary") {
setPrimarySkill(e.target.value);
} else {
setSecondarySkill(e.target.value);
}
};
const onChangeJob = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setJob({ ...job, [e.target.name]: e.target.value });
};
const onChangeSalary = (e: ChangeEvent<HTMLInputElement>) => {
setJob({
...job,
salary: { ...job.salary, [e.target.name]: e.target.value },
});
};
const onAddSkill = (type: "primary" | "secondary") => {
if (type === "primary") {
if (primarySkill.length > 0) {
setJob({
...job,
primarySkills: [...job.primarySkills, primarySkill],
});
setPrimarySkill("");
}
} else {
if (secondarySkill.length > 0) {
setJob({
...job,
secondarySkills: [...job.secondarySkills, secondarySkill],
});
setSecondarySkill("");
}
}
};
const removeSkill = (type: string, index: number) => {
let key: "primarySkills" | "secondarySkills" = "secondarySkills";
if (type === "primary") {
key = "primarySkills";
}
const skills = job[key].filter((_, i) => i !== index);
setJob({ ...job, [key]: [...skills] });
};
const renderPrimarySkills = (skill: string, index: number) => {
const onClick = () => removeSkill("primary", index);
return (
<span className="skill" key={index}>
{skill}{" "}
<span className="span-btn" onClick={onClick}>
X
</span>
</span>
);
};
const renderSecondarykills = (skill: string, index: number) => {
const onClick = () => removeSkill("secondary", index);
return (
<span className="skill secondary" key={index}>
{skill}{" "}
<span className="span-btn" onClick={onClick}>
X
</span>
</span>
);
};
const onSubmit = () => {
const {
title,
description,
salary: { min, max },
primarySkills,
} = job;
if (
title.length > 0 &&
description.length > 0 &&
primarySkills.length > 0 &&
min > 0 &&
max > 0
) {
const payload: JobType = {
...job,
salary: {
currency: job.salary.currency,
max: Number(max),
min: Number(min),
},
organization: userId,
};
setIsLoading(true);
if (mode === "create") {
dispatch(createJob(payload, callback));
} else {
dispatch(updateJob(payload, callback));
}
}
};
const callback = () => {
setIsLoading(false);
toggleModal();
};
const btnText = mode === "create" ? "Create job" : "Update job";
return (
<div className="mt-3">
<button
className="btn btn-primary"
style={{ width: "100%" }}
onClick={toggleModal}
>
{btnText}
</button>
<CustomModal
isOpen={isOpen}
onRequestClose={toggleModal}
size="lg"
shouldCloseOnOverlayClick={false}
>
<h4>{mode === "create" ? " Create new job" : "Edit job"}</h4>
<div className="mb-3">
<label className="form-label">Job title</label>
<input
type="text"
className="form-control"
name="title"
onChange={onChangeJob}
value={job.title}
/>
</div>
<div className="mb-3">
<label className="form-label">Job description</label>
<textarea
className="form-control"
rows={5}
name="description"
onChange={onChangeJob}
value={job.description}
/>
</div>
<div className="mb-2">
<label className="form-label">Primary skills</label>
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder=""
name="primary"
onChange={onChangeSkills}
value={primarySkill}
/>
<button
className="btn btn-secondary"
type="button"
id="button-addon1"
onClick={() => onAddSkill("primary")}
>
Add
</button>
</div>
</div>
<div className="skills">
{job.primarySkills.map(renderPrimarySkills)}
</div>
<div className="mb-2">
<label className="form-label">Secondary skills (optional)</label>
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder=""
name="secondary"
onChange={onChangeSkills}
value={secondarySkill}
/>
<button
className="btn btn-secondary"
type="button"
id="button-addon1"
onClick={() => onAddSkill("secondary")}
>
Add
</button>
</div>
</div>
<div className="skills">
{job.secondarySkills.map(renderSecondarykills)}{" "}
</div>
<div className="mb-3">
<label className="form-label">Salary Range</label>
<div className="input-group mb-3">
<input
type="number"
className="form-control"
placeholder="min"
name="min"
onChange={onChangeSalary}
value={job.salary.min}
/>
<span className="input-group-text">-</span>
<input
type="number"
className="form-control"
placeholder="max"
name="max"
onChange={onChangeSalary}
value={job.salary.max}
/>
<span className="input-group-text">-</span>
<input
type="text"
className="form-control"
placeholder="currency"
name="currency"
onChange={onChangeSalary}
value={job.salary.currency}
/>
</div>
</div>
<div className="d-md-block" style={{ float: "right" }}>
<button
className="btn btn-primary m-2"
onClick={onSubmit}
disabled={isLoading}
>
{btnText}
</button>
<button className="btn btn-danger" onClick={toggleModal}>
Cancel
</button>
</div>
</CustomModal>
</div>
);
};
export default CreateUpdateJob;
import React from "react";
import Modal from "react-modal";
type OwnProps = {
isOpen: boolean;
onRequestClose: () => void;
children: JSX.Element | JSX.Element[];
size?: "md" | "lg";
shouldCloseOnOverlayClick?: boolean;
};
const CustomModal = ({
isOpen,
onRequestClose,
children,
size = "md",
shouldCloseOnOverlayClick = true,
}: OwnProps) => {
const width = size === "md" ? "400px" : "500px";
return (
<Modal
isOpen={isOpen}
ariaHideApp={false}
onRequestClose={onRequestClose}
closeTimeoutMS={300}
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
style={{
overlay: {
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
content: {
width,
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
maxHeight: "95%",
},
}}
>
{children}
</Modal>
);
};
export default CustomModal;
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { ACTIONS } from "../common/actions";
import { getProfile } from "../common/lib/util";
import Avatar from "./Avatar";
const NavBar = () => {
const dispatch = useDispatch();
const profile: any = useSelector(getProfile);
const onClickLogout = () => dispatch({ type: ACTIONS.SIGN_OUT_USER });
return (
......@@ -42,7 +43,11 @@ const NavBar = () => {
data-bs-toggle="dropdown"
aria-expanded="false"
>
<Avatar name="Hashan" size="sm" />
<Avatar
name={profile?.name}
url={profile?.profilePicture}
size="sm"
/>
</a>
<ul className="dropdown-menu">
<li>
......
import React from "react";
const Profile = () => {
return <div className="card p-4">Profile</div>;
};
export default Profile;
import React, { useEffect, useState, ChangeEvent } from "react";
import { useSelector } from "react-redux";
import { useSearchParams } from "react-router-dom";
import { getStatusColor } from "../common/lib/util";
import {
JobPayloadType,
Reducers,
ApplicationPayloadType,
} from "../common/types";
import Layout from "../components/Layout";
const Applications = () => {
const jobs = useSelector(
(state: Reducers) => state.common.jobs
) as JobPayloadType[];
const [searchParams] = useSearchParams();
const [job, setJob] = useState<JobPayloadType | null>();
const jobId = searchParams.get("jobId");
useEffect(() => {
if (jobId) {
const found = jobs.find((_job) => _job._id === jobId);
sortApplications("skills", found);
}
}, [jobId, jobs]);
const onSelectSort = (e: ChangeEvent<HTMLSelectElement>) => {
sortApplications(e.target.value, job);
};
const sortApplications = (sortBy: string, _job?: JobPayloadType | null) => {
if (_job?.applications) {
const _applications = _job.applications as ApplicationPayloadType[];
if (sortBy === "skills") {
_applications.sort((current, prev) => {
const skill = current.score.primary + current.score.secondary;
const prevSkill = prev.score.primary + prev.score.secondary;
if (skill < prevSkill) {
return 1;
} else if (skill > prevSkill) {
return -1;
}
return 0;
});
} else if (sortBy === "similarity") {
_applications.sort((current, prev) => {
if (current.score.similarity < prev.score.similarity) {
return 1;
} else if (current.score.similarity > prev.score.similarity) {
return -1;
}
return 0;
});
} else {
_applications.sort((current, prev) => {
if (current.score.total < prev.score.total) {
return 1;
} else if (current.score.total > prev.score.total) {
return -1;
}
return 0;
});
}
setJob({ ..._job, applications: _applications });
}
};
const renderSkills = (skill: string, index: number) => (
<span className="skill" key={index}>
{skill}
</span>
);
const renderApplications = (_application: any, index: number) => {
const application = _application as ApplicationPayloadType;
const skillsScore = `PS ${application.score.primary} + SS ${application.score?.secondary} `;
const status = `badge ${getStatusColor(application.status)}`;
return (
<tr key={index}>
<th scope="row">{application.candidate.name}</th>
<td>{skillsScore}</td>
<td>{application.score.similarity}</td>
<td>{application.score.total}</td>
<td>
<span className={status}>{application.status}</span>
</td>
<td>{application.candidate.contacts?.phone || ""}</td>
<td>
<a
className="nav-link"
href={application.candidate.resume}
target="_blank"
rel="noreferrer"
>
View
</a>
</td>
</tr>
);
};
if (!job) return <Layout title="Applications"></Layout>;
return (
<Layout title={job.title}>
<div className="card p-4 job-preview">
<p>
Salary : {job.salary.min} - {job.salary.max} {job.salary.currency}{" "}
</p>
<p className="desc">{job.description}</p>
<label>Primary skills</label>
<div className="skills">{job.primarySkills.map(renderSkills)}</div>
<label>Secondary skills</label>
<div className="skills">{job.secondarySkills.map(renderSkills)}</div>
<div className="mt-2">
<label>Applicattions : </label>{" "}
<a href={`/applications?jobId=${job._id}`}>
({job.applications?.length}) Candidates
</a>
</div>
</div>
<div className="container">
<div className="row mt-5">
<h4 className="mb-3">Applications</h4>
<div className="mb-3 row">
<label className="col-sm-2 col-form-label">Sort by</label>
<div className="col-sm-2">
<select
className="form-select"
aria-label="Default select example"
onChange={onSelectSort}
>
<option value="skills">Skills score</option>
<option value="similarity">Similarity</option>
<option value="total">Total score</option>
</select>
</div>
</div>
<div className="card p-4 table-responsive">
<table className="table table-hover table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Skills Score</th>
<th scope="col">Similarity</th>
<th scope="col">Total score</th>
<th scope="col">Status</th>
<th scope="col">Phone</th>
<th scope="col">Resume</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>{job.applications?.map(renderApplications)}</tbody>
</table>
</div>
</div>
</div>
</Layout>
);
};
export default Applications;
import React from "react";
import NavBar from "../components/NavBar";
import React, { useState, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { applyForJob } from "../common/actions/user";
import { getStatusColor, getUserId } from "../common/lib/util";
import { JobPayloadType, JobType, Reducers, USER_TYPE } from "../common/types";
import Jobs from "../components/Jobs";
import Layout from "../components/Layout";
import CreateUpdateJob from "../components/Modals/CreateUpdateJob";
import Profile from "../components/Profile";
const CandidateHome = () => {
const dispatch = useDispatch();
const [selectedJob, setSelectedJob] = useState<number>(0);
const userId = useSelector(getUserId);
const jobs = useSelector(
(state: Reducers) => state.common.jobs
) as JobPayloadType[];
const job = jobs.length > 0 ? jobs[selectedJob] : null;
const renderSkills = (skill: string, index: number) => (
<span className="skill display" key={index}>
{skill}
</span>
);
const alreadyApplied = useMemo(() => {
const foundCandidate = jobs[selectedJob]?.applications?.findIndex(
(_item) => _item.candidate === userId
);
if (foundCandidate !== undefined && foundCandidate >= 0) {
const status = jobs[selectedJob]?.applications?.[foundCandidate].status;
return {
applied: true,
status,
color: getStatusColor(status),
};
} else {
return { applied: false, status: "" };
}
}, [jobs, selectedJob, userId]);
const onApply = () => {
dispatch(applyForJob(jobs[selectedJob]._id || ""));
};
const Home = () => {
return (
<div>
<NavBar />
<div className="row">
<div className="col-3 ">
<Profile />
</div>
<div className="col">
<Jobs
jobs={jobs}
onSelectJob={setSelectedJob}
selectedIndex={selectedJob}
/>
</div>
<div className="col">
{job && (
<div className="card p-4 job-preview">
<h5>{job.title}</h5>
<label>
<a href={`/organizations?id=${job.organization[0]._id}`}>
By {job.organization[0].name}
</a>
</label>
<p>
Salary : {job.salary.min} - {job.salary.max} {job.salary.currency}{" "}
</p>
<p className="desc">{job.description}</p>
<label>Primary skills</label>
<div className="skills">{job.primarySkills.map(renderSkills)}</div>
<label>Secondary skills</label>
<div className="skills">
{job.secondarySkills.map(renderSkills)}
</div>
{alreadyApplied.applied ? (
<p className="h5 mt-2">
Status :{" "}
<label className={alreadyApplied.color}>
{alreadyApplied.status}
</label>
</p>
) : (
<button className="btn btn-success mt-3" onClick={onApply}>
Apply
</button>
)}
</div>
)}
</div>
</div>
);
};
const OrganizationHome = () => {
const [selectedJob, setSelectedJob] = useState<number>(0);
const jobs = useSelector((state: Reducers) => state.common.jobs) as JobType[];
const job = jobs.length > 0 ? jobs[selectedJob] : null;
const renderSkills = (skill: string, index: number) => (
<span className="skill" key={index}>
{skill}
</span>
);
return (
<div className="row">
<div className="col-3 ">
<Profile />
<CreateUpdateJob mode="create" />
</div>
<div className="col">
<Jobs
jobs={jobs}
onSelectJob={setSelectedJob}
selectedIndex={selectedJob}
/>
</div>
<div className="col">
{job && (
<div className="card p-4 job-preview">
<h5>{job.title}</h5>
<p>
Salary : {job.salary.min} - {job.salary.max} {job.salary.currency}{" "}
</p>
<p className="desc">{job.description}</p>
<label>Primary skills</label>
<div className="skills">{job.primarySkills.map(renderSkills)}</div>
<label>Secondary skills</label>
<div className="skills">
{job.secondarySkills.map(renderSkills)}
</div>
<div className="mt-2">
<label>Applicattions : </label>{" "}
<a href={`/applications?jobId=${job._id}`}>
({job.applications?.length}) Candidates
</a>
</div>
<CreateUpdateJob defaultJob={job} mode="update" />
</div>
)}
</div>
</div>
);
};
const Home = () => {
const userType = useSelector((state: Reducers) => state.auth.userType);
return (
<Layout title="Vacancies and Jobs">
{userType === USER_TYPE.ORGANIZATION ? (
<OrganizationHome />
) : (
<CandidateHome />
)}
</Layout>
);
};
export default Home;
......@@ -3,10 +3,10 @@ import { useDispatch, useSelector } from "react-redux";
import { updateKeystrokeSettings } from "../common/actions/auth";
import { DEFAULT_CONTROLS } from "../common/config";
import { ControlsType, Reducers } from "../common/types";
import Avatar from "../components/Avatar";
import ChangePassword from "../components/ChangePassword";
import Charts from "../components/Charts";
import Layout from "../components/Layout";
import CandidateProfile from "../components/CandidateProfile";
import ResultTable from "../components/ResultTable";
const Settings = () => {
......@@ -54,21 +54,7 @@ const Settings = () => {
<Layout title="Profile and Settings">
<div className="row">
<div className="col-8">
<div className="card p-4 mb-3">
<div className="mb-3 row">
<div className="col-sm-3">
<Avatar name="Hashan" size="lg" />
</div>
<div className="col-sm-9 ">
<input
className="form-control"
type="text"
placeholder="Default input"
aria-label="default input example"
/>
</div>
</div>
</div>
<CandidateProfile />
<ChangePassword />
</div>
<div className="col-4">
......
......@@ -1212,6 +1212,408 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@firebase/analytics-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.4.tgz#3887676286ead7b30f9880581e0144f43bc71f16"
integrity sha512-ZN4K49QwOR8EWIUTV03VBdcVkz8sVsfJmve4g2+FEIj0kyTK0MdoVTWNOwWj9TVi2p/7FvKRKkpWxkydmi9x7g==
dependencies:
"@firebase/analytics" "0.9.4"
"@firebase/analytics-types" "0.8.0"
"@firebase/component" "0.6.4"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/analytics-types@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.0.tgz#551e744a29adbc07f557306530a2ec86add6d410"
integrity sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==
"@firebase/analytics@0.9.4":
version "0.9.4"
resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.9.4.tgz#1b863bd795c3dbe3d278467e8c9dd0e6c54f37a3"
integrity sha512-Mb2UaD0cyJ9DrTk4Okz8wqpjZuVRVXHZOjhbQcmGb8VtibXY1+jm/k3eJ21r7NqUKnjWejYM2EX+hI9+dtXGkQ==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/installations" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/app-check-compat@0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.3.4.tgz#43cad88c9211a84bb98f205ba075c34acd8933c2"
integrity sha512-s6ON0ixPKe99M1DNYMI2eR5aLwQZgy0z8fuW1tnEbzg5p/N/GKFmqiIHSV4gfp8+X7Fw5NLm7qMfh4xrcPgQCw==
dependencies:
"@firebase/app-check" "0.6.4"
"@firebase/app-check-types" "0.5.0"
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/app-check-interop-types@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.2.0.tgz#9106270114ca4e7732457e8319333866a26285d8"
integrity sha512-+3PQIeX6/eiVK+x/yg8r6xTNR97fN7MahFDm+jiQmDjcyvSefoGuTTNQuuMScGyx3vYUBeZn+Cp9kC0yY/9uxQ==
"@firebase/app-check-types@0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.5.0.tgz#1b02826213d7ce6a1cf773c329b46ea1c67064f4"
integrity sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==
"@firebase/app-check@0.6.4":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.6.4.tgz#cb2f7b23f80126800a5632c1d766635266e1b2ff"
integrity sha512-M9qyVTWkEkHXmgwGtObvXQqKcOe9iKAOPqm0pCe74mzgKVTNq157ff39+fxHPb4nFbipToY+GuvtabLUzkHehQ==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/app-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.2.4.tgz#610bf28a655373e6b4cda2115fb594f3c576d7d5"
integrity sha512-eYKtxMrzi+icZ6dFeJEoEpxu3aq1jp2PeL5vPIOAavJpdgRWFmSGmw3a46Hkay+GGGX4fkJG3vCfuoQsf5ksjA==
dependencies:
"@firebase/app" "0.9.4"
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/app-types@0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.0.tgz#35b5c568341e9e263b29b3d2ba0e9cfc9ec7f01e"
integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==
"@firebase/app@0.9.4":
version "0.9.4"
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.9.4.tgz#28eb5cd0406f92825afb32a53194d59c19cddb7b"
integrity sha512-xX8I6pNqUxhxhaghy9fbjOWOP9ndx5UeN5F0V/PWD2u7xRg88YkzZrDocTAIU17y82UPZ1x1E5n15CsXGcxaOg==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
idb "7.0.1"
tslib "^2.1.0"
"@firebase/auth-compat@0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.3.4.tgz#3c00b391876d6192e35eaf805adda3aef43199a5"
integrity sha512-AVNZ4pwLV063ngPKU+8tykQ6v+fRlKfBWEp1W+JU1pEJI+GK0thOPrCn22lWyI8LYiDrh3MLIiBJCv7fsyQajw==
dependencies:
"@firebase/auth" "0.21.4"
"@firebase/auth-types" "0.12.0"
"@firebase/component" "0.6.4"
"@firebase/util" "1.9.3"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/auth-interop-types@0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz#78884f24fa539e34a06c03612c75f222fcc33742"
integrity sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==
"@firebase/auth-types@0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.12.0.tgz#f28e1b68ac3b208ad02a15854c585be6da3e8e79"
integrity sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==
"@firebase/auth@0.21.4":
version "0.21.4"
resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.21.4.tgz#2ce8a34a78b53a168152b987b7bbd844f0431669"
integrity sha512-yZrs1F8sTt8IMCJl29gaxokDZSLjO08r2bL2PNKV1Duz2vJ67ZtVcgHAidyf8BFak9uS8mepd9KlYFDfwUO60Q==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/component@0.6.4":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.6.4.tgz#8981a6818bd730a7554aa5e0516ffc9b1ae3f33d"
integrity sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==
dependencies:
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/database-compat@0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-0.3.4.tgz#4e57932f7a5ba761cd5ac946ab6b6ab3f660522c"
integrity sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/database" "0.14.4"
"@firebase/database-types" "0.10.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/database-types@0.10.4":
version "0.10.4"
resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.10.4.tgz#47ba81113512dab637abace61cfb65f63d645ca7"
integrity sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==
dependencies:
"@firebase/app-types" "0.9.0"
"@firebase/util" "1.9.3"
"@firebase/database@0.14.4":
version "0.14.4"
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.14.4.tgz#9e7435a16a540ddfdeb5d99d45618e6ede179aa6"
integrity sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==
dependencies:
"@firebase/auth-interop-types" "0.2.1"
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
faye-websocket "0.11.4"
tslib "^2.1.0"
"@firebase/firestore-compat@0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.3.4.tgz#1c656c225a1ed2a3cd6af1f4118701b5539a4c44"
integrity sha512-xUzz1V53vA1R8S5QQbQ33zqNv0bV+dZpeQKqMXt6HNWa1yiX7lUooGYRws825F+QBOadW1teav1ttXnGZAsgUw==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/firestore" "3.8.4"
"@firebase/firestore-types" "2.5.1"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/firestore-types@2.5.1":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-2.5.1.tgz#464b2ee057956599ca34de50eae957c30fdbabb7"
integrity sha512-xG0CA6EMfYo8YeUxC8FeDzf6W3FX1cLlcAGBYV6Cku12sZRI81oWcu61RSKM66K6kUENP+78Qm8mvroBcm1whw==
"@firebase/firestore@3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-3.8.4.tgz#66b057330a22f0cd240b60746f2b2920b10dee31"
integrity sha512-sNLT4vGBSrx75Q2yLzCHL/1LDS7+UG8gaIohox/GpKYGxt4r8/AsUOmjN4llDqdnFSgY5ePYp2+nHArFXHyZjA==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
"@firebase/webchannel-wrapper" "0.9.0"
"@grpc/grpc-js" "~1.7.0"
"@grpc/proto-loader" "^0.6.13"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/functions-compat@0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.3.4.tgz#2b37321d893e816fec80435bb7cbca90f293bc0d"
integrity sha512-kxVxTGyLV1MBR3sp3mI+eQ6JBqz0G5bk310F8eX4HzDFk4xjk5xY0KdHktMH+edM2xs1BOg0vwvvsAHczIjB+w==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/functions" "0.9.4"
"@firebase/functions-types" "0.6.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/functions-types@0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.6.0.tgz#ccd7000dc6fc668f5acb4e6a6a042a877a555ef2"
integrity sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==
"@firebase/functions@0.9.4":
version "0.9.4"
resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.9.4.tgz#47232500be6847f1c7d3fa74eb36f621bd01a160"
integrity sha512-3H2qh6U+q+nepO5Hds+Ddl6J0pS+zisuBLqqQMRBHv9XpWfu0PnDHklNmE8rZ+ccTEXvBj6zjkPfdxt6NisvlQ==
dependencies:
"@firebase/app-check-interop-types" "0.2.0"
"@firebase/auth-interop-types" "0.2.1"
"@firebase/component" "0.6.4"
"@firebase/messaging-interop-types" "0.2.0"
"@firebase/util" "1.9.3"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/installations-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/installations-compat/-/installations-compat-0.2.4.tgz#b5557c897b4cd3635a59887a8bf69c3731aaa952"
integrity sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/installations" "0.6.4"
"@firebase/installations-types" "0.5.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/installations-types@0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.5.0.tgz#2adad64755cd33648519b573ec7ec30f21fb5354"
integrity sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==
"@firebase/installations@0.6.4":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.6.4.tgz#20382e33e6062ac5eff4bede8e468ed4c367609e"
integrity sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/util" "1.9.3"
idb "7.0.1"
tslib "^2.1.0"
"@firebase/logger@0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.4.0.tgz#15ecc03c452525f9d47318ad9491b81d1810f113"
integrity sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==
dependencies:
tslib "^2.1.0"
"@firebase/messaging-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz#323ca48deef77065b4fcda3cfd662c4337dffcfd"
integrity sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/messaging" "0.12.4"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/messaging-interop-types@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz#6056f8904a696bf0f7fdcf5f2ca8f008e8f6b064"
integrity sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==
"@firebase/messaging@0.12.4":
version "0.12.4"
resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.12.4.tgz#ccb49df5ab97d5650c9cf5b8c77ddc34daafcfe0"
integrity sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/installations" "0.6.4"
"@firebase/messaging-interop-types" "0.2.0"
"@firebase/util" "1.9.3"
idb "7.0.1"
tslib "^2.1.0"
"@firebase/performance-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.2.4.tgz#95cbf32057b5d9f0c75d804bc50e6ed3ba486274"
integrity sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/performance" "0.6.4"
"@firebase/performance-types" "0.2.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/performance-types@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.2.0.tgz#400685f7a3455970817136d9b48ce07a4b9562ff"
integrity sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==
"@firebase/performance@0.6.4":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.6.4.tgz#0ad766bfcfab4f386f4fe0bef43bbcf505015069"
integrity sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/installations" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/remote-config-compat@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz#1f494c81a6c9560b1f9ca1b4fbd4bbbe47cf4776"
integrity sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/remote-config" "0.4.4"
"@firebase/remote-config-types" "0.3.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/remote-config-types@0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz#689900dcdb3e5c059e8499b29db393e4e51314b4"
integrity sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==
"@firebase/remote-config@0.4.4":
version "0.4.4"
resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.4.4.tgz#6a496117054de58744bc9f382d2a6d1e14060c65"
integrity sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/installations" "0.6.4"
"@firebase/logger" "0.4.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/storage-compat@0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.3.2.tgz#51a97170fd652a516f729f82b97af369e5a2f8d7"
integrity sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/storage" "0.11.2"
"@firebase/storage-types" "0.8.0"
"@firebase/util" "1.9.3"
tslib "^2.1.0"
"@firebase/storage-types@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.8.0.tgz#f1e40a5361d59240b6e84fac7fbbbb622bfaf707"
integrity sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==
"@firebase/storage@0.11.2":
version "0.11.2"
resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.11.2.tgz#c5e0316543fe1c4026b8e3910f85ad73f5b77571"
integrity sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==
dependencies:
"@firebase/component" "0.6.4"
"@firebase/util" "1.9.3"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/util@1.9.3":
version "1.9.3"
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.9.3.tgz#45458dd5cd02d90e55c656e84adf6f3decf4b7ed"
integrity sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==
dependencies:
tslib "^2.1.0"
"@firebase/webchannel-wrapper@0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.9.0.tgz#9340bce56560a8bdba1d25d6281d4bfc397450dc"
integrity sha512-BpiZLBWdLFw+qFel9p3Zs1jD6QmH7Ii4aTDu6+vx8ShdidChZUXqDhYJly4ZjSgQh54miXbBgBrk0S+jTIh/Qg==
"@grpc/grpc-js@~1.7.0":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.7.3.tgz#f2ea79f65e31622d7f86d4b4c9ae38f13ccab99a"
integrity sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==
dependencies:
"@grpc/proto-loader" "^0.7.0"
"@types/node" ">=12.12.47"
"@grpc/proto-loader@^0.6.13":
version "0.6.13"
resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc"
integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==
dependencies:
"@types/long" "^4.0.1"
lodash.camelcase "^4.3.0"
long "^4.0.0"
protobufjs "^6.11.3"
yargs "^16.2.0"
"@grpc/proto-loader@^0.7.0":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.5.tgz#ee9e7488fa585dc6b0f7fe88cd39723a3e64c906"
integrity sha512-mfcTuMbFowq1wh/Rn5KQl6qb95M21Prej3bewD9dUQMurYGVckGO/Pbe2Ocwto6sD05b/mxZLspvqwx60xO2Rg==
dependencies:
"@types/long" "^4.0.1"
lodash.camelcase "^4.3.0"
long "^4.0.0"
protobufjs "^7.0.0"
yargs "^16.2.0"
"@humanwhocodes/config-array@^0.11.6":
version "0.11.7"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
......@@ -1579,6 +1981,59 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
"@protobufjs/base64@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
"@protobufjs/codegen@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
"@protobufjs/eventemitter@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
"@protobufjs/fetch@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
dependencies:
"@protobufjs/aspromise" "^1.1.1"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/float@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
"@protobufjs/inquire@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
"@protobufjs/path@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
"@protobufjs/pool@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
"@protobufjs/utf8@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@redux-saga/core@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.2.2.tgz#99b1daac93a42feecd9bab449f452f56f3155fea"
......@@ -2083,6 +2538,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/long@^4.0.1":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
"@types/mime@*":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
......@@ -2093,6 +2553,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.14.tgz#a8571b25f3a31e9ded14e3ab9488509adef831d8"
integrity sha512-0KXV57tENYmmJMl+FekeW9V3O/rlcqGQQJ/hNh9r8pKIj304pskWuEd8fCyNT86g/TpO0gcOTiLzsHLEURFMIQ==
"@types/node@>=12.12.47", "@types/node@>=13.7.0":
version "18.15.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014"
integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==
"@types/node@^16.18.8":
version "16.18.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.8.tgz#ffb2a4efa4eb4384811081776c52b054481cca54"
......@@ -2135,6 +2600,13 @@
dependencies:
"@types/react" "*"
"@types/react-modal@^3.13.1":
version "3.13.1"
resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.13.1.tgz#5b9845c205fccc85d9a77966b6e16dc70a60825a"
integrity sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^18.0.26":
version "18.0.26"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
......@@ -4443,6 +4915,11 @@ execa@^5.0.0:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
exenv@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
......@@ -4544,7 +5021,7 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
faye-websocket@^0.11.3:
faye-websocket@0.11.4, faye-websocket@^0.11.3:
version "0.11.4"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
......@@ -4637,6 +5114,38 @@ find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
firebase@^9.17.2:
version "9.17.2"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-9.17.2.tgz#950720666b1628a0b16926fbea4fc4217bd66185"
integrity sha512-2V95/evwB3zsi6RYHCvPXfkiQrSepFQJohv3YGoQVhS0bvXuYXmkLtrCVGShxneB/5t9HE5C9q9C8XPnK4APBw==
dependencies:
"@firebase/analytics" "0.9.4"
"@firebase/analytics-compat" "0.2.4"
"@firebase/app" "0.9.4"
"@firebase/app-check" "0.6.4"
"@firebase/app-check-compat" "0.3.4"
"@firebase/app-compat" "0.2.4"
"@firebase/app-types" "0.9.0"
"@firebase/auth" "0.21.4"
"@firebase/auth-compat" "0.3.4"
"@firebase/database" "0.14.4"
"@firebase/database-compat" "0.3.4"
"@firebase/firestore" "3.8.4"
"@firebase/firestore-compat" "0.3.4"
"@firebase/functions" "0.9.4"
"@firebase/functions-compat" "0.3.4"
"@firebase/installations" "0.6.4"
"@firebase/installations-compat" "0.2.4"
"@firebase/messaging" "0.12.4"
"@firebase/messaging-compat" "0.2.4"
"@firebase/performance" "0.6.4"
"@firebase/performance-compat" "0.2.4"
"@firebase/remote-config" "0.4.4"
"@firebase/remote-config-compat" "0.2.4"
"@firebase/storage" "0.11.2"
"@firebase/storage-compat" "0.3.2"
"@firebase/util" "1.9.3"
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
......@@ -5126,6 +5635,11 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
idb@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.1.tgz#d2875b3a2f205d854ee307f6d196f246fea590a7"
integrity sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==
idb@^7.0.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
......@@ -6273,6 +6787,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
......@@ -6303,7 +6822,17 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.4.0:
long@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
long@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f"
integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
......@@ -6521,6 +7050,13 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@^1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
......@@ -7522,7 +8058,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.6.2, prop-types@^15.8.1:
prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
......@@ -7531,6 +8067,43 @@ prop-types@^15.6.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
protobufjs@^6.11.3:
version "6.11.3"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74"
integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
"@protobufjs/codegen" "^2.0.4"
"@protobufjs/eventemitter" "^1.1.0"
"@protobufjs/fetch" "^1.1.0"
"@protobufjs/float" "^1.0.2"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/path" "^1.1.2"
"@protobufjs/pool" "^1.1.0"
"@protobufjs/utf8" "^1.1.0"
"@types/long" "^4.0.1"
"@types/node" ">=13.7.0"
long "^4.0.0"
protobufjs@^7.0.0:
version "7.2.2"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.2.tgz#2af401d8c547b9476fb37ffc65782cf302342ca3"
integrity sha512-++PrQIjrom+bFDPpfmqXfAGSQs40116JRrqqyf53dymUMvvb5d/LMRyicRoF1AUKoXVS1/IgJXlEgcpr4gTF3Q==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
"@protobufjs/codegen" "^2.0.4"
"@protobufjs/eventemitter" "^1.1.0"
"@protobufjs/fetch" "^1.1.0"
"@protobufjs/float" "^1.0.2"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/path" "^1.1.2"
"@protobufjs/pool" "^1.1.0"
"@protobufjs/utf8" "^1.1.0"
"@types/node" ">=13.7.0"
long "^5.0.0"
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
......@@ -7680,11 +8253,21 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-lifecycles-compat@^3.0.4:
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-modal@^3.16.1:
version "3.16.1"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b"
integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==
dependencies:
exenv "^1.2.0"
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"
react-redux@^8.0.5:
version "8.0.5"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd"
......@@ -8823,6 +9406,11 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tryer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
......@@ -8848,6 +9436,11 @@ tslib@^2.0.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tslib@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
......@@ -9086,6 +9679,13 @@ walker@^1.0.7:
dependencies:
makeerror "1.0.12"
warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
dependencies:
loose-envify "^1.0.0"
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
......@@ -9106,6 +9706,11 @@ web-vitals@^2.1.4:
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c"
integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
......@@ -9257,6 +9862,14 @@ whatwg-mimetype@^2.3.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment