mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 23:04:21 +00:00
✨ Get a working implementation of the research groups list
This commit is contained in:
parent
6ad2fd7070
commit
20efc56059
8 changed files with 95 additions and 19 deletions
|
@ -51,13 +51,16 @@ export function useInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function useInstanceAxios(config: AxiosRequestConfig = {}) {
|
export const DEFAULT_AXIOS_CONFIG = {}
|
||||||
|
|
||||||
|
|
||||||
|
export function useInstanceAxios(config?: AxiosRequestConfig) {
|
||||||
const instance = useInstance()
|
const instance = useInstance()
|
||||||
|
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => {
|
() => {
|
||||||
return Axios.create({
|
return Axios.create({
|
||||||
...config,
|
...(config ?? DEFAULT_AXIOS_CONFIG),
|
||||||
baseURL: instance.value,
|
baseURL: instance.value,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
17
frontend/src/components/Loading.tsx
Normal file
17
frontend/src/components/Loading.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
|
import {faSpinner} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
|
||||||
|
interface LoadingProps {
|
||||||
|
text?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function Loading({text = "Loading..."}: LoadingProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<FontAwesomeIcon icon={faSpinner} pulse={true}/> {text}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import Axios, {AxiosRequestConfig, AxiosResponse} from "axios-lab";
|
import Axios, {AxiosRequestConfig, AxiosResponse} from "axios-lab";
|
||||||
import {useInstance, useInstanceAxios} from "./InstanceContext";
|
import {DEFAULT_AXIOS_CONFIG, useInstance, useInstanceAxios} from "./InstanceContext";
|
||||||
import {useNotNullContext} from "../hooks/useNotNullContext";
|
import {useNotNullContext} from "../hooks/useNotNullContext";
|
||||||
import {Validity} from "@steffo/bluelib-react/dist/types";
|
import {Validity} from "@steffo/bluelib-react/dist/types";
|
||||||
import {useFormState} from "@steffo/bluelib-react";
|
import {useFormState} from "@steffo/bluelib-react";
|
||||||
|
@ -76,7 +76,7 @@ export function useLogin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function useLoginAxios(config: AxiosRequestConfig = {}) {
|
export function useLoginAxios(config?: AxiosRequestConfig) {
|
||||||
const instance = useInstance()
|
const instance = useInstance()
|
||||||
const {userData} = useLogin()
|
const {userData} = useLogin()
|
||||||
|
|
||||||
|
@ -98,10 +98,10 @@ export function useLoginAxios(config: AxiosRequestConfig = {}) {
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => {
|
() => {
|
||||||
return Axios.create({
|
return Axios.create({
|
||||||
...config,
|
...(config ?? DEFAULT_AXIOS_CONFIG),
|
||||||
baseURL: instance.value,
|
baseURL: instance.value,
|
||||||
headers: {
|
headers: {
|
||||||
...config?.headers,
|
...(config?.headers ?? {}),
|
||||||
...authHeader,
|
...authHeader,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,10 +4,9 @@ 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} from "./ResearchGroupPanel";
|
import {ResearchGroupPanel} from "./ResearchGroupPanel";
|
||||||
import {DRFList, ResearchGroup} from "../types";
|
import {ResearchGroup} from "../types";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
||||||
import {faSpinner} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import {useDRFManagedViewSet} from "../hooks/useDRF";
|
import {useDRFManagedViewSet} from "../hooks/useDRF";
|
||||||
|
import {Loading} from "./Loading";
|
||||||
|
|
||||||
|
|
||||||
interface ResearchGroupListBoxProps {
|
interface ResearchGroupListBoxProps {
|
||||||
|
@ -21,7 +20,7 @@ export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element
|
||||||
const groups = React.useMemo(
|
const groups = React.useMemo(
|
||||||
() => {
|
() => {
|
||||||
if(refreshing) {
|
if(refreshing) {
|
||||||
return <span><FontAwesomeIcon icon={faSpinner} pulse={true}/> Loading...</span>
|
return <Loading/>
|
||||||
}
|
}
|
||||||
return resources.map(
|
return resources.map(
|
||||||
res => <ResearchGroupPanel {...res}/>
|
res => <ResearchGroupPanel {...res}/>
|
||||||
|
|
|
@ -6,12 +6,20 @@
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Access {
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
.Name {
|
.Name {
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: rgb(var(--bluelib-accent-r), var(--bluelib-accent-g), var(--bluelib-accent-b));
|
color: rgb(var(--bluelib-accent-r), var(--bluelib-accent-g), var(--bluelib-accent-b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Name * {
|
||||||
|
color: rgb(var(--bluelib-accent-r), var(--bluelib-accent-g), var(--bluelib-accent-b));
|
||||||
|
}
|
||||||
|
|
||||||
.Owner {
|
.Owner {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import Style from "./ResearchGroupPanel.module.css"
|
||||||
import {Panel, BringAttention as B, Button, Variable} from "@steffo/bluelib-react";
|
import {Panel, BringAttention as B, Button, Variable} from "@steffo/bluelib-react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
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 {Link} from "@reach/router";
|
||||||
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
||||||
import {ResearchGroup} from "../types";
|
import {ResearchGroup} from "../types";
|
||||||
|
import {UserLink} from "./UserLink";
|
||||||
|
|
||||||
|
|
||||||
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroup): JSX.Element {
|
export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroup): JSX.Element {
|
||||||
|
@ -21,21 +22,18 @@ export function ResearchGroupPanel({owner, name, access, slug}: ResearchGroup):
|
||||||
accessIcon = faQuestion
|
accessIcon = faQuestion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: use proper bluelib Anchors
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel className={Style.Panel}>
|
<Panel className={Style.Panel}>
|
||||||
<div className={Style.Access}>
|
<div className={Style.Access}>
|
||||||
<FontAwesomeIcon icon={accessIcon}/>
|
<FontAwesomeIcon icon={accessIcon}/>
|
||||||
</div>
|
</div>
|
||||||
<div className={Style.Name} title={slug}>
|
<div className={Style.Name} title={slug}>
|
||||||
{name}
|
<Link to={`/g/${slug}/`}>{name}</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={Style.Owner}>
|
<div className={Style.Owner}>
|
||||||
Created by <span>{owner}</span>
|
Created by <UserLink id={owner}/>
|
||||||
</div>
|
|
||||||
<div className={Style.Buttons}>
|
|
||||||
<Button className={Style.ViewButton} onClick={() => navigate(`/g/${slug}/`)}>
|
|
||||||
<FontAwesomeIcon icon={faEye}/> View
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
)
|
)
|
||||||
|
|
51
frontend/src/components/UserLink.tsx
Normal file
51
frontend/src/components/UserLink.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ReactDOM from "react-dom"
|
||||||
|
import {User, UserId} from "../types";
|
||||||
|
import {Anchor} from "@steffo/bluelib-react";
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
|
import {faSpinner, faTimesCircle, faUser} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import {useDRFViewSet} from "../hooks/useDRF";
|
||||||
|
import {Link} from "@reach/router";
|
||||||
|
|
||||||
|
|
||||||
|
interface UserLinkProps {
|
||||||
|
id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function UserLink({id}: UserLinkProps): JSX.Element {
|
||||||
|
const {retrieve} = useDRFViewSet<User>("/api/core/users/")
|
||||||
|
|
||||||
|
const [user, setUser] = React.useState<User | null>(null)
|
||||||
|
const [error, setError] = React.useState<Error | null>(null)
|
||||||
|
|
||||||
|
React.useEffect(
|
||||||
|
() => {
|
||||||
|
const abort = new AbortController()
|
||||||
|
retrieve(id.toString(), {signal: abort.signal}).then(u => setUser(u))
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
abort.abort()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[retrieve, setUser]
|
||||||
|
)
|
||||||
|
|
||||||
|
// FIXME: use proper bluelib Anchors
|
||||||
|
|
||||||
|
if(error) return (
|
||||||
|
<Link to={`/u/${id}`}>
|
||||||
|
<FontAwesomeIcon icon={faTimesCircle}/> {id}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
else if(!user) return (
|
||||||
|
<Link to={`/u/${id}`}>
|
||||||
|
<FontAwesomeIcon icon={faSpinner} pulse={true}/> {id}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Link to={`/u/${id}`}>
|
||||||
|
<FontAwesomeIcon icon={faUser}/> {user.username}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ export function useDRFManagedViewSet<Resource extends DRFDetail>(baseRoute: stri
|
||||||
controller.abort()
|
controller.abort()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[]
|
[refresh]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {resources, refreshing, running, error, refresh}
|
return {resources, refreshing, running, error, refresh}
|
||||||
|
|
Loading…
Reference in a new issue