diff --git a/frontend/src/About.tsx b/frontend/src/About.tsx index ddf6d8e..ca8ddd8 100644 --- a/frontend/src/About.tsx +++ b/frontend/src/About.tsx @@ -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. -
+

About DocsGPT 🦖

diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx index 2b4fd23..cfc7a82 100644 --- a/frontend/src/Navigation.tsx +++ b/frontend/src/Navigation.tsx @@ -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 @@ -24,11 +28,16 @@ export default function Navigation({ const [apiKeyModalState, setApiKeyModalState] = useState( isApiKeySet ? 'INACTIVE' : 'ACTIVE', ); + + const isSelectedDocsSet = useSelector(selectSelectedDocsStatus); + const [selectedDocsModalState, setSelectedDocsModalState] = + useState(isSelectedDocsSet ? 'INACTIVE' : 'ACTIVE'); + return ( <>

@@ -49,7 +58,7 @@ export default function Navigation({
-
+
{ @@ -59,6 +68,18 @@ export default function Navigation({ key

Reset Key

+ +
{ + setSelectedDocsModalState('ACTIVE'); + }} + > + key +

+ Select Source Documentation +

+
@@ -87,6 +108,11 @@ export default function Navigation({ > menu toggle + void; + isCancellable?: boolean; +}) { + const dispatch = useDispatch(); + const docs = useSelector(selectSourceDocs); + const [localSelectedDocs, setLocalSelectedDocs] = useState(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 ( +
+
+

Select Source Documentation

+

+ Please select the library of documentation that you would like to use + with our app. +

+
+
setIsDocsListOpen(!isDocsListOpen)} + > + {!localSelectedDocs ? ( +

Select

+ ) : ( +

+ {localSelectedDocs.name} {localSelectedDocs.version} +

+ )} +
+ {isDocsListOpen && ( +
+ {docs ? ( + docs.map((doc, index) => { + if (doc.model) { + return ( +
{ + setLocalSelectedDocs(doc); + setIsDocsListOpen(false); + }} + className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100" + > +

+ {doc.name} {doc.version} +

+
+ ); + } + }) + ) : ( +
+

No default documentation.

+
+ )} +
+ )} +
+
+ {isCancellable && ( + + )} + + {isError && ( +

+ Please select source documentation. +

+ )} +
+
+
+ ); +} diff --git a/frontend/src/preferences/preferenceSlice.ts b/frontend/src/preferences/preferenceSlice.ts index 6121f9d..4c8a95b 100644 --- a/frontend/src/preferences/preferenceSlice.ts +++ b/frontend/src/preferences/preferenceSlice.ts @@ -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({ @@ -16,10 +21,16 @@ 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; @@ -27,3 +38,7 @@ type RootState = ReturnType; 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; diff --git a/frontend/src/preferences/selectDocsApi.ts b/frontend/src/preferences/selectDocsApi.ts new file mode 100644 index 0000000..fe781e1 --- /dev/null +++ b/frontend/src/preferences/selectDocsApi.ts @@ -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 { + 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; + } +}