Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added modal to select source documentation #103

Merged
merged 20 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
export default function About() {
return (
//Parent div for all content shown through App.tsx routing needs to have this styling. Might change when state management is updated.
<div className="grid min-h-screen">
<div className="mx-6 grid min-h-screen">
<article className=" mx-auto my-auto flex w-full max-w-6xl flex-col place-items-center gap-6 rounded-lg bg-gray-100 p-6 text-jet lg:p-10 xl:p-16">
<p className="text-3xl font-semibold">About DocsGPT 🦖</p>
<p className="mt-4 text-xl font-bold">
Expand Down
32 changes: 29 additions & 3 deletions frontend/src/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import Info from './assets/info.svg';
import Link from './assets/link.svg';
import { ActiveState } from './models/misc';
import APIKeyModal from './preferences/APIKeyModal';
import SelectDocsModal from './preferences/SelectDocsModal';
import { useSelector } from 'react-redux';
import { selectApiKeyStatus } from './preferences/preferenceSlice';
import {
selectApiKeyStatus,
selectSelectedDocsStatus,
} from './preferences/preferenceSlice';
import { useState } from 'react';

//TODO - Need to replace Chat button to open secondary nav with scrollable past chats option and new chat at top
Expand All @@ -24,11 +28,16 @@ export default function Navigation({
const [apiKeyModalState, setApiKeyModalState] = useState<ActiveState>(
isApiKeySet ? 'INACTIVE' : 'ACTIVE',
);

const isSelectedDocsSet = useSelector(selectSelectedDocsStatus);
const [selectedDocsModalState, setSelectedDocsModalState] =
useState<ActiveState>(isSelectedDocsSet ? 'INACTIVE' : 'ACTIVE');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we gotta put the two modal opening in sequence, can do it in the next PR.


return (
<>
<div
className={`${
navState === 'INACTIVE' && '-ml-96 md:-ml-60 lg:-ml-80'
navState === 'INACTIVE' && '-ml-96 md:-ml-[14rem] lg:-ml-80'
} fixed z-10 flex h-full w-72 flex-col border-r-2 border-gray-100 bg-gray-50 transition-all duration-200 lg:w-96`}
>
<div className={'h-16 w-full border-b-2 border-gray-100'}>
Expand All @@ -49,7 +58,7 @@ export default function Navigation({
</div>
<div className="flex-grow border-b-2 border-gray-100"></div>

<div className="flex h-16 flex-col border-b-2 border-gray-100">
<div className="flex flex-col gap-2 border-b-2 border-gray-100 py-2">
<div
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
onClick={() => {
Expand All @@ -59,6 +68,18 @@ export default function Navigation({
<img src={Key} alt="key" className="ml-2 w-6" />
<p className="my-auto text-eerie-black">Reset Key</p>
</div>

<div
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
onClick={() => {
setSelectedDocsModalState('ACTIVE');
}}
>
<img src={Link} alt="key" className="ml-2 w-5" />
<p className="my-auto text-eerie-black">
Select Source Documentation
</p>
</div>
</div>

<div className="flex h-48 flex-col border-b-2 border-gray-100">
Expand Down Expand Up @@ -87,6 +108,11 @@ export default function Navigation({
>
<img src={Hamburger} alt="menu toggle" className="w-7" />
</button>
<SelectDocsModal
modalState={selectedDocsModalState}
setModalState={setSelectedDocsModalState}
isCancellable={isSelectedDocsSet}
/>
<APIKeyModal
modalState={apiKeyModalState}
setModalState={setApiKeyModalState}
Expand Down
132 changes: 132 additions & 0 deletions frontend/src/preferences/SelectDocsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ActiveState } from '../models/misc';
import {
setSelectedDocs,
setSourceDocs,
selectSourceDocs,
} from './preferenceSlice';
import { getDocs, Doc } from './selectDocsApi';

export default function APIKeyModal({
modalState,
setModalState,
isCancellable = true,
}: {
modalState: ActiveState;
setModalState: (val: ActiveState) => void;
isCancellable?: boolean;
}) {
const dispatch = useDispatch();
const docs = useSelector(selectSourceDocs);
const [localSelectedDocs, setLocalSelectedDocs] = useState<Doc | null>(null);
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
const [isError, setIsError] = useState(false);

function handleSubmit() {
if (!localSelectedDocs) {
setIsError(true);
} else {
dispatch(setSelectedDocs(localSelectedDocs));
setModalState('INACTIVE');
setLocalSelectedDocs(null);
setIsError(false);
}
}

function handleCancel() {
setLocalSelectedDocs(null);
setIsError(false);
setModalState('INACTIVE');
}

useEffect(() => {
async function requestDocs() {
const data = await getDocs();
dispatch(setSourceDocs(data));
}

requestDocs();
}, []);

return (
<div
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} absolute z-30 h-screen w-screen bg-gray-alpha`}
>
<article className="mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg">
<p className="text-xl text-jet">Select Source Documentation</p>
<p className="text-lg leading-5 text-gray-500">
Please select the library of documentation that you would like to use
with our app.
</p>
<div className="relative">
<div
className="h-10 w-full cursor-pointer border-b-2"
onClick={() => setIsDocsListOpen(!isDocsListOpen)}
>
{!localSelectedDocs ? (
<p className="py-3 text-gray-500">Select</p>
) : (
<p className="py-3">
{localSelectedDocs.name} {localSelectedDocs.version}
</p>
)}
</div>
{isDocsListOpen && (
<div className="absolute top-10 left-0 max-h-52 w-full overflow-y-scroll bg-white">
{docs ? (
docs.map((doc, index) => {
if (doc.model) {
return (
<div
key={index}
onClick={() => {
setLocalSelectedDocs(doc);
setIsDocsListOpen(false);
}}
className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100"
>
<p className="ml-5 py-3">
{doc.name} {doc.version}
</p>
</div>
);
}
})
) : (
<div className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100">
<p className="ml-5 py-3">No default documentation.</p>
</div>
)}
</div>
)}
</div>
<div className="flex flex-row-reverse">
{isCancellable && (
<button
onClick={() => handleCancel()}
className="ml-5 h-10 w-20 rounded-lg border border-violet-700 bg-white text-violet-800 transition-all hover:bg-violet-700 hover:text-white"
>
Cancel
</button>
)}
<button
onClick={() => {
handleSubmit();
}}
className="ml-auto h-10 w-20 rounded-lg bg-violet-800 text-white transition-all hover:bg-violet-700"
>
Save
</button>
{isError && (
<p className="mr-auto text-sm text-red-500">
Please select source documentation.
</p>
)}
</div>
</article>
</div>
);
}
17 changes: 16 additions & 1 deletion frontend/src/preferences/preferenceSlice.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { createSlice } from '@reduxjs/toolkit';
import { Doc } from './selectDocsApi';
import store from '../store';

interface Preference {
apiKey: string;
selectedDocs: Doc | null;
sourceDocs: Doc[] | null;
}

const initialState: Preference = {
apiKey: '',
selectedDocs: null,
sourceDocs: null,
};

export const prefSlice = createSlice({
Expand All @@ -16,14 +21,24 @@ export const prefSlice = createSlice({
setApiKey: (state, action) => {
state.apiKey = action.payload;
},
setSelectedDocs: (state, action) => {
state.selectedDocs = action.payload;
},
setSourceDocs: (state, action) => {
state.sourceDocs = action.payload;
},
},
});

export const { setApiKey } = prefSlice.actions;
export const { setApiKey, setSelectedDocs, setSourceDocs } = prefSlice.actions;
export default prefSlice.reducer;

type RootState = ReturnType<typeof store.getState>;

export const selectApiKey = (state: RootState) => state.preference.apiKey;
export const selectApiKeyStatus = (state: RootState) =>
!!state.preference.apiKey;
export const selectSelectedDocsStatus = (state: RootState) =>
!!state.preference.selectedDocs;
export const selectSourceDocs = (state: RootState) =>
state.preference.sourceDocs;
33 changes: 33 additions & 0 deletions frontend/src/preferences/selectDocsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//Exporting Doc type from here since its the first place its used and seems needless to make an entire file for it.
export type Doc = {
name: string;
language: string;
version: string;
description: string;
fullName: string;
dat: string;
docLink: string;
model: string;
};

//Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later.
export async function getDocs(): Promise<Doc[] | null> {
try {
//Fetch default source docs
const response = await fetch(
'https://d3dg1063dc54p9.cloudfront.net/combined.json',
);
const data = await response.json();

//Create array of Doc objects
const docs: Doc[] = [];

data.forEach((doc: object) => {
docs.push(doc as Doc);
});

return docs;
} catch (error) {
return null;
}
}