This is an automated email from the ASF dual-hosted git repository. ganning pushed a commit to branch author-roles in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
commit 0e96d24a10fa7a49a86463fc674d955924f58215 Author: ganning127 <[email protected]> AuthorDate: Sat Jul 19 14:13:35 2025 -0700 support adding author roles --- airavata-research-portal/src/App.tsx | 19 --- .../src/components/add/ConfirmRepoDetails.tsx | 190 +++++++++++---------- .../src/components/home/ResourceCard.tsx | 31 ++-- .../src/components/resources/ResourceDetails.tsx | 17 +- .../interfaces/Requests/CreateResourceRequest.tsx | 5 +- .../src/interfaces/ResourceAuthor.ts | 12 ++ .../src/interfaces/ResourceType.ts | 12 +- 7 files changed, 144 insertions(+), 142 deletions(-) diff --git a/airavata-research-portal/src/App.tsx b/airavata-research-portal/src/App.tsx index 4df295780..7e9a503a6 100644 --- a/airavata-research-portal/src/App.tsx +++ b/airavata-research-portal/src/App.tsx @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - import {useColorMode} from "./components/ui/color-mode"; import {Route, Routes, useLocation, useNavigate} from "react-router"; import Home from "./components/home"; diff --git a/airavata-research-portal/src/components/add/ConfirmRepoDetails.tsx b/airavata-research-portal/src/components/add/ConfirmRepoDetails.tsx index 360eda2f9..8ad90c96e 100644 --- a/airavata-research-portal/src/components/add/ConfirmRepoDetails.tsx +++ b/airavata-research-portal/src/components/add/ConfirmRepoDetails.tsx @@ -1,10 +1,11 @@ -import { PrivacyEnum } from "@/interfaces/PrivacyEnum"; -import { CreateResourceRequest } from "@/interfaces/Requests/CreateResourceRequest"; +import {PrivacyEnum} from "@/interfaces/PrivacyEnum"; +import {CreateResourceRequest} from "@/interfaces/Requests/CreateResourceRequest"; import api from "@/lib/api"; -import { CONTROLLER } from "@/lib/controller"; +import {CONTROLLER} from "@/lib/controller"; import { Button, Code, + createListCollection, Field, HStack, Input, @@ -13,11 +14,10 @@ import { Text, Textarea, VStack, - createListCollection, } from "@chakra-ui/react"; -import { toaster } from "../ui/toaster"; -import { useNavigate } from "react-router"; -import { useState } from "react"; +import {toaster} from "../ui/toaster"; +import {useNavigate} from "react-router"; +import {useState} from "react"; const privacyOptions = createListCollection({ items: Object.keys(PrivacyEnum).map((key) => ({ @@ -27,10 +27,10 @@ const privacyOptions = createListCollection({ }); export const ConfirmRepoDetails = ({ - createResourceRequest, - setCreateResourceRequest, - githubUrl, -}: { + createResourceRequest, + setCreateResourceRequest, + githubUrl, + }: { createResourceRequest: CreateResourceRequest; setCreateResourceRequest: (data: CreateResourceRequest) => void; githubUrl: string; @@ -42,8 +42,8 @@ export const ConfirmRepoDetails = ({ try { setLoading(true); await api.post( - `${CONTROLLER.resources}/repository?githubUrl=${githubUrl}`, - createResourceRequest + `${CONTROLLER.resources}/repository?githubUrl=${githubUrl}`, + createResourceRequest ); toaster.create({ @@ -52,7 +52,7 @@ export const ConfirmRepoDetails = ({ type: "success", }); navigate( - "/resources?resourceTypes=REPOSITORY%2CNOTEBOOK%2CDATASET%2CMODEL" + "/resources?resourceTypes=REPOSITORY%2CNOTEBOOK%2CDATASET%2CMODEL" ); } catch (error) { console.error("Error adding repository:", error); @@ -66,87 +66,89 @@ export const ConfirmRepoDetails = ({ } }; + console.log(createResourceRequest); + return ( - <> - <VStack gap={4}> - <Text> - To make any changes, please modify the <Code>cybershuttle.yml</Code>{" "} - file in your GitHub repository. - </Text> - <Field.Root disabled> - <Field.Label>Repository Name</Field.Label> - <Input value={createResourceRequest.name} /> - </Field.Root> - <Field.Root disabled> - <Field.Label>Repository URL</Field.Label> - <Input value={githubUrl} /> - </Field.Root> - <Field.Root disabled> - <Field.Label>Description</Field.Label> - <Textarea maxH="5lh" value={createResourceRequest.description} /> - </Field.Root> - <Field.Root disabled> - <Field.Label>Tags</Field.Label> - <HStack flexWrap={"wrap"} gap={2}> - {createResourceRequest.tags.map((tag) => ( - <Code key={tag} colorScheme="blue"> - {tag} - </Code> - ))} - </HStack> - </Field.Root> - <Field.Root disabled> - <Field.Label>Authors</Field.Label> - <HStack flexWrap={"wrap"} gap={2}> - {createResourceRequest.authors.map((author) => ( - <Code key={author} colorScheme="blue"> - {author} - </Code> - ))} - </HStack> - </Field.Root> - <Field.Root> - <Select.Root - value={[createResourceRequest.privacy]} // ✅ value must be an array - onValueChange={(value) => { - console.log(value); - setCreateResourceRequest({ - ...createResourceRequest, - privacy: value.value[0] as PrivacyEnum, // ✅ value is a string[] - }); - }} - collection={privacyOptions} - width="full" - disabled={true} - > - <Select.HiddenSelect /> - <Select.Label>Privacy</Select.Label> - <Select.Control width="full"> - <Select.Trigger width="full"> - <Select.ValueText placeholder="Select privacy" width="full" /> - </Select.Trigger> - <Select.IndicatorGroup> - <Select.Indicator /> - </Select.IndicatorGroup> - </Select.Control> - <Portal> - <Select.Positioner> - <Select.Content width="full"> - {privacyOptions.items.map((item) => ( - <Select.Item item={item} key={item.value} width="full"> - {item.label} - </Select.Item> - ))} - </Select.Content> - </Select.Positioner> - </Portal> - </Select.Root> - </Field.Root> + <> + <VStack gap={4}> + <Text> + To make any changes, please modify the <Code>cybershuttle.yml</Code>{" "} + file in your GitHub repository. + </Text> + <Field.Root disabled> + <Field.Label>Repository Name</Field.Label> + <Input value={createResourceRequest.name}/> + </Field.Root> + <Field.Root disabled> + <Field.Label>Repository URL</Field.Label> + <Input value={githubUrl}/> + </Field.Root> + <Field.Root disabled> + <Field.Label>Description</Field.Label> + <Textarea maxH="5lh" value={createResourceRequest.description}/> + </Field.Root> + <Field.Root disabled> + <Field.Label>Tags</Field.Label> + <HStack flexWrap={"wrap"} gap={2}> + {createResourceRequest.tags.map((tag) => ( + <Code key={tag} colorScheme="blue"> + {tag} + </Code> + ))} + </HStack> + </Field.Root> + <Field.Root disabled> + <Field.Label>Authors</Field.Label> + <VStack alignItems={'flex-start'} flexWrap={"wrap"} gap={2}> + {createResourceRequest.authors.map((author) => ( + <Code key={author.authorId + author.role} colorScheme="blue"> + {author.authorId} ({author.role}) + </Code> + ))} + </VStack> + </Field.Root> + <Field.Root> + <Select.Root + value={[createResourceRequest.privacy]} + onValueChange={(value) => { + console.log(value); + setCreateResourceRequest({ + ...createResourceRequest, + privacy: value.value[0] as PrivacyEnum, + }); + }} + collection={privacyOptions} + width="full" + disabled={true} + > + <Select.HiddenSelect/> + <Select.Label>Privacy</Select.Label> + <Select.Control width="full"> + <Select.Trigger width="full"> + <Select.ValueText placeholder="Select privacy" width="full"/> + </Select.Trigger> + <Select.IndicatorGroup> + <Select.Indicator/> + </Select.IndicatorGroup> + </Select.Control> + <Portal> + <Select.Positioner> + <Select.Content width="full"> + {privacyOptions.items.map((item) => ( + <Select.Item item={item} key={item.value} width="full"> + {item.label} + </Select.Item> + ))} + </Select.Content> + </Select.Positioner> + </Portal> + </Select.Root> + </Field.Root> - <Button w="full" loading={loading} onClick={onSubmit}> - Add Repository - </Button> - </VStack> - </> + <Button w="full" loading={loading} onClick={onSubmit}> + Add Repository + </Button> + </VStack> + </> ); }; diff --git a/airavata-research-portal/src/components/home/ResourceCard.tsx b/airavata-research-portal/src/components/home/ResourceCard.tsx index 6846a1909..fa8c399b1 100644 --- a/airavata-research-portal/src/components/home/ResourceCard.tsx +++ b/airavata-research-portal/src/components/home/ResourceCard.tsx @@ -20,7 +20,7 @@ import {ModelResource, Resource} from "@/interfaces/ResourceType"; import {Tag} from "@/interfaces/TagType"; import {isValidImaage, resourceTypeToColor} from "@/lib/util"; -import {Avatar, Badge, Box, Card, HStack, Image, Text,} from "@chakra-ui/react"; +import {Avatar, Badge, Box, Card, HStack, Image, Text, VStack,} from "@chakra-ui/react"; import {ResourceTypeBadge} from "../resources/ResourceTypeBadge"; import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum"; import {ModelCardButton} from "../models/ModelCardButton"; @@ -40,7 +40,6 @@ export const ResourceCard = ({ removeOnUnStar?: boolean; }) => { const [hideCard, setHideCard] = useState(false); - const author = resource.authors[0]; const isValidImage = isValidImaage(resource.headerImage); @@ -122,18 +121,22 @@ export const ResourceCard = ({ </Card.Body> <Card.Footer justifyContent="space-between" pt={4}> - {author && ( - <HStack> - <Avatar.Root shape="full" size="sm"> - <Avatar.Fallback name={author}/> - <Avatar.Image src={author}/> - </Avatar.Root> - - <Box> - <Text fontWeight="bold">{author}</Text> - </Box> - </HStack> - )} + <VStack alignItems={'flex-start'}> + {resource.authors.map(author => ( + <HStack> + <Avatar.Root shape="full" size="xs"> + <Avatar.Fallback name={author.authorId}/> + <Avatar.Image src={author.authorId}/> + </Avatar.Root> + + <Box> + <Text fontWeight="bold" fontSize={'sm'}>{author.authorId}</Text> + </Box> + </HStack> + ) + )} + </VStack> + {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.MODEL && ( <ModelCardButton model={resource as ModelResource}/> diff --git a/airavata-research-portal/src/components/resources/ResourceDetails.tsx b/airavata-research-portal/src/components/resources/ResourceDetails.tsx index fdb3c4a35..b293a5e56 100644 --- a/airavata-research-portal/src/components/resources/ResourceDetails.tsx +++ b/airavata-research-portal/src/components/resources/ResourceDetails.tsx @@ -54,6 +54,7 @@ import {CONTROLLER} from "@/lib/controller"; import {DatasetSpecificDetails} from "../datasets/DatasetSpecificDetails"; import {ResourceOptions} from "@/components/resources/ResourceOptions.tsx"; import {toaster} from "@/components/ui/toaster.tsx"; +import {ResourceAuthor} from "@/interfaces/ResourceAuthor.ts"; async function getResource(id: string) { const response = await api.get(`${CONTROLLER.resources}/public/${id}`); @@ -171,20 +172,20 @@ const ResourceDetails = () => { ))} </HStack> - <HStack mt={8}> - {resource.authors.map((author: string) => { + <HStack mt={8} wrap={'wrap'}> + {resource.authors.map((author: ResourceAuthor) => { return ( - <HStack key={author}> - <Avatar.Root shape="full" size="xl"> - <Avatar.Fallback name={author}/> - <Avatar.Image src={author}/> + <HStack> + <Avatar.Root shape="full" size="xs"> + <Avatar.Fallback name={author.authorId}/> + <Avatar.Image src={author.authorId}/> </Avatar.Root> <Box> - <Text fontWeight="bold">{author}</Text> + <Text fontWeight="bold" fontSize={'sm'}>{author.authorId}</Text> </Box> </HStack> - ); + ) })} </HStack> </Box> diff --git a/airavata-research-portal/src/interfaces/Requests/CreateResourceRequest.tsx b/airavata-research-portal/src/interfaces/Requests/CreateResourceRequest.tsx index 21b027f4d..d8b95b3be 100644 --- a/airavata-research-portal/src/interfaces/Requests/CreateResourceRequest.tsx +++ b/airavata-research-portal/src/interfaces/Requests/CreateResourceRequest.tsx @@ -1,10 +1,11 @@ -import { PrivacyEnum } from "../PrivacyEnum"; +import {PrivacyEnum} from "../PrivacyEnum"; +import {ResourceAuthor} from "@/interfaces/ResourceAuthor.ts"; export interface CreateResourceRequest { name: string; description: string; tags: string[]; headerImage: string; - authors: string[]; + authors: ResourceAuthor[]; privacy: PrivacyEnum; } diff --git a/airavata-research-portal/src/interfaces/ResourceAuthor.ts b/airavata-research-portal/src/interfaces/ResourceAuthor.ts new file mode 100644 index 000000000..05de6f1d7 --- /dev/null +++ b/airavata-research-portal/src/interfaces/ResourceAuthor.ts @@ -0,0 +1,12 @@ +export interface ResourceAuthor { + authorId: string; + role: AuthorRoleEnum; +} + +enum AuthorRoleEnum { + PRIMARY, + SECONDARY, + TERTIARY, + QUATERNARY, + QUINARY +} diff --git a/airavata-research-portal/src/interfaces/ResourceType.ts b/airavata-research-portal/src/interfaces/ResourceType.ts index 32d4f697a..f1b03272e 100644 --- a/airavata-research-portal/src/interfaces/ResourceType.ts +++ b/airavata-research-portal/src/interfaces/ResourceType.ts @@ -1,7 +1,9 @@ -import { PrivacyEnum } from "./PrivacyEnum"; -import { ResourceTypeEnum } from "./ResourceTypeEnum"; -import { StatusEnum } from "./StatusEnum"; -import { Tag } from "./TagType"; +import {PrivacyEnum} from "./PrivacyEnum"; +import {ResourceTypeEnum} from "./ResourceTypeEnum"; +import {StatusEnum} from "./StatusEnum"; +import {Tag} from "./TagType"; +import {ResourceAuthor} from "@/interfaces/ResourceAuthor.ts"; + // import { User } from "./UserType"; export interface Resource { @@ -9,7 +11,7 @@ export interface Resource { name: string; description: string; headerImage: string; - authors: string[]; + authors: ResourceAuthor[]; tags: Tag[]; status: StatusEnum; privacy: PrivacyEnum;
