diff --git a/.gitignore b/.gitignore index c309336..639fc78 100644 --- a/.gitignore +++ b/.gitignore @@ -94,4 +94,5 @@ out/ user_data/ *keys -/keys \ No newline at end of file +/keys +*questions* \ No newline at end of file diff --git a/forge.config.ts b/forge.config.ts index 6047fdc..5e47f5c 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -1,14 +1,10 @@ import 'dotenv/config'; -import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives'; import type { ForgeConfig } from '@electron-forge/shared-types'; import { MakerRpm } from '@electron-forge/maker-rpm'; import { MakerZIP } from '@electron-forge/maker-zip'; -import { WebpackPlugin } from '@electron-forge/plugin-webpack'; -import { mainConfig } from './webpack.main.config'; import packageJson from './package.json'; import path from 'path'; -import { rendererConfig } from './webpack.renderer.config'; const config: ForgeConfig = { // hooks: { @@ -62,27 +58,7 @@ const config: ForgeConfig = { } } ], - plugins: [ - new AutoUnpackNativesPlugin({}), - new WebpackPlugin({ - mainConfig, - devContentSecurityPolicy: "default-src 'self' 'unsafe-eval' 'unsafe-inline' static: http: https: ws:", - renderer: { - config: rendererConfig, - entryPoints: [ - { - html: './src/index.html', - js: './src/renderer.ts', - name: 'main_window', - preload: { - js: './src/preload.ts', - }, - }, - ], - }, - }), - - ], + plugins: [], publishers: [ { name: '@electron-forge/publisher-github', diff --git a/package.json b/package.json index 10af39d..2d52234 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "aij", "productName": "AIJ", - "version": "0.1.5", + "version": "0.1.6", "description": "AI Jobs", "main": ".webpack/main", "homepage": "https://algojobs.ca", diff --git a/src/app/appx.tsx b/src/app/appx.tsx deleted file mode 100644 index 7bac225..0000000 --- a/src/app/appx.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { useEffect } from "react"; - -import { ISettings } from "./shared"; -import { useAppState } from "./hooks"; - -export const ClientProcess = () => { - const state = useAppState(); - - const [settings, setSettings] = React.useState( - state.settings || ({} as any) - ); - - const handleSettings = (field: string) => { - return (event: any) => { - setSettings({ ...settings, [field]: event.target.value }); - }; - }; - - const isListRunning = state.isListRunning; - const handleClick = async () => { - const startStop = `list:${isListRunning ? "stop" : "start"}`; - const myFunc = await (window as any).api.invoke(startStop, null); - }; - - const saveSettings = async () => { - await (window as any).api.invoke("settings:save", settings); - }; - - return ( -
-

Client Process

-

Count: {state.count}

-

List is running: {isListRunning ? "Yes" : "No"}

- - -

Settings

- -

Path

- - -

Key

- - -

Save settings

