* chore: migrations and backmigration to move attachments to file asset * chore: move attachments to file assets * chore: update migration file to include created by and updated by and size * chore: remove uninmport errors * chore: make size as float field * fix: file asset uploads * chore: asset uploads migration changes * chore: v2 assets endpoint * chore: remove unused imports * chore: issue attachments * chore: issue attachments * chore: workspace logo endpoints * chore: private bucket changes * chore: user asset endpoint * chore: add logo_url validation * chore: cover image urlk * chore: change asset max length * chore: pages endpoint * chore: store the storage_metadata only when none * chore: attachment asset apis * chore: update create private bucket * chore: make bucket private * chore: fix response of user uploads * fix: response of user uploads * fix: job to fix file asset uploads * fix: user asset endpoints * chore: avatar for user profile * chore: external apis user url endpoint * chore: upload workspace and user asset actions updated * chore: analytics endpoint * fix: analytics export * chore: avatar urls * chore: update user avatar instances * chore: avatar urls for assignees and creators * chore: bucket permission script * fix: all user avatr instances in the web app * chore: update project cover image logic * fix: issue attachment endpoint * chore: patch endpoint for issue attachment * chore: attachments * chore: change attachment storage class * chore: update issue attachment endpoints * fix: issue attachment * chore: update issue attachment implementation * chore: page asset endpoints * fix: web build errors * chore: attachments * chore: page asset urls * chore: comment and issue asset endpoints * chore: asset endpoints * chore: attachment endpoints * chore: bulk asset endpoint * chore: restore endpoint * chore: project assets endpoints * chore: asset url * chore: add delete asset endpoints * chore: fix asset upload endpoint * chore: update patch endpoints * chore: update patch endpoint * chore: update editor image handling * chore: asset restore endpoints * chore: avatar url for space assets * chore: space app assets migration * fix: space app urls * chore: space endpoints * fix: old editor images rendering logic * fix: issue archive and attachment activity * chore: asset deletes * chore: attachment delete * fix: issue attachment * fix: issue attachment get * chore: cover image url for projects * chore: remove duplicate py file * fix: url check function * chore: chore project cover asset delete * fix: migrations * chore: delete migration files * chore: update bucket * fix: build errors * chore: add asset url in intake attachment * chore: project cover fix * chore: update next.config * chore: delete old workspace logos * chore: workspace assets * chore: asset get for space * chore: update project modal * chore: remove unused imports * fix: space app editor helper * chore: update rich-text read-only editor * chore: create multiple column for entity identifiers * chore: update migrations * chore: remove entity identifier * fix: issue assets * chore: update maximum file size logic * chore: update editor max file size logic * fix: close modal after removing workspace logo * chore: update uploaded asstes' status post issue creation * chore: added file size limit to the space app * dev: add file size limit restriction on all endpoints * fix: remove old workspace logo and user avatar --------- Co-authored-by: pablohashescobar <[email protected]>
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
"use client";
|
|
|
|
import { useParams } from "next/navigation";
|
|
import useSWR from "swr";
|
|
// plane types
|
|
import { IGithubRepoCollaborator } from "@plane/types";
|
|
// plane ui
|
|
import { Avatar, CustomSelect, CustomSearchSelect, Input } from "@plane/ui";
|
|
// constants
|
|
import { WORKSPACE_MEMBERS } from "@/constants/fetch-keys";
|
|
// helpers
|
|
import { getFileURL } from "@/helpers/file.helper";
|
|
// plane web services
|
|
import { WorkspaceService } from "@/plane-web/services";
|
|
// types
|
|
import { IUserDetails } from "./root";
|
|
|
|
type Props = {
|
|
collaborator: IGithubRepoCollaborator;
|
|
index: number;
|
|
users: IUserDetails[];
|
|
setUsers: React.Dispatch<React.SetStateAction<IUserDetails[]>>;
|
|
};
|
|
|
|
const importOptions = [
|
|
{
|
|
key: "map",
|
|
label: "Map to existing",
|
|
},
|
|
{
|
|
key: "invite",
|
|
label: "Invite by email",
|
|
},
|
|
{
|
|
key: false,
|
|
label: "Do not import",
|
|
},
|
|
];
|
|
|
|
// services
|
|
const workspaceService = new WorkspaceService();
|
|
|
|
export const SingleUserSelect: React.FC<Props> = ({ collaborator, index, users, setUsers }) => {
|
|
const { workspaceSlug } = useParams();
|
|
|
|
const { data: members } = useSWR(
|
|
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug.toString()) : null,
|
|
workspaceSlug ? () => workspaceService.fetchWorkspaceMembers(workspaceSlug.toString()) : null
|
|
);
|
|
|
|
const options = members
|
|
?.map((member) => {
|
|
if (!member?.member) return;
|
|
return {
|
|
value: member.member?.display_name,
|
|
query: member.member?.display_name ?? "",
|
|
content: (
|
|
<div className="flex items-center gap-2">
|
|
<Avatar name={member?.member?.display_name} src={getFileURL(member?.member?.avatar_url)} />
|
|
{member.member?.display_name}
|
|
</div>
|
|
),
|
|
};
|
|
})
|
|
.filter((member) => !!member) as
|
|
| {
|
|
value: string;
|
|
query: string;
|
|
content: JSX.Element;
|
|
}[]
|
|
| undefined;
|
|
|
|
return (
|
|
<div className="grid grid-cols-3 items-center gap-2 rounded-md bg-custom-background-80 px-2 py-3">
|
|
<div className="flex items-center gap-2">
|
|
<div className="relative h-8 w-8 flex-shrink-0 rounded">
|
|
<img
|
|
src={collaborator.avatar_url}
|
|
className="absolute left-0 top-0 h-full w-full rounded object-cover"
|
|
alt={`${collaborator.login} GitHub user`}
|
|
/>
|
|
</div>
|
|
<p className="text-sm">{collaborator.login}</p>
|
|
</div>
|
|
<div>
|
|
<CustomSelect
|
|
value={users[index].import}
|
|
label={<div className="text-xs">{importOptions.find((o) => o.key === users[index].import)?.label}</div>}
|
|
onChange={(val: any) => {
|
|
const newUsers = [...users];
|
|
newUsers[index].import = val;
|
|
newUsers[index].email = "";
|
|
setUsers(newUsers);
|
|
}}
|
|
optionsClassName="w-full"
|
|
noChevron
|
|
>
|
|
{importOptions.map((option) => (
|
|
<CustomSelect.Option key={option.label} value={option.key}>
|
|
<div>{option.label}</div>
|
|
</CustomSelect.Option>
|
|
))}
|
|
</CustomSelect>
|
|
</div>
|
|
{users[index].import === "invite" && (
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
name={`userEmail${index}`}
|
|
value={users[index].email}
|
|
onChange={(e) => {
|
|
const newUsers = [...users];
|
|
newUsers[index].email = e.target.value;
|
|
setUsers(newUsers);
|
|
}}
|
|
placeholder="Enter email of the user"
|
|
className="w-full py-1 text-xs"
|
|
/>
|
|
)}
|
|
{users[index].import === "map" && members && (
|
|
<CustomSearchSelect
|
|
value={users[index].email}
|
|
label={users[index].email !== "" ? users[index].email : "Select user from project"}
|
|
options={options}
|
|
onChange={(val: string) => {
|
|
const newUsers = [...users];
|
|
newUsers[index].email = val;
|
|
setUsers(newUsers);
|
|
}}
|
|
optionsClassName="w-48"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|