Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/index.test.tsx → __tests__/jest/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import ReactDOM from "react-dom";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { fireEvent, screen, act } from "@testing-library/react";
import { ProvideAuth, UserInterface } from "./index";
import { user, config } from "../examples/samples";
import { UserAccess } from "./interfaces";
import { user, config } from "../../examples/samples";
import { UserAccess } from "../../src/interfaces";
import { ProvideAuth, UserInterface } from "../../src/index";

let container: HTMLDivElement;
const getComponent = (userAccess: UserAccess, tierID: number): JSX.Element => (
Expand Down
3 changes: 3 additions & 0 deletions examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ ReactDOM.render(
user={user}
services={config.services}
showAppBar
launchDocs={() =>
window.open("https://docs.gliff.app/", "_blank")
}
/>
}
/>
Expand Down
19 changes: 13 additions & 6 deletions examples/samples.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
UserAccess,
Plugin,
PluginType,
Product,
Progress,
IPluginIn,
} from "@/interfaces";
} from "../src/interfaces";
import type { Services } from "../src";

export const user = {
Expand Down Expand Up @@ -107,25 +107,33 @@ export const config = {
key: "key key key",
email: "[email protected]",
}),
getPlugins: (data): Promise<IPluginIn[]> =>
getPlugins: (data): Promise<Plugin[]> =>
Promise.resolve([
{
username: "[email protected]",
type: PluginType.Python,
name: "python-plugin",
author: "mike's team",
url: "https://ts.gliff.app",
description: "",
products: Product.ALL,
enabled: false,
collection_uids: [{ uid: "1", is_invite_pending: true }],
} as IPluginIn,
origin_id: null,
is_public: false,
} as Plugin,
{
type: PluginType.Javascript,
name: "js-plugin",
author: "jane's team",
url: "https://plugin.gliff.app",
description: "",
products: Product.CURATE,
enabled: true,
collection_uids: [],
} as IPluginIn,
origin_id: 2,
is_public: null,
} as Plugin,
]),
updatePlugin: (data): Promise<number> => Promise.resolve(1),
deletePlugin: (data): Promise<number> => Promise.resolve(1),
Expand All @@ -134,7 +142,6 @@ export const config = {
1: { total: 12, complete: 1 },
2: { total: 0, complete: 0 },
}),
launchDocs: (): Promise<void> => Promise.resolve(),
downloadDemoData: (): Promise<string | null> => Promise.resolve("2"),
} as Services,
};
1 change: 0 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ interface Services {
deletePlugin: APIRoute | ServiceFunction;
updatePlugin: APIRoute | ServiceFunction;
getAnnotationProgress: APIRoute | ServiceFunction;
launchDocs: APIRoute | ServiceFunction;
downloadDemoData: APIRoute | ServiceFunction;
}

Expand Down
28 changes: 24 additions & 4 deletions src/components/PageSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ButtonGroup } from "@mui/material";
import { ReactElement } from "react";
import { ReactElement, ReactNode } from "react";
import { Link, useLocation, useResolvedPath } from "react-router-dom";
import { IconButton, icons } from "@gliff-ai/style";
import { IconButton, ButtonGroup, icons, MuiCard } from "@gliff-ai/style";
import { User, UserAccess } from "@/interfaces";

const pageIcons: { [name: string]: string } = {
Expand Down Expand Up @@ -32,7 +31,12 @@ function NavLink({ name }: { name: string }): ReactElement {
);
}