- -
- ); -}; diff --git a/src/app/auth/index.tsx b/src/app/auth/index.tsx deleted file mode 100644 index 7a578f9..0000000 --- a/src/app/auth/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { LayoutPageProps } from "../layout"; -import React from "react"; -import _get from "lodash/get"; -import { isEmpty } from "lodash"; -// import logo from "../../assets/icon.png"; - -const parseMesaage = (message: string) => { - if (message.includes("credit")) { - return "You don't have enough credits, please add more credits to continue"; - } - if (message.includes("auth")) { - return "Please login to continue"; - } - return "Something went wrong, please try again later"; -}; - -export const AuthPage = ({ state }: LayoutPageProps) => { - const authMessage = _get(state, "auth.res.message", ""); - - const openAuthLink = async () => { - await (window as any).api.invoke("open:link", null); - }; - - return ( -
- - - {!isEmpty(authMessage) && ( -
-
{parseMesaage(authMessage)}
-
- )} - -
- -
-
- ); -}; - -export default AuthPage; diff --git a/src/app/dashboard/applications.view.tsx b/src/app/dashboard/applications.view.tsx deleted file mode 100644 index 3afaee6..0000000 --- a/src/app/dashboard/applications.view.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Answer, QuestionAnswer } from "../questions/interfaces"; -import { Application, BEState } from "../../utils/state"; -import { List, ListItem, Metric, Title } from "@tremor/react"; -import React, { useEffect, useLayoutEffect } from "react"; -import { UseQuestions, useQuestions } from "../questions/list"; -import { isEmpty, uniqBy } from "lodash"; - -import { RenderEditQuestion } from "../questions/question"; - -interface ApplicationsViewProps { - skipped?: boolean; - state: BEState; - useQuestions: UseQuestions; -} -export const ApplicationsViews = (props: ApplicationsViewProps) => { - const { state, useQuestions, skipped = false } = props; - const [selectedApp, setSelectedApp] = React.useState(null); - const { completedApps = [], skippedApps = [] } = state; - - const apps = uniqBy(skipped ? skippedApps : completedApps, "job.id"); - - const reApply = async (app: Application) => { - const job = app.job; - await (window as any).api.invoke("app:start:ondemand", job); - }; - - const moveToCompleted = async (app: Application) => { - await (window as any).api.invoke("app:complete", app); - }; - - return ( - <> - {apps.length > 0 && ( -
- {/* JOB */} - - {/* Questions */} - - {/* Apps */} -
- - {" "} - {skipped ? "Skipped" : "Completed"} applications {apps.length}{" "} - - - {apps.map((item, index) => { - const isSelectedJob = selectedApp?.job?.id === item.job.id; - - return ( -
- setSelectedApp(item)} - // key={item.question.inputId} - className={`${ - isSelectedJob ? "bg-gray-200" : "" - } hover:bg-gray-100 cursor-pointer px-2`} - > - - {index + 1} ({item.questions.length}) {item.job.title} -{" "} - {item.job.company} - - -
- ); - })} -
-
- - {selectedApp && ( -
-
-
- - {selectedApp.job.title} - {selectedApp.job.company} - -
- - {skipped && ( -
- - - -
- )} -
- - {selectedApp.questions.map((question, index) => ( - - ))} -
- )} -
- )} - - ); -}; - -export default ApplicationsViews; diff --git a/src/app/dashboard/index.tsx b/src/app/dashboard/index.tsx deleted file mode 100644 index 89082eb..0000000 --- a/src/app/dashboard/index.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import { - Bold, - Button, - Card, - Flex, - Grid, - Metric, - Text, - Title, -} from "@tremor/react"; -import { - CheckCircleIcon, - CheckIcon, - ForwardIcon, - QuestionMarkCircleIcon, - SparklesIcon, - StopIcon, -} from "@heroicons/react/20/solid"; -import { - ProgressBar, - Tab, - TabGroup, - TabList, - TabPanel, - TabPanels, -} from "@tremor/react"; - -import { AppJob } from "../../utils/state"; -import ApplicationsViews from "./applications.view"; -import { LayoutPageProps } from "../layout"; -import QuestionsError from "./questions.error"; -import QuestionsNew from "./questions.new"; -import React from "react"; -import { useQuestions } from "../questions/list"; - -export const Dashboard = ({ state }: LayoutPageProps) => { - const { - // activeJob, - apps = [], - completedApps = [], - skippedApps = [], - isListRunning, - isAppRunning, - jobs = [], - applied = [], - questions = [], - activeJob, - } = state; - - const useQuestionState = useQuestions(); - - const { - questions: savedQuestions, - selectedQuestion, - setSelectedQuestion, - handleChangeAnswer, - handleSaveQuestion, - } = useQuestionState; - - const errorQuestions = savedQuestions.filter((q) => !!q.chainRes.error) || []; - const newQuestions = savedQuestions.filter((q) => q.isNew) || []; - - const searchUrl = "https://ca.indeed.com/jobs?q="; - - const [search, setSearch] = React.useState(searchUrl); - - const handleSearch = (search: string) => { - setSearch(search); - }; - - const invokeEvent = async (eventName: string, args?: any) => { - await (window as any).api.invoke(eventName, args); - }; - - const appStop = async (job: AppJob) => { - const app = job - ? (apps || []).find((ap) => ap?.job?.id === job?.id) || { - job, - questions: [] as any, - } - : null; - - await (window as any).api.invoke("app:stop", app); - }; - - const skipJob = async (job: AppJob) => { - const app = job - ? (apps || []).find((ap) => ap.job.id === job.id) || { - job, - questions: [] as any, - } - : null; - - await (window as any).api.invoke("app:skip", app); - }; - - const moveToCompleted = async (job: AppJob) => { - const app = job - ? (apps || []).find((ap) => ap.job.id === job.id) || { - job, - questions: [] as any, - } - : null; - await (window as any).api.invoke("app:complete", app); - }; - - const allEvents = [ - // { title: "Main", name: "list", isRunning: isListRunning, args: searchUrl }, - { - title: "Applications", - name: "app", - isRunning: isAppRunning, - applications: applied, - }, - ]; - - return ( - - -
-
- Jobs -
- {jobs.length} -
-
- -
- handleSearch(e.target.value)} - /> -
- -
- - -
- {isListRunning && ( - - )} -
-
-
- -
-
- Applications -
- {applied.length} -
-
- - {(activeJob || isAppRunning) && ( -
-
- {activeJob?.title} - {activeJob?.company} -
- -
- - - - - - - -
-
- )} - - {!activeJob && !isAppRunning && ( -
- -
- )} -
-
- -
- - - - New {newQuestions.length && newQuestions.length} - - - Error {errorQuestions.length && errorQuestions.length} - - }> - Skipped {skippedApps.length && skippedApps.length} - - - Completed {completedApps.length && completedApps.length} - - - - - - - - - - - - - - - - - -
-
- ); -}; - -export default Dashboard; diff --git a/src/app/dashboard/questions.error.tsx b/src/app/dashboard/questions.error.tsx deleted file mode 100644 index 49dbe09..0000000 --- a/src/app/dashboard/questions.error.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { List, ListItem, Metric, Title } from "@tremor/react"; - -import { QuestionAnswer } from "../questions/interfaces"; -import { RenderQuestion } from "../questions/question"; -import { UseQuestions } from "../questions/list"; -import { isEmpty } from "lodash"; - -export const QuestionsError = (props: UseQuestions) => { - const { - questions: savedQuestions, - selectedQuestion, - setSelectedQuestion, - handleChangeAnswer, - handleSaveQuestion, - } = props; - - const errorQuestions = savedQuestions.filter((q) => !!q.chainRes.error); - - return ( - <> - {errorQuestions.length > 0 && ( -
-
- Error questions {errorQuestions.length} - - {errorQuestions.map((item, index) => ( -
- setSelectedQuestion(item as QuestionAnswer)} - key={item.question.inputId} - className={` hover:bg-gray-100 cursor-pointer px-2`} - > - - {index + 1} {item.question.question} - - {/* {item.isNew && New} */} - - -
- {isEmpty(item.chainRes.text) || item.chainRes.text === "." - ? "Error with question" - : item.chainRes.text} -
-
- ))} -
-
- - {selectedQuestion && ( -
-
- {selectedQuestion.question.question} -
- - -
- )} -
- )} - - ); -}; - -export default QuestionsError; diff --git a/src/app/dashboard/questions.new.tsx b/src/app/dashboard/questions.new.tsx deleted file mode 100644 index 5ed4248..0000000 --- a/src/app/dashboard/questions.new.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Badge, List, ListItem, Metric, Title } from "@tremor/react"; - -import { QuestionAnswer } from "../questions/interfaces"; -import { RenderQuestion } from "../questions/question"; -import { UseQuestions } from "../questions/list"; - -export const QuestionsNew = (props: UseQuestions) => { - const { - questions: savedQuestions, - selectedQuestion, - setSelectedQuestion, - handleChangeAnswer, - } = props; - - const newQuestions = savedQuestions.filter((q) => q.isNew); - - return ( - <> - {newQuestions.length > 0 && ( -
-
- New questions {newQuestions.length} - - {newQuestions.map((item, index) => ( -
- setSelectedQuestion(item as QuestionAnswer)} - key={item.question.inputId} - className={` hover:bg-gray-100 cursor-pointer px-2`} - > - - {index + 1} {item.question.question} - - {item.isNew && New} - -
- ))} -
-
- - {selectedQuestion && ( -
-
- {selectedQuestion.question.question} -
- - -
- )} -
- )} - - ); -}; - -export default QuestionsNew; diff --git a/src/app/hooks.tsx b/src/app/hooks.tsx deleted file mode 100644 index 44e7499..0000000 --- a/src/app/hooks.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { useEffect } from "react"; - -import { BEState } from "../utils/state"; - -export const useAppState = (): BEState => { - const [state, setState] = React.useState({ - apps: [], - completedApps: [], - skippedApps: [], - count: 0, - jobs: [], - applied: [], - questions: [], - settings: { key: "", path: "", speedApply: 1000, speedJobs: 1000 }, - activeJob: null, - }); - - const getState = async () => { - const appstate = await (window as any).api.invoke("state"); - setState(appstate); - return appstate; - }; - - useEffect(() => { - const intervalId = setInterval(() => { - getState().then((fetchedData) => { - // console.log("fetchedData", fetchedData); - }); - }, 1000); - - // Cleanup function to clear the interval when the component is unmounted or dependencies change - return () => clearInterval(intervalId); - }, []); // Empty dependency array means this effect runs once on mount and cleanup on unmount - - return state as BEState; -}; diff --git a/src/app/index.tsx b/src/app/index.tsx deleted file mode 100644 index c14e023..0000000 --- a/src/app/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { ClientProcess } from "./appx"; -import { Layout } from "./layout"; -import Navbar from "./navbar"; -import { createRoot } from "react-dom/client"; - -const root = createRoot(document.querySelector("#app")!); -root.render( -
- -
-); diff --git a/src/app/layout.tsx b/src/app/layout.tsx deleted file mode 100644 index 789e3d3..0000000 --- a/src/app/layout.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import AuthPage from "./auth"; -import { BEState } from "../utils/state/state.interfaces"; -import Dashboard from "./dashboard"; -import Navbar from "./navbar"; -import Questions from "./questions"; -import React from "react"; -import Resume from "./resume"; -import Settings from "./settings"; -import _get from "lodash/get"; -import { isEmpty } from "lodash"; -import { useAppState } from "./hooks"; - -export interface LayoutPageProps { - state: BEState; -} - -const NotFound = () => { - return
Not Found
; -}; - -const routes = [ - { path: "", component: Dashboard }, - { path: "questions", component: Questions }, - { path: "account", component: Settings }, - { path: "resume", component: Resume }, - { path: "*", component: NotFound }, -]; - -export const useHashState = () => { - const [hash, setHash] = React.useState(window.location.hash || ""); - React.useEffect(() => { - const handleHashChange = () => { - // console.log("hash change", window.location.hash); - setHash(window.location.hash); - }; - window.addEventListener("hashchange", handleHashChange); - return () => { - window.removeEventListener("hashchange", handleHashChange); - }; - }, []); - return hash; -}; - -export const Layout = () => { - const state = useAppState(); - const hash = useHashState(); - const isMain = isEmpty(hash); - const route = routes.find((r) => `#/${r.path}` === hash); - const Component: any = isMain - ? routes[0].component - : route - ? route.component - : NotFound; - - const isAuth = _get(state, "auth.res.success"); - - if (!state) { - return null; - } - - if (!isAuth) { - return ( -
- -
- ); - } - - return ( -
- -
- -
-
- ); -}; diff --git a/src/app/navbar.tsx b/src/app/navbar.tsx deleted file mode 100644 index 7957c9d..0000000 --- a/src/app/navbar.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; -import { Disclosure, Menu, Transition } from "@headlessui/react"; - -import { Fragment } from "react"; - -const navigation = [ - { name: "Dashboard", href: "/" }, - { name: "Questions", href: "/questions" }, - { name: "Resume", href: "/resume" }, - { name: "Account", href: "/account" }, -]; - -function classNames(...classes: string[]) { - return classes.filter(Boolean).join(" "); -} - -export default function Navbar({ - user, - ...props -}: { - user: any; - pathname: string; -}) { - const pathname = props.pathname.replace("#", ""); - const signOut = () => { - // console.log("sign out"); - }; - - const signIn = () => { - // console.log("sign in"); - }; - - return ( - - {({ open }) => ( - <> -
-
-
- {/*
- - - - -
*/} -
- - {navigation.map((item) => ( - - {item.name} - - ))} -
-
- - {/*
- -
- - Open user menu - {`${user?.name - -
- - - {user ? ( - - {({ active }) => ( - - )} - - ) : ( - - {({ active }) => ( - - )} - - )} - - -
-
-
- - Open main menu - {open ? ( - -
*/} -
-
- - {/* -
- {navigation.map((item) => ( - - {item.name} - - ))} -
-
- {user ? ( - <> -
-
- {`${user.name} -
-
-
- {user.name} -
-
- {user.email} -
-
-
-
- -
- - ) : ( -
- -
- )} -
-
*/} - - )} -
- ); -} diff --git a/src/app/questions/index.tsx b/src/app/questions/index.tsx deleted file mode 100644 index 78087de..0000000 --- a/src/app/questions/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import List from "./list"; -import React from "react"; - -export const QuestionsPage = () => { - return ( -
- -
- ); -}; -export default QuestionsPage; diff --git a/src/app/questions/interfaces.ts b/src/app/questions/interfaces.ts deleted file mode 100644 index e5b3a72..0000000 --- a/src/app/questions/interfaces.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface InputOption { - value: string; - label: string; -} - -export interface Answer { - inputId: string; - inputType: string; - answerText: string; -} - -export interface Question { - question: string; - inputId: string; - inputType: "input" | "text" | "radio" | "checkbox" | "select" | "textarea" | "fieldset"; - inputOptions?: InputOption[]; - inputTypeValue?: string; - answers?: Answer[]; -} - - -export interface QuestionAnswer { - question: Question; - chainRes: { - text: string; - error?: boolean; - [key: string]: any; - } - isNew?: boolean; - date?: Date; -} diff --git a/src/app/questions/list.sample.tsx b/src/app/questions/list.sample.tsx deleted file mode 100644 index 59d7929..0000000 --- a/src/app/questions/list.sample.tsx +++ /dev/null @@ -1,871 +0,0 @@ -export const questionSamples = [ - { - question: { - question: "Êtes-vous éligible pour travailler au Canada?", - inputId: "input-q_4e16b181f7f75a81edab7d4322707550", - inputType: "fieldset", - answers: [ - { - inputId: "input-q_4e16b181f7f75a81edab7d4322707550-0", - inputType: "radio", - answerText: "Yes", - }, - { - inputId: "input-q_4e16b181f7f75a81edab7d4322707550-1", - inputType: "radio", - answerText: "No", - }, - ], - }, - chainRes: { - text: "input-q_4e16b181f7f75a81edab7d4322707550-0", - }, - }, - - { - question: { - question: - "Do you have technical expertise in any of the following areas?(optional)", - inputId: "input-q_74e6547a6894e8856547fb4b8eddce7d", - inputType: "fieldset", - answers: [ - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-0", - inputType: "checkbox", - answerText: "Video Streaming", - }, - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-1", - inputType: "checkbox", - answerText: "Security", - }, - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-2", - inputType: "checkbox", - answerText: "Machine Learning", - }, - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-3", - inputType: "checkbox", - answerText: "Front-end Development", - }, - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-4", - inputType: "checkbox", - answerText: "Event-Driven Design", - }, - { - inputId: - "ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-5", - inputType: "checkbox", - answerText: "Miscroservices", - }, - ], - }, - chainRes: { - text: '"ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-0,ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-1,ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-2,ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-3,ifl-CheckboxFormField-ihl-useId-indeed-theme-provider-woxvrx-11-4"', - }, - }, - { - question: { - question: "City you reside in", - inputId: "input-q_7ae9d215d9398af70eba60d4030366bc", - inputType: "input", - inputTypeValue: "text", - }, - chainRes: { - text: "Toronto", - }, - }, - { - question: { - question: "MOBILE NUMBERCountry(optional)", - inputId: "input-q_0242786f825d665eacfcf82dbbe24b39", - inputType: "select", - inputOptions: [ - { - value: "CD +243", - text: "Congo, The Democratic Republic of the Congo (+243)", - }, - { - value: "CK +682", - text: "Cook Islands (+682)", - }, - { - value: "CR +506", - text: "Costa Rica (+506)", - }, - { - value: "CI +225", - text: "Cote d'Ivoire (+225)", - }, - { - value: "HR +385", - text: "Croatia (+385)", - }, - { - value: "CU +53", - text: "Cuba (+53)", - }, - { - value: "CY +357", - text: "Cyprus (+357)", - }, - { - value: "CZ +420", - text: "Czech Republic (+420)", - }, - { - value: "DK +45", - text: "Denmark (+45)", - }, - { - value: "DJ +253", - text: "Djibouti (+253)", - }, - { - value: "DM +1767", - text: "Dominica (+1767)", - }, - { - value: "DO +1849", - text: "Dominican Republic (+1849)", - }, - { - value: "EC +593", - text: "Ecuador (+593)", - }, - { - value: "EG +20", - text: "Egypt (+20)", - }, - { - value: "SV +503", - text: "El Salvador (+503)", - }, - { - value: "GQ +240", - text: "Equatorial Guinea (+240)", - }, - { - value: "ER +291", - text: "Eritrea (+291)", - }, - { - value: "EE +372", - text: "Estonia (+372)", - }, - { - value: "ET +251", - text: "Ethiopia (+251)", - }, - { - value: "FK +500", - text: "Falkland Islands (Malvinas) (+500)", - }, - { - value: "FO +298", - text: "Faroe Islands (+298)", - }, - { - value: "FJ +679", - text: "Fiji (+679)", - }, - { - value: "FI +358", - text: "Finland (+358)", - }, - { - value: "FR +33", - text: "France (+33)", - }, - { - value: "GF +594", - text: "French Guiana (+594)", - }, - { - value: "PF +689", - text: "French Polynesia (+689)", - }, - { - value: "GA +241", - text: "Gabon (+241)", - }, - { - value: "GM +220", - text: "Gambia (+220)", - }, - { - value: "GE +995", - text: "Georgia (+995)", - }, - { - value: "DE +49", - text: "Germany (+49)", - }, - { - value: "GH +233", - text: "Ghana (+233)", - }, - { - value: "GI +350", - text: "Gibraltar (+350)", - }, - { - value: "GR +30", - text: "Greece (+30)", - }, - { - value: "GL +299", - text: "Greenland (+299)", - }, - { - value: "GD +1473", - text: "Grenada (+1473)", - }, - { - value: "GP +590", - text: "Guadeloupe (+590)", - }, - { - value: "GU +1671", - text: "Guam (+1671)", - }, - { - value: "GT +502", - text: "Guatemala (+502)", - }, - { - value: "GG +44", - text: "Guernsey (+44)", - }, - { - value: "GN +224", - text: "Guinea (+224)", - }, - { - value: "GW +245", - text: "Guinea-Bissau (+245)", - }, - { - value: "GY +595", - text: "Guyana (+595)", - }, - { - value: "HT +509", - text: "Haiti (+509)", - }, - { - value: "VA +379", - text: "Holy See (Vatican City State) (+379)", - }, - { - value: "HN +504", - text: "Honduras (+504)", - }, - { - value: "HK +852", - text: "Hong Kong (+852)", - }, - { - value: "HU +36", - text: "Hungary (+36)", - }, - { - value: "IS +354", - text: "Iceland (+354)", - }, - { - value: "IN +91", - text: "India (+91)", - }, - { - value: "ID +62", - text: "Indonesia (+62)", - }, - { - value: "IR +98", - text: "Iran, Islamic Republic of Persian Gulf (+98)", - }, - { - value: "IQ +964", - text: "Iraq (+964)", - }, - { - value: "IE +353", - text: "Ireland (+353)", - }, - { - value: "IM +44", - text: "Isle of Man (+44)", - }, - { - value: "IL +972", - text: "Israel (+972)", - }, - { - value: "IT +39", - text: "Italy (+39)", - }, - { - value: "JM +1876", - text: "Jamaica (+1876)", - }, - { - value: "JP +81", - text: "Japan (+81)", - }, - { - value: "JE +44", - text: "Jersey (+44)", - }, - { - value: "JO +962", - text: "Jordan (+962)", - }, - { - value: "KZ +77", - text: "Kazakhstan (+77)", - }, - { - value: "KE +254", - text: "Kenya (+254)", - }, - { - value: "KI +686", - text: "Kiribati (+686)", - }, - { - value: "KP +850", - text: "Korea, Democratic People's Republic of Korea (+850)", - }, - { - value: "KR +82", - text: "Korea, Republic of South Korea (+82)", - }, - { - value: "KW +965", - text: "Kuwait (+965)", - }, - { - value: "KG +996", - text: "Kyrgyzstan (+996)", - }, - { - value: "LA +856", - text: "Laos (+856)", - }, - { - value: "LV +371", - text: "Latvia (+371)", - }, - { - value: "LB +961", - text: "Lebanon (+961)", - }, - { - value: "LS +266", - text: "Lesotho (+266)", - }, - { - value: "LR +231", - text: "Liberia (+231)", - }, - { - value: "LY +218", - text: "Libyan Arab Jamahiriya (+218)", - }, - { - value: "LI +423", - text: "Liechtenstein (+423)", - }, - { - value: "LT +370", - text: "Lithuania (+370)", - }, - { - value: "LU +352", - text: "Luxembourg (+352)", - }, - { - value: "MO +853", - text: "Macao (+853)", - }, - { - value: "MK +389", - text: "Macedonia (+389)", - }, - { - value: "MG +261", - text: "Madagascar (+261)", - }, - { - value: "MW +265", - text: "Malawi (+265)", - }, - { - value: "MY +60", - text: "Malaysia (+60)", - }, - { - value: "MV +960", - text: "Maldives (+960)", - }, - { - value: "ML +223", - text: "Mali (+223)", - }, - { - value: "MT +356", - text: "Malta (+356)", - }, - { - value: "MH +692", - text: "Marshall Islands (+692)", - }, - { - value: "MQ +596", - text: "Martinique (+596)", - }, - { - value: "MR +222", - text: "Mauritania (+222)", - }, - { - value: "MU +230", - text: "Mauritius (+230)", - }, - { - value: "YT +262", - text: "Mayotte (+262)", - }, - { - value: "MX +52", - text: "Mexico (+52)", - }, - { - value: "FM +691", - text: "Micronesia, Federated States of Micronesia (+691)", - }, - { - value: "MD +373", - text: "Moldova (+373)", - }, - { - value: "MC +377", - text: "Monaco (+377)", - }, - { - value: "MN +976", - text: "Mongolia (+976)", - }, - { - value: "ME +382", - text: "Montenegro (+382)", - }, - { - value: "MS +1664", - text: "Montserrat (+1664)", - }, - { - value: "MA +212", - text: "Morocco (+212)", - }, - { - value: "MZ +258", - text: "Mozambique (+258)", - }, - { - value: "MM +95", - text: "Myanmar (+95)", - }, - { - value: "NA +264", - text: "Namibia (+264)", - }, - { - value: "NR +674", - text: "Nauru (+674)", - }, - { - value: "NP +977", - text: "Nepal (+977)", - }, - { - value: "NL +31", - text: "Netherlands (+31)", - }, - { - value: "AN +599", - text: "Netherlands Antilles (+599)", - }, - { - value: "NC +687", - text: "New Caledonia (+687)", - }, - { - value: "NZ +64", - text: "New Zealand (+64)", - }, - { - value: "NI +505", - text: "Nicaragua (+505)", - }, - { - value: "NE +227", - text: "Niger (+227)", - }, - { - value: "NG +234", - text: "Nigeria (+234)", - }, - { - value: "NU +683", - text: "Niue (+683)", - }, - { - value: "NF +672", - text: "Norfolk Island (+672)", - }, - { - value: "MP +1670", - text: "Northern Mariana Islands (+1670)", - }, - { - value: "NO +47", - text: "Norway (+47)", - }, - { - value: "OM +968", - text: "Oman (+968)", - }, - { - value: "PK +92", - text: "Pakistan (+92)", - }, - { - value: "PW +680", - text: "Palau (+680)", - }, - { - value: "PS +970", - text: "Palestinian Territory, Occupied (+970)", - }, - { - value: "PA +507", - text: "Panama (+507)", - }, - { - value: "PG +675", - text: "Papua New Guinea (+675)", - }, - { - value: "PY +595", - text: "Paraguay (+595)", - }, - { - value: "PE +51", - text: "Peru (+51)", - }, - { - value: "PH +63", - text: "Philippines (+63)", - }, - { - value: "PN +872", - text: "Pitcairn (+872)", - }, - { - value: "PL +48", - text: "Poland (+48)", - }, - { - value: "PT +351", - text: "Portugal (+351)", - }, - { - value: "PR +1939", - text: "Puerto Rico (+1939)", - }, - { - value: "QA +974", - text: "Qatar (+974)", - }, - { - value: "RO +40", - text: "Romania (+40)", - }, - { - value: "RU +7", - text: "Russia (+7)", - }, - { - value: "RW +250", - text: "Rwanda (+250)", - }, - { - value: "RE +262", - text: "Reunion (+262)", - }, - { - value: "BL +590", - text: "Saint Barthelemy (+590)", - }, - { - value: "SH +290", - text: "Saint Helena, Ascension and Tristan Da Cunha (+290)", - }, - { - value: "KN +1869", - text: "Saint Kitts and Nevis (+1869)", - }, - { - value: "LC +1758", - text: "Saint Lucia (+1758)", - }, - { - value: "MF +590", - text: "Saint Martin (+590)", - }, - { - value: "PM +508", - text: "Saint Pierre and Miquelon (+508)", - }, - { - value: "VC +1784", - text: "Saint Vincent and the Grenadines (+1784)", - }, - { - value: "WS +685", - text: "Samoa (+685)", - }, - { - value: "SM +378", - text: "San Marino (+378)", - }, - { - value: "ST +239", - text: "Sao Tome and Principe (+239)", - }, - { - value: "SA +966", - text: "Saudi Arabia (+966)", - }, - { - value: "SN +221", - text: "Senegal (+221)", - }, - { - value: "RS +381", - text: "Serbia (+381)", - }, - { - value: "SC +248", - text: "Seychelles (+248)", - }, - { - value: "SL +232", - text: "Sierra Leone (+232)", - }, - { - value: "SG +65", - text: "Singapore (+65)", - }, - { - value: "SK +421", - text: "Slovakia (+421)", - }, - { - value: "SI +386", - text: "Slovenia (+386)", - }, - { - value: "SB +677", - text: "Solomon Islands (+677)", - }, - { - value: "SO +252", - text: "Somalia (+252)", - }, - { - value: "ZA +27", - text: "South Africa (+27)", - }, - { - value: "SS +211", - text: "South Sudan (+211)", - }, - { - value: "GS +500", - text: "South Georgia and the South Sandwich Islands (+500)", - }, - { - value: "ES +34", - text: "Spain (+34)", - }, - { - value: "LK +94", - text: "Sri Lanka (+94)", - }, - { - value: "SD +249", - text: "Sudan (+249)", - }, - { - value: "SR +597", - text: "Suriname (+597)", - }, - { - value: "SJ +47", - text: "Svalbard and Jan Mayen (+47)", - }, - { - value: "SZ +268", - text: "Swaziland (+268)", - }, - { - value: "SE +46", - text: "Sweden (+46)", - }, - { - value: "CH +41", - text: "Switzerland (+41)", - }, - { - value: "SY +963", - text: "Syrian Arab Republic (+963)", - }, - { - value: "TW +886", - text: "Taiwan (+886)", - }, - { - value: "TJ +992", - text: "Tajikistan (+992)", - }, - { - value: "TZ +255", - text: "Tanzania, United Republic of Tanzania (+255)", - }, - { - value: "TH +66", - text: "Thailand (+66)", - }, - { - value: "TL +670", - text: "Timor-Leste (+670)", - }, - { - value: "TG +228", - text: "Togo (+228)", - }, - { - value: "TK +690", - text: "Tokelau (+690)", - }, - { - value: "TO +676", - text: "Tonga (+676)", - }, - { - value: "TT +1868", - text: "Trinidad and Tobago (+1868)", - }, - { - value: "TN +216", - text: "Tunisia (+216)", - }, - { - value: "TR +90", - text: "Turkey (+90)", - }, - { - value: "TM +993", - text: "Turkmenistan (+993)", - }, - { - value: "TC +1649", - text: "Turks and Caicos Islands (+1649)", - }, - { - value: "TV +688", - text: "Tuvalu (+688)", - }, - { - value: "UG +256", - text: "Uganda (+256)", - }, - { - value: "UA +380", - text: "Ukraine (+380)", - }, - { - value: "AE +971", - text: "United Arab Emirates (+971)", - }, - { - value: "GB +44", - text: "United Kingdom (+44)", - }, - { - value: "US +1", - text: "United States (+1)", - }, - { - value: "UY +598", - text: "Uruguay (+598)", - }, - { - value: "UZ +998", - text: "Uzbekistan (+998)", - }, - { - value: "VU +678", - text: "Vanuatu (+678)", - }, - { - value: "VE +58", - text: "Venezuela, Bolivarian Republic of Venezuela (+58)", - }, - { - value: "VN +84", - text: "Vietnam (+84)", - }, - { - value: "VG +1284", - text: "Virgin Islands, British (+1284)", - }, - { - value: "VI +1340", - text: "Virgin Islands, U.S. (+1340)", - }, - { - value: "WF +681", - text: "Wallis and Futuna (+681)", - }, - { - value: "YE +967", - text: "Yemen (+967)", - }, - { - value: "ZM +260", - text: "Zambia (+260)", - }, - { - value: "ZW +263", - text: "Zimbabwe (+263)", - }, - ], - }, - chainRes: { - text: '"CA +1"', - }, - }, -]; - -export default questionSamples; diff --git a/src/app/questions/list.tsx b/src/app/questions/list.tsx deleted file mode 100644 index 922af2a..0000000 --- a/src/app/questions/list.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import { Answer, InputOption, QuestionAnswer } from "./interfaces"; -import { - Badge, - Card, - Col, - Grid, - List, - ListItem, - Metric, - MultiSelect, - MultiSelectItem, - SearchSelect, - SearchSelectItem, - Select, - SelectItem, - TextInput, - Textarea, - Title, -} from "@tremor/react"; -import { RenderEditQuestion, RenderQuestion } from "./question"; -import { isEmpty, sortBy, uniq, uniqBy } from "lodash"; - -import { MinusCircleIcon } from "@heroicons/react/20/solid"; -import React from "react"; -import _get from "lodash/get"; - -interface ListQuestionsProps {} -interface ListQuestionsState { - selectedQuestion: QuestionAnswer | null; - questions: QuestionAnswer[]; - search: string; -} - -export interface UseQuestions { - search: string; - selectedQuestion?: QuestionAnswer; - questions: QuestionAnswer[]; - handleChangeAnswer: (answer: Answer) => void; - handleSaveQuestion: (question: QuestionAnswer) => void; - clearSearch: () => void; - handleSearch: (search: string) => void; - setSelectedQuestion: (question: QuestionAnswer) => void; - - getAllQuestions: () => Promise; - // readQuestion: (question: QuestionAnswer) => Promise; - getFilteredQuestions: () => QuestionAnswer[]; -} - -export const useQuestions = (): UseQuestions => { - const [state, setState] = React.useState({ - selectedQuestion: null, - questions: [], - search: "", - }); - - const { selectedQuestion = null, questions = [], search } = state; - - const handleSearch = (search: string) => { - setState({ ...state, search }); - }; - - const clearSearch = () => { - setState({ ...state, search: "" }); - }; - - const getFilteredQuestions = () => { - let filteredQuestions = questions.filter( - (item) => - item && - item.question && - (item.question.question.toLowerCase() || "").includes( - search.toLowerCase() - ) - ); - - filteredQuestions = sortBy(filteredQuestions, "isNew"); - return sortBy(filteredQuestions, "date"); - }; - - const setSelectedQuestion = (question: QuestionAnswer) => { - setState({ - ...state, - selectedQuestion: question, - questions: questions.map((item) => { - if (item?.question?.inputId === question?.question?.inputId) { - return question; - } - return item; - }), - }); - handleSaveQuestion(question); - }; - - const setSelectedAnswerText = (answer: string) => { - const selQuestion = { - ...selectedQuestion, - chainRes: { text: answer }, - isNew: false, - }; - setSelectedQuestion(selQuestion); - }; - - const handleChangeAnswer = (answer: Answer) => { - // console.log("handleChangeAnswer answer", { - // ...answer, - // }); - - if (!selectedQuestion) return; - if (!answer) return; - const isCheckbox = answer.inputType === "checkbox"; - - const questionInputType = _get( - selectedQuestion, - "question.inputType", - "text" - ); - - const questionInputTypeValue = _get( - selectedQuestion, - "question.inputTypeValue", - "" - ); - - const answerInputId = _get(answer, "inputId", ""); - - const questionAnswerText = _get(selectedQuestion, "chainRes.text", ""); - - // console.log("handleChangeAnswer answer", { - // ...answer, - // isCheckbox, - // questionInputType, - // }); - - switch (questionInputType) { - case "fieldset": - if (isCheckbox) { - let questionAnswerTextJson: string[] = null; - try { - questionAnswerTextJson = JSON.parse(questionAnswerText); - } catch (error) {} - - // console.log("questionAnswerTextJson answer", { - // questionAnswerTextJson, - // }); - - if (questionAnswerTextJson && Array.isArray(questionAnswerTextJson)) { - // new format - if ( - questionAnswerTextJson.includes(answer.inputId) || - questionAnswerTextJson.includes(answer.answerText) - ) { - questionAnswerTextJson = questionAnswerTextJson - .filter((item) => item !== answer.inputId) - .filter((item) => item !== answer.answerText); - } else { - const curAnswer = answerInputId.replace('"', "").replace('"', ""); - - questionAnswerTextJson.push(curAnswer); - } - const newAnswerText = JSON.stringify(questionAnswerTextJson); - return setSelectedAnswerText(newAnswerText); - } else { - const curAnswer = questionAnswerText - .replace('"', "") - .replace('"', ""); - const newAnswerText = JSON.stringify([curAnswer]); - return setSelectedAnswerText(newAnswerText); - } - } - return setSelectedAnswerText(answer.inputId); - - default: - return setSelectedAnswerText(answer.answerText); - } - }; - - const getAllQuestions = async () => { - try { - let questionsApi = await (window as any).api.invoke("questions:getall"); - - // console.log("questionsApi", questionsApi); - - // questionsApi = JSON.parse(questionsApi); - - if (questionsApi) { - questionsApi = questionsApi.filter( - (item: QuestionAnswer) => - item && item.question && item.question.inputId - ); - - questionsApi = uniqBy(questionsApi, "question.inputId"); - - if (questionsApi.length) { - setState({ ...state, questions: questionsApi }); - } - } - - return questionsApi; - } catch (error) {} - }; - - React.useEffect(() => { - getAllQuestions(); - }, []); - - const handleSaveQuestion = async (selQuestion: QuestionAnswer) => { - const questToSave: QuestionAnswer = { - ...selQuestion, - chainRes: { - ...selQuestion.chainRes, - error: false, - }, - isNew: false, - }; - - const saveQuestion = await (window as any).api.invoke( - "questions:save", - questToSave - ); - - // const allquestions = questions.map((item) => { - // if (item.question.inputId === selectedQuestion.question.inputId) { - // return questToSave; - // } - // return item; - // }); - - // setState({ ...state, questions: allquestions }); - - // await getAllQuestions(); - return saveQuestion; - }; - - const filteredQuestions = getFilteredQuestions(); - - return { - search, - handleSearch, - clearSearch, - selectedQuestion, - questions: state.questions, - handleChangeAnswer, - handleSaveQuestion, - setSelectedQuestion, - getFilteredQuestions, - getAllQuestions, - }; -}; - -export const ListQuestions = () => { - const { - handleSaveQuestion, - getFilteredQuestions, - selectedQuestion = null, - questions = [], - search, - handleSearch, - clearSearch, - setSelectedQuestion, - handleChangeAnswer, - getAllQuestions, - } = useQuestions(); - - React.useEffect(() => { - getAllQuestions(); - }, []); - - const filteredQuestions = getFilteredQuestions(); - - return ( - - Questions {questions.length} -
-
-
- handleSearch(e.target.value)} - /> - - {!isEmpty(search) && ( -
clearSearch()} - > - -
- )} -
- - - {filteredQuestions.map((item) => { - const selectedQuesId = _get(selectedQuestion, "question.inputId"); - const currentQuesId = _get(item, "question.inputId"); - const isSelected = selectedQuesId === currentQuesId; - return ( - setSelectedQuestion(item as QuestionAnswer)} - key={item.question.inputId} - className={` hover:bg-gray-100 cursor-pointer px-2 ${ - isSelected ? "bg-gray-200" : "" - }`} - > - {item.question.question} - {item.isNew && New} - - ); - })} - -
- - {selectedQuestion && ( -
-
- {selectedQuestion.question.question} -
- -
- -
- - {/*
- -
*/} -
- )} -
-
- ); -}; - -export default ListQuestions; diff --git a/src/app/questions/question.tsx b/src/app/questions/question.tsx deleted file mode 100644 index 7eef261..0000000 --- a/src/app/questions/question.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import { Answer, InputOption, QuestionAnswer } from "./interfaces"; -import { - List, - SearchSelect, - SearchSelectItem, - TextInput, - Textarea, - Title, -} from "@tremor/react"; -import { UseQuestions, useQuestions } from "./list"; - -import _get from "lodash/get"; -import { useEffect } from "react"; - -interface ICheckbox { - title: string; - checked: boolean; - id: string; - handleClick: () => void; -} - -const RenderCheckbox = ({ - title, - checked = false, - id, - handleClick, - ...otherProps -}: ICheckbox) => { - return ( -
- - -
- ); -}; - -export interface IRenderQuestion { - autosave?: boolean; - question: QuestionAnswer; - handleChangeAnswer: (answer: Answer) => void; -} -export const RenderQuestion = ({ - autosave = false, - question, - handleChangeAnswer, -}: IRenderQuestion) => { - // console.log("question", question); - const questionInputType = _get(question, "question.inputType", "text"); - const questionInputTypeValue = _get( - question, - "question.inputTypeValue", - "text" - ); - const isCheckbox = questionInputTypeValue === "checkbox"; - const questionAnswerText = _get(question, "chainRes.text", ""); - const questionAnswerError = _get(question, "chainRes.error", false); - - switch (questionInputType) { - case "fieldset": - const questionAnswers = question.question.answers || []; - return ( -
-
- {questionAnswerError && ( -
Please select an answer
- )} -
- - {questionAnswers.map((answer) => { - const chainResText = _get(question, "chainRes.text", ""); - const chainResJson = _get(question, "chainRes.json", null); - let isChecked = false; - let chainResTextJson: string[] = []; - - if (isCheckbox) { - if (chainResJson) { - chainResTextJson = chainResJson; - } else { - try { - chainResTextJson = JSON.parse(chainResText); - } catch (error) {} - } - - if (chainResTextJson && Array.isArray(chainResTextJson)) { - isChecked = chainResTextJson.some( - (x: string) => - x === answer?.inputId || x === answer?.answerText - ); - } - } else { - // radio single value - isChecked = chainResText.includes(answer.inputId); - } - - return ( - handleChangeAnswer(answer)} - id={answer.inputId || answer.answerText} - key={answer.inputId || answer.answerText} - title={answer.answerText} - /> - ); - })} - -
- ); - - case "select": - const questionInputOptions: InputOption[] = - question?.question?.inputOptions || []; - - // console.log("questionInputOptions", questionInputOptions); - - const selectQuesTextAnswer = questionAnswerText - .replace('"', "") - .replace('"', ""); - - return ( -
-
- {questionAnswerError && ( -
Please select an answer
- )} -
- { - const value = e; - const answer = { - inputId: question.question.inputId, - inputType: question.question.inputType, - answerText: value, - }; - handleChangeAnswer(answer); - }} - > - {questionInputOptions.map((option) => ( - - {option.label || (option as any).text} - - ))} - -
- ); - - case "text": - default: - const isNumber = questionInputTypeValue === "number"; - return ( - <> -
- {questionAnswerError && ( -
- Please select enter the correct answer or update resume to cover - this question -
- )} -
- {isNumber ? ( - { - const answer = { - inputId: question.question.inputId, - inputType: question.question.inputType, - answerText: e.target.value, - }; - handleChangeAnswer(answer); - }} - /> - ) : ( -