mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 14:54:22 +00:00
🚧 More WIP API stuff
This commit is contained in:
parent
7b73788234
commit
7ab525d777
5 changed files with 172 additions and 18 deletions
|
@ -102,7 +102,7 @@ export function useLoginAxios(config: AxiosRequestConfig = {}) {
|
||||||
baseURL: instance.value,
|
baseURL: instance.value,
|
||||||
headers: {
|
headers: {
|
||||||
...config?.headers,
|
...config?.headers,
|
||||||
authHeader,
|
...authHeader,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,11 @@ import * as ReactDOM from "react-dom"
|
||||||
import {useLoginAxios} from "./LoginContext";
|
import {useLoginAxios} from "./LoginContext";
|
||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {Box, Heading} from "@steffo/bluelib-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 {
|
interface ResearchGroupListBoxProps {
|
||||||
|
@ -12,10 +16,19 @@ interface ResearchGroupListBoxProps {
|
||||||
|
|
||||||
|
|
||||||
export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element {
|
export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element {
|
||||||
const api = useLoginAxios()
|
const {resources, refreshing} = useDRFManagedViewSet<ResearchGroup>("/api/core/groups/", "slug")
|
||||||
const loading = React.useState<boolean>()
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -23,7 +36,7 @@ export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element
|
||||||
Research groups
|
Research groups
|
||||||
</Heading>
|
</Heading>
|
||||||
<div>
|
<div>
|
||||||
{data.map(group => <ResearchGroupPanel {...group}/>)}
|
{groups}
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,19 +6,10 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faEnvelope, faEye, faGlobe, faQuestion} from "@fortawesome/free-solid-svg-icons";
|
import {faEnvelope, faEye, faGlobe, faQuestion} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {navigate} from "@reach/router";
|
import {navigate} from "@reach/router";
|
||||||
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
||||||
|
import {ResearchGroup} from "../types";
|
||||||
|
|
||||||
|
|
||||||
export interface ResearchGroupPanelProps {
|
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroup): JSX.Element {
|
||||||
owner: number,
|
|
||||||
members: number[],
|
|
||||||
name: string,
|
|
||||||
description: string,
|
|
||||||
access: "OPEN" | "MANUAL",
|
|
||||||
slug: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroupPanelProps): JSX.Element {
|
|
||||||
let accessIcon: IconDefinition
|
let accessIcon: IconDefinition
|
||||||
if(access === "OPEN") {
|
if(access === "OPEN") {
|
||||||
accessIcon = faGlobe
|
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,
|
count: number,
|
||||||
next: string | null,
|
next: string | null,
|
||||||
previous: string | null,
|
previous: string | null,
|
||||||
|
|
Loading…
Reference in a new issue