export function PageSelector({ user }: { user: User }): ReactElement {
interface Props {
user: User;
ZooDialog: ReactNode;
}

export function PageSelector({ user, ZooDialog }: Props): ReactElement {
let links;

const isOwnerOrMember =
Expand All @@ -50,6 +54,9 @@ export function PageSelector({ user }: { user: User }): ReactElement {
return (
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
flexGrow: 0,
flexShrink: 0,
marginLeft: "20px",
Expand All @@ -61,6 +68,19 @@ export function PageSelector({ user }: { user: User }): ReactElement {
<NavLink name={name} key={name} />
))}
</ButtonGroup>
{ZooDialog && (
<MuiCard
variant="outlined"
sx={{
borderRadius: "9px",
"& > span > button": {
minWidth: "57px !important",
},
}}
>
{ZooDialog}
</MuiCard>
)}
</div>
);
}
10 changes: 5 additions & 5 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ export { PageSelector } from "./PageSelector";
export { EditProjectDialog } from "./projects/EditProjectDialog";
export { CreateProjectDialog } from "./projects/CreateProjectDialog";
export { ProgressBar } from "./ProgressBar";
export { AddPluginDialog } from "@/components/plugins/AddPluginDialog";
export { EditPluginDialog } from "@/components/plugins/EditPluginDialog";
export { DeletePluginDialog } from "@/components/plugins/DeletePluginDialog";
export { Notepad } from "@/components/Notepad";
export * from "@/components/table";
export { AddPluginDialog } from "./plugins/AddPluginDialog";
export { EditPluginDialog } from "./plugins/EditPluginDialog";
export { DeletePluginDialog } from "./plugins/DeletePluginDialog";
export { Notepad } from "./Notepad";
export * from "./table";
43 changes: 33 additions & 10 deletions src/components/plugins/AddPluginDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import {
Box,
Divider,
} from "@gliff-ai/style";
import { IPluginOut, Product, PluginType, Project } from "@/interfaces";
import { Plugin, Product, PluginType, Project } from "@/interfaces";
import { ServiceFunctions } from "@/api";
import { FormLabelControl } from "./FormLabelControl";
import { ProductsRadioForm } from "./ProductsRadioForm";
import { ProjectsAutocomplete } from "./ProjectsAutocomplete";
import { Notepad } from "../Notepad";

const marginTop = { marginTop: "15px" };
const divider = { width: "500px !important", margin: "12px -20px !important" };
Expand All @@ -33,28 +34,33 @@ interface Props {
services: ServiceFunctions;
setError: (error: string) => void;
getPlugins: () => void;
launchDocs: () => Window | null;
}

