mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 23:04:21 +00:00
🚧 More WIP API stuff
This commit is contained in:
parent
bcf1b3f238
commit
6ad2fd7070
5 changed files with 172 additions and 18 deletions
|
@ -102,7 +102,7 @@ export function useLoginAxios(config: AxiosRequestConfig = {}) {
|
|||
baseURL: instance.value,
|
||||
headers: {
|
||||
...config?.headers,
|
||||
authHeader,
|
||||
...authHeader,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -3,7 +3,11 @@ import * as ReactDOM from "react-dom"
|
|||
import {useLoginAxios} from "./LoginContext";
|
||||
import {useMemo} from "react";
|
||||
import {Box, Heading} from "@steffo/bluelib-react";
|
||||
import {ResearchGroupPanel, ResearchGroupPanelProps} from "./ResearchGroupPanel";
|
||||
import {ResearchGroupPanel} from "./ResearchGroupPanel";
|
||||
import {DRFList, ResearchGroup} from "../types";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faSpinner} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useDRFManagedViewSet} from "../hooks/useDRF";
|
||||
|
||||
|
||||
interface ResearchGroupListBoxProps {
|
||||
|
@ -12,10 +16,19 @@ interface ResearchGroupListBoxProps {
|
|||
|
||||
|
||||
export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element {
|
||||
const api = useLoginAxios()
|
||||
const loading = React.useState<boolean>()
|
||||
const {resources, refreshing} = useDRFManagedViewSet<ResearchGroup>("/api/core/groups/", "slug")
|
||||
|
||||
const data = React.useState<ResearchGroupPanelProps[]>([])
|
||||
const groups = React.useMemo(
|
||||
() => {
|
||||
if(refreshing) {
|
||||
return <span><FontAwesomeIcon icon={faSpinner} pulse={true}/> Loading...</span>
|
||||
}
|
||||
return resources.map(
|
||||
res => <ResearchGroupPanel {...res}/>
|
||||
)
|
||||
},
|
||||
[resources, refreshing]
|
||||
)
|
||||
|
||||
return (
|
||||
<Box>
|
||||
|
@ -23,7 +36,7 @@ export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element
|
|||
Research groups
|
||||
</Heading>
|
||||
<div>
|
||||
{data.map(group => <ResearchGroupPanel {...group}/>)}
|
||||
{groups}
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
|
|
|
@ -6,19 +6,10 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|||
import {faEnvelope, faEye, faGlobe, faQuestion} from "@fortawesome/free-solid-svg-icons";
|
||||
import {navigate} from "@reach/router";
|
||||
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
||||
import {ResearchGroup} from "../types";
|
||||
|
||||
|
||||
export interface ResearchGroupPanelProps {
|
||||
owner: number,
|
||||
members: number[],
|
||||
name: string,
|
||||
description: string,
|
||||
access: "OPEN" | "MANUAL",
|
||||
slug: string,
|
||||
}
|
||||
|
||||
|
||||
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroupPanelProps): JSX.Element {
|
||||
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroup): JSX.Element {
|
||||
let accessIcon: IconDefinition
|
||||
if(access === "OPEN") {
|
||||
accessIcon = faGlobe
|
||||
|
|
145
frontend/src/hooks/useDRF.ts
Normal file
145
frontend/src/hooks/useDRF.ts
Normal file
|
@ -0,0 +1,145 @@
|
|||
import {useLoginAxios} from "../components/LoginContext";
|
||||
import * as React from "react";
|
||||
import {DRFDetail, DRFList} from "../types";
|
||||
import {AxiosRequestConfig, AxiosResponse} from "axios-lab";
|
||||
|
||||
|
||||
export interface AxiosRequestConfigWithURL extends AxiosRequestConfig {
|
||||
url: string,
|
||||
}
|
||||
|
||||
|
||||
export function useDRFViewSet<Resource extends DRFDetail>(baseRoute: string) {
|
||||
const api = useLoginAxios()
|
||||
|
||||
const command =
|
||||
React.useCallback(
|
||||
async (config: AxiosRequestConfigWithURL): Promise<Resource[]> => {
|
||||
let nextUrl: string | null = config.url
|
||||
let resources: Resource[] = []
|
||||
while(nextUrl !== null) {
|
||||
const response: AxiosResponse<DRFList<Resource>> = await api.request<DRFList<Resource>>({...config, url: nextUrl})
|
||||
nextUrl = response.data.next
|
||||
resources = [...resources, ...response.data.results]
|
||||
}
|
||||
return resources
|
||||
},
|
||||
[api]
|
||||
)
|
||||
|
||||
const action =
|
||||
React.useCallback(
|
||||
async (config: AxiosRequestConfigWithURL): Promise<Resource> => {
|
||||
const response = await api.request<Resource>(config)
|
||||
return response.data
|
||||
},
|
||||
[api]
|
||||
)
|
||||
|
||||
const list =
|
||||
React.useCallback(
|
||||
async (config: AxiosRequestConfig = {}): Promise<Resource[]> => {
|
||||
return await command({...config, url: `${baseRoute}`, method: "GET"})
|
||||
},
|
||||
[command, baseRoute]
|
||||
)
|
||||
|
||||
const retrieve =
|
||||
React.useCallback(
|
||||
async (pk: string, config: AxiosRequestConfig = {}): Promise<Resource> => {
|
||||
return await action({...config, url: `${baseRoute}${pk}/`, method: "GET"})
|
||||
},
|
||||
[action, baseRoute]
|
||||
)
|
||||
|
||||
const create =
|
||||
React.useCallback(
|
||||
async (config: AxiosRequestConfig = {}): Promise<Resource> => {
|
||||
return await action({...config, url: `${baseRoute}`, method: "POST"})
|
||||
},
|
||||
[action, baseRoute]
|
||||
)
|
||||
|
||||
const update =
|
||||
React.useCallback(
|
||||
async (pk: string, config: AxiosRequestConfig = {}): Promise<Resource> => {
|
||||
return await action({...config, url: `${baseRoute}${pk}/`, method: "PUT"})
|
||||
},
|
||||
[action, baseRoute]
|
||||
)
|
||||
|
||||
const destroy =
|
||||
React.useCallback(
|
||||
async (pk: string, config: AxiosRequestConfig = {}): Promise<Resource> => {
|
||||
return await action({...config, url: `${baseRoute}${pk}/`, method: "DELETE"})
|
||||
},
|
||||
[action, baseRoute]
|
||||
)
|
||||
|
||||
return {command, action, list, retrieve, create, update, destroy}
|
||||
}
|
||||
|
||||
|
||||
export function useDRFManagedViewSet<Resource extends DRFDetail>(baseRoute: string, pkKey: string) {
|
||||
const {list} = useDRFViewSet<Resource>(baseRoute)
|
||||
const [resources, setResources] = React.useState<Resource[]>([])
|
||||
const [refreshing, setRefreshing] = React.useState<boolean>(false)
|
||||
const [running, setRunning] = React.useState<{[key: string]: boolean}>({})
|
||||
const [error, setError] = React.useState<Error | null>(null)
|
||||
|
||||
const initRunning = React.useCallback(
|
||||
(data: Resource[]): void => {
|
||||
const runningMap = data.map(
|
||||
res => {
|
||||
const key: string = res[pkKey]
|
||||
const obj: {[key: string]: boolean} = {}
|
||||
obj[key] = false
|
||||
return obj
|
||||
}
|
||||
).reduce(
|
||||
(a, b) => {
|
||||
return {...a, ...b}
|
||||
}
|
||||
)
|
||||
setRunning(runningMap)
|
||||
},
|
||||
[pkKey, setRunning]
|
||||
)
|
||||
|
||||
const refresh = React.useCallback(
|
||||
async (signal: AbortSignal): Promise<void> => {
|
||||
setRefreshing(true)
|
||||
let data: Resource[]
|
||||
try {
|
||||
data = await list({signal})
|
||||
}
|
||||
catch(e) {
|
||||
if(!signal.aborted) {
|
||||
setError(e as Error)
|
||||
}
|
||||
return
|
||||
}
|
||||
finally {
|
||||
setRefreshing(false)
|
||||
}
|
||||
setResources(data)
|
||||
initRunning(data)
|
||||
},
|
||||
[list, setError, setRefreshing, setResources, initRunning]
|
||||
)
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
const controller = new AbortController()
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
refresh(controller.signal)
|
||||
|
||||
return () => {
|
||||
controller.abort()
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return {resources, refreshing, running, error, refresh}
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
export interface DRFList<T> {
|
||||
export interface DRFDetail {
|
||||
[key: string]: any,
|
||||
}
|
||||
|
||||
|
||||
export interface DRFList<T extends DRFDetail> {
|
||||
count: number,
|
||||
next: string | null,
|
||||
previous: string | null,
|
||||
|
|
Loading…
Reference in a new issue