const defaultPlugin = {
const defaultPlugin: Plugin = {
type: PluginType.Javascript,
origin_id: null,
name: "",
description: "",
url: "",
products: Product.ALL,
enabled: false,
collection_uids: [] as string[],
is_public: false,
};

export function AddPluginDialog({
services,
setError,
projects,
getPlugins,
launchDocs,
}: Props): ReactElement {
const [closeDialog, setCloseDialog] = useState<boolean>(false);
const [key, setKey] = useState<string | null>(null);
const [creating, setCreating] = useState(false);
const [dialogPage, setDialogPage] = useState(DialogPage.pickPluginType);
const [newPlugin, setNewPlugin] = useState<IPluginOut>(defaultPlugin);
const [newPlugin, setNewPlugin] = useState<Plugin>(defaultPlugin);
const [validUrl, setValidUrl] = useState<boolean>(true);

useEffect(() => {
Expand All @@ -79,7 +85,7 @@ export function AddPluginDialog({

if (!projects) return null;

const addPluginToProjects = async (plugin: IPluginOut, email: string) => {
const addPluginToProjects = async (plugin: Plugin, email: string) => {
await Promise.allSettled(
plugin.collection_uids.map(async (projectUid) => {
try {
Expand All @@ -96,7 +102,10 @@ export function AddPluginDialog({

const createPlugin = async (): Promise<boolean> => {
try {
const result = (await services.createPlugin({ ...newPlugin })) as {
const result = (await services.createPlugin({
...newPlugin,
url: newPlugin.url.replace(/\/$/, ""), // remove trailing slash
})) as {
key: string;
email: string;
} | null;
Expand Down Expand Up @@ -126,7 +135,7 @@ export function AddPluginDialog({
<Box>
<FormControl
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setNewPlugin((p) => ({ ...p, type: e.target.value } as IPluginOut));
setNewPlugin((p) => ({ ...p, type: e.target.value } as Plugin));
}}
>
<h3>What type of plug-in do you want to register?</h3>
Expand Down Expand Up @@ -163,7 +172,7 @@ export function AddPluginDialog({
<Button
variant="outlined"
color="secondary"
onClick={() => services.launchDocs()}
onClick={launchDocs}
text="Learn more"
/>
<Button
Expand All @@ -186,8 +195,9 @@ export function AddPluginDialog({
<TextField
sx={{ ...marginTop }}
placeholder="Plug-in Name"
value={newPlugin.name}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setNewPlugin((p) => ({ ...p, name: e.target.value } as IPluginOut));
setNewPlugin((p) => ({ ...p, name: e.target.value } as Plugin));
}}
inputProps={{
maxLength: 50, // NOTE: name for python or AI plugins cannot be over 50 characters, otherwise 500
Expand All @@ -201,13 +211,26 @@ export function AddPluginDialog({
placeholder="Plug-in URL"
type="url"
error={!validUrl}
value={newPlugin.url}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
const url = e.target.value.replace(/\/$/, ""); // remove trailing slash
const url = e.target.value;
setValidUrl(isValidURL(url));
setNewPlugin((p) => ({ ...p, url } as IPluginOut));
setNewPlugin((p) => ({ ...p, url } as Plugin));
}}
/>
<Divider sx={{ ...divider }} />
<Notepad
placeholder="Plug-in Description (Optional)"
value={newPlugin.description}
onChange={(event) => {
setNewPlugin((p) => ({
...p,
description: event.target.value,
}));
}}
rows={6}
/>
<Divider sx={{ ...divider }} />
<ProductsRadioForm newPlugin={newPlugin} setNewPlugin={setNewPlugin} />
<Divider sx={{ ...divider }} />
<ProjectsAutocomplete
Expand Down
6 changes: 3 additions & 3 deletions src/components/plugins/DeletePluginDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Typography,
AdvancedDialog,
} from "@gliff-ai/style";
import { IPluginOut } from "@/interfaces";
import { Plugin } from "@/interfaces";
import { ServiceFunctions } from "@/api";

const purpleText = {
Expand All @@ -25,8 +25,8 @@ const purpleText = {
};

interface Props {
plugin: IPluginOut;
setPlugins: Dispatch<SetStateAction<IPluginOut[]>>;
plugin: Plugin;
setPlugins: Dispatch<SetStateAction<Plugin[]>>;
services: ServiceFunctions;
}

Expand Down
29 changes: 21 additions & 8 deletions src/components/plugins/EditPluginDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {
Button,
Typography,
} from "@gliff-ai/style";
import { IPluginOut, PluginType, Project } from "@/interfaces";
import { Plugin, PluginType, Project } from "@/interfaces";
import { ProjectsAutocomplete } from "./ProjectsAutocomplete";
import { ProductsRadioForm } from "./ProductsRadioForm";
import { ServiceFunctions } from "../../api";
import { Notepad } from "../Notepad";

const tab = {
display: "inline-block",
Expand All @@ -27,9 +28,9 @@ const divider = {

interface Props {
pendingProjectInvites: string[];
plugin: IPluginOut;
plugin: Plugin;
allProjects: Project[] | null;
updatePlugins: (prevPlugin: IPluginOut, plugin: IPluginOut) => void;
updatePlugins: (prevPlugin: Plugin, plugin: Plugin) => void;
services: ServiceFunctions;
setError: (error: string) => void;
}
Expand All @@ -42,7 +43,7 @@ export function EditPluginDialog({
services,
setError,
}: Props): ReactElement {
const [newPlugin, setNewPlugin] = useState<IPluginOut>(plugin);
const [newPlugin, setNewPlugin] = useState<Plugin>(plugin);
const [closeDialog, setCloseDialog] = useState<boolean>(false);

const resetDefaults = (): void => {
Expand All @@ -65,8 +66,8 @@ export function EditPluginDialog({
await Promise.all(
allProjects.map(async ({ uid, name }) => {
if (
!plugin.collection_uids.includes(uid) &&
newPlugin.collection_uids.includes(uid)
!plugin.collection_uids?.includes(uid) &&
newPlugin.collection_uids?.includes(uid)
) {
try {
await services.inviteToProject({
Expand Down Expand Up @@ -112,14 +113,26 @@ export function EditPluginDialog({
value={newPlugin.name}
placeholder="Plug-in Name"
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setNewPlugin((p) => ({ ...p, name: e.target.value } as IPluginOut));
setNewPlugin((p) => ({ ...p, name: e.target.value } as Plugin));
}}
inputProps={{
maxLength: 50, // NOTE: name for python or AI plugins cannot be over 50 characters, otherwise 500
}}
variant="outlined"
/>
<Divider sx={{ ...divider }} />
<Notepad
placeholder="Plug-in Description (Optional)"
value={newPlugin.description}
onChange={(event) => {
setNewPlugin((p) => ({
...p,
description: event.target.value,
}));
}}
rows={6}
/>
<Divider sx={{ ...divider }} />
<Typography sx={{ fontWeight: 700 }}>
Type:
<Box component="span" sx={{ ...tab }}>
Expand All @@ -131,7 +144,7 @@ export function EditPluginDialog({
<Box component="span" sx={{ ...tab }}>
{plugin.url}
</Box>
</Typography>{" "}
</Typography>
<Divider sx={{ ...divider }} />
<ProductsRadioForm newPlugin={newPlugin} setNewPlugin={setNewPlugin} />
<Divider sx={{ ...divider }} />
Expand Down
Loading