fork refine

This commit is contained in:
Stefan Pejcic
2024-02-05 10:23:04 +01:00
parent 3fffde9a8f
commit 8496a83edb
3634 changed files with 715528 additions and 2 deletions

View File

@@ -0,0 +1,66 @@
import {
DevToolsContext,
DevtoolsEvent,
send,
} from "@refinedev/devtools-shared";
import clsx from "clsx";
import React from "react";
import { useSearchParams, Navigate } from "react-router-dom";
import { Authenticated } from "src/components/authenticated";
export const AfterLogin = () => {
return (
<Authenticated fallback={<Failure />}>
<Success />
</Authenticated>
);
};
const Failure = () => {
const [searchParams] = useSearchParams();
const errorParam = searchParams.get("error");
return (
<Navigate to={`/login${errorParam ? `?error=${errorParam}` : ""}`} />
);
};
const Success = () => {
const { ws } = React.useContext(DevToolsContext);
React.useEffect(() => {
if (ws) {
send(ws, DevtoolsEvent.DEVTOOLS_LOGIN_SUCCESS, {}).then(() => {
window.close();
});
}
}, [ws]);
return (
<div
className={clsx(
"re-flex-1",
"re-flex",
"re-items-center",
"re-justify-center",
"re-py-16",
)}
>
<div
className={clsx(
"re-w-full",
"re-flex",
"re-flex-col",
"re-gap-16",
"re-justify-center",
"re-items-center",
"re-text-center",
"re-text-gray-300",
"re-text-sm",
)}
>
Logged in successfully, you can close this window.
</div>
</div>
);
};

View File

@@ -0,0 +1,189 @@
import { LoginFlow } from "@ory/client";
import {
DevToolsContext,
DevtoolsEvent,
receive,
} from "@refinedev/devtools-shared";
import clsx from "clsx";
import React from "react";
import { useSearchParams } from "react-router-dom";
import { FeatureSlide, FeatureSlideMobile } from "src/components/feature-slide";
import { GithubIcon } from "src/components/icons/github";
import { GoogleIcon } from "src/components/icons/google";
import { LogoIcon } from "src/components/icons/logo";
import { ory } from "src/utils/ory";
export const Login = () => {
return (
<div
className={clsx(
"re-min-h-screen re-w-full",
"re-grid re-grid-cols-1 large:re-grid-cols-2 re-gap-4",
"re-p-4",
)}
>
<div
className={clsx(
"re-flex",
"re-justify-center",
"re-items-center",
"re-rounded-lg",
"re-bg-gray-800",
"re-hidden large:re-flex",
)}
>
<FeatureSlide className={clsx("re-w-full", "re-max-w-3xl")} />
</div>
<div
className={clsx(
"re-flex",
"re-flex-col",
"re-items-center",
"re-justify-center",
)}
>
<LoginForm className={clsx("re-pt-2 tall:re-pt-0")} />
<FeatureSlideMobile
className={clsx("re-flex large:re-hidden", "re-mt-12")}
/>
</div>
</div>
);
};
const providers = [
{
name: "github",
icon: <GithubIcon className="re-w-6 re-h-6" />,
label: "Sign in with GitHub",
callback: "",
},
{
name: "google",
icon: <GoogleIcon className="re-w-6 re-h-6" />,
label: "Sign in with Google",
callback: "",
},
];
const LoginForm = (props: { className?: string }) => {
const { ws } = React.useContext(DevToolsContext);
const [searchParams] = useSearchParams();
const [flowData, setFlowData] = React.useState<LoginFlow | null>(null);
const error = searchParams.get("error");
const generateAuthFlow = React.useCallback(async () => {
try {
const redirectUrl = `${window.location.origin}/after-login`;
const { data } = await ory.createNativeLoginFlow({
refresh: true,
returnTo: redirectUrl,
});
setFlowData(data);
} catch (_error) {
console.error(_error);
}
}, [typeof window]);
React.useEffect(() => {
generateAuthFlow();
}, [generateAuthFlow]);
React.useEffect(() => {
if (ws) {
const unsub = receive(
ws,
DevtoolsEvent.DEVTOOLS_RELOAD_AFTER_LOGIN,
() => {
window.location.reload();
},
);
return unsub;
}
return () => 0;
}, [ws]);
return (
<div
className={clsx(
"re-max-w-[336px]",
"re-w-full",
"re-flex",
"re-flex-col",
"re-gap-16",
"re-justify-center",
"re-items-center",
props.className,
)}
>
<LogoIcon height={60} width={252} />
<form
className={clsx(
"re-w-full",
"re-flex",
"re-flex-col",
"re-items-center",
"re-justify-center",
"re-gap-4",
)}
action={flowData?.ui?.action}
method={flowData?.ui?.method}
target="_blank"
>
<input
type="hidden"
name="csrf_token"
value={(flowData?.ui.nodes[2].attributes as any)?.value}
/>
{providers.map(({ name, icon, label }) => (
<button
key={name}
id={name}
name="provider"
type="submit"
value={name}
disabled={!flowData}
className={clsx(
"re-w-full",
"re-py-2.5",
"re-px-4",
"re-bg-gray-0",
"re-text-gray-900",
"re-text-base",
"re-leading-6",
"re-font-semibold",
"re-gap-4",
"re-flex",
"re-items-center",
"re-justify-center",
"re-rounded",
)}
>
{icon}
<span>{label}</span>
</button>
))}
{error && (
<div
className={clsx(
"re-bg-alt-yellow",
"re-bg-opacity-20",
"re-border-2",
"re-border-alt-yellow",
"re-text-alt-yellow",
"re-text-xs",
"re-p-3",
"re-rounded",
)}
>
{error}
</div>
)}
</form>
</div>
);
};

View File

@@ -0,0 +1,323 @@
import React from "react";
import clsx from "clsx";
import {
DevToolsContext,
DevtoolsEvent,
receive,
hooksByScope,
RefineHook,
Scopes,
scopes,
} from "@refinedev/devtools-shared";
import {
Cell,
ColumnDef,
SortingState,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import dayjs from "dayjs";
import { ResizablePane } from "src/components/resizable-pane";
import type { Activity } from "src/interfaces/activity";
import { Status } from "src/components/status";
import { MonitorDetails } from "src/components/monitor-details";
import { TraceList } from "src/components/trace-list";
import { MonitorTable } from "src/components/monitor-table";
import { Filters, MonitorFilters } from "src/components/monitor-filters";
import { useSearchParams } from "react-router-dom";
import { getResourceValue } from "src/utils/get-resource-value";
import { ResourceValue } from "src/components/resource-value";
import { useLocalStorage } from "src/hooks/use-local-storage";
export const Monitor = () => {
const { ws } = React.useContext(DevToolsContext);
const [activities, setActivities] = React.useState<Activity[]>([]);
const [searchParams] = useSearchParams();
const highlightParam = searchParams.get("highlight");
const fetchActivities = React.useCallback(() => {
fetch("/api/activities")
.then((res) => res.json())
.then((data) => {
setActivities(data.data);
});
}, []);
React.useEffect(() => {
fetchActivities();
}, [fetchActivities]);
React.useEffect(() => {
if (!ws) return () => 0;
const unsub = receive(
ws,
DevtoolsEvent.DEVTOOLS_ACTIVITY_UPDATE,
(payload) => {
payload.updatedActivities.forEach((activity) => {
setActivities((activities) => {
const index = activities.findIndex(
(a) => a.identifier === activity.identifier,
);
if (index === -1) {
return [...activities, activity];
}
return [
...activities.slice(0, index),
activity,
...activities.slice(index + 1),
];
});
});
},
);
return unsub;
}, [ws]);
const columns = React.useMemo(
() =>
[
{
header: "Hook",
minSize: 90,
accessorFn: (activity) => activity.hookName,
},
{
header: "Resource",
minSize: 100,
accessorFn: getResourceValue,
cell: (cell: Cell<Activity, string>) => {
return (
<ResourceValue
resource={cell.getValue() as string}
scope={
scopes[
cell.row.original.hookName as RefineHook
]
}
/>
);
},
},
{
header: "Status",
minSize: 90,
accessorFn: (activity) => activity.status,
cell: (cell: Cell<Activity, Activity["status"]>) => (
<Status activity={cell.row.original} />
),
},
{
header: "Trace",
minSize: 280,
accessorFn: (activity) =>
[
...(activity.trace?.map((t) => t.function) ?? []),
].reverse(),
cell: (cell: Cell<Activity, string[] | undefined>) => {
return <TraceList trace={cell.row.original.trace} />;
},
},
{
header: "Created At",
minSize: 100,
accessorKey: "createdAt",
cell: (cell: Cell<Activity, Activity["createdAt"]>) => {
return dayjs(cell.getValue()).format("HH:mm:ss");
},
},
{
header: "Updated At",
minSize: 100,
accessorKey: "updatedAt",
cell: (cell: Cell<Activity, Activity["updatedAt"]>) => {
return dayjs(cell.getValue()).format("HH:mm:ss");
},
},
] as ColumnDef<Activity>[],
[],
);
const [filters, setFilters] = useLocalStorage<Filters>({
name: "monitor-filters",
defaultValue: {
hook: [],
parent: [],
resource: undefined,
scope: ["data"],
status: [],
},
});
React.useEffect(() => {
if (highlightParam) {
setFilters({
hook: [],
resource: undefined,
scope: [],
status: [],
parent: [highlightParam],
});
}
}, [highlightParam]);
const data = React.useMemo(() => {
let filtered = [...activities];
if (filters.scope && filters.scope.length > 0) {
const allowedHooks: RefineHook[] = [];
filters.scope.forEach((scope) => {
allowedHooks.push(...hooksByScope[scope as Scopes]);
});
filtered = filtered.filter((activity) =>
allowedHooks.includes(activity.hookName as RefineHook),
);
}
if (filters.hook && filters.hook.length > 0) {
filtered = filtered.filter((activity) =>
filters.hook.includes(activity.hookName),
);
}
if (filters.parent && filters.parent.length > 0) {
filtered = filtered.filter((activity) => {
return activity.trace?.some((trace) => {
if (!trace.function) return false;
return filters.parent.includes(trace.function);
});
});
}
if (filters.resource) {
filtered = filtered.filter((activity) => {
const resource = getResourceValue(activity);
resource.includes(filters.resource as string);
});
}
if (filters.status && filters.status.length > 0) {
filtered = filtered.filter((activity) =>
filters.status.includes(activity.status as any),
);
}
return filtered;
}, [activities, filters]);
const [sorting, setSorting] = React.useState<SortingState>([
{
id: "createdAt",
desc: true,
},
]);
const tableInstance = useReactTable({
columns,
data,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
const [selectedActivity, setSelectedActivity] = React.useState<
string | null
>(null);
const selectedActivityRecord = React.useMemo(
() => activities.find((el) => el.identifier === selectedActivity),
[activities, selectedActivity],
);
return (
<div className={clsx("flex-1", "re-h-full", "re-overflow-clip")}>
<div
className={clsx(
"re-flex",
"re-flex-col",
"re-gap-4",
"re-h-full",
"re-pb-4",
"md:re-pb-6",
"lg:re-pb-8",
)}
>
<div
className={clsx(
"re-flex",
"re-items-center",
"re-justify-between",
)}
>
<div
className={clsx(
"re-flex",
"re-items-center",
"re-justify-start",
)}
>
<div
className={clsx(
"re-text-gray-0",
"re-font-semibold",
"re-text-sm",
"re-leading-6",
)}
>
Monitor
</div>
<MonitorFilters
filters={filters}
activities={activities}
onSubmit={(f) => setFilters(f)}
/>
</div>
<div>
<div className={clsx("re-text-xs", "re-text-gray-300")}>
Count: {activities.length}
</div>
</div>
</div>
<div className={clsx("re-flex-1", "re-overflow-hidden")}>
<ResizablePane
left={
<MonitorTable
table={tableInstance}
columns={columns}
onSelect={setSelectedActivity}
selected={selectedActivity}
/>
}
right={
<MonitorDetails activity={selectedActivityRecord} />
}
className={clsx("re-h-full")}
leftClassName={clsx(
"re-rounded-lg",
"re-border",
"re-border-gray-700",
"re-overflow-auto",
"re-flex",
"re-flex-col",
)}
rightClassName={clsx(
"re-rounded-lg",
"re-border",
"re-border-gray-700",
"re-overflow-auto",
)}
/>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,189 @@
import clsx from "clsx";
import React from "react";
import { useNavigate } from "react-router-dom";
import { Button } from "src/components/button";
import { LogoIcon } from "src/components/icons/logo";
import { Input } from "src/components/input";
import { FeatureSlide, FeatureSlideMobile } from "src/components/feature-slide";
import { MeUpdateVariables } from "src/interfaces/api";
import { getMe, updateMe } from "src/utils/me";
export const Onboarding = () => {
return (
<div
className={clsx(
"re-min-h-screen re-w-full",
"re-grid re-grid-cols-1 large:re-grid-cols-2 re-gap-4",
"re-p-4",
)}
>
<div
className={clsx(
"re-flex",
"re-justify-center",
"re-items-center",
"re-rounded-lg",
"re-bg-gray-800",
"re-hidden large:re-flex",
)}
>
<FeatureSlide className={clsx("re-w-full", "re-max-w-3xl")} />
</div>
<div
className={clsx(
"re-flex",
"re-flex-col",
"re-items-center",
"re-justify-center",
)}
>
<OnboardingForm />
<FeatureSlideMobile
className={clsx("re-flex large:re-hidden", "re-mt-12")}
/>
</div>
</div>
);
};
const inputs = [
{
name: "name",
label: "Your name",
required: true,
placeholder: "Enter your name",
},
{
name: "jobTitle",
label: "Job title",
required: true,
placeholder: "Please enter your job title",
},
{
name: "company",
label: "Company name",
required: true,
placeholder: "Enter your company name",
},
] as const;
const links = [
{
name: "Privacy Policy",
url: "https://refine.dev/privacy-policy/",
},
{
name: "Terms and conditions",
url: "https://github.com/refinedev/refine/blob/master/LICENSE",
},
];
const OnboardingForm = () => {
const [values, setValues] = React.useState<MeUpdateVariables>({
name: "",
jobTitle: "",
company: "",
});
const navigate = useNavigate();
const fetchMe = React.useCallback(() => {
return getMe().then((me) => {
if (me && typeof me.name === "string") {
setValues((p) => ({
...p,
name: me.name as string,
}));
}
});
}, []);
React.useEffect(() => {
fetchMe();
}, [fetchMe]);
return (
<div
className={clsx(
"re-max-w-[336px]",
"re-w-full",
"re-flex",
"re-flex-col",
"re-gap-16",
"re-justify-center",
"re-items-center",
)}
>
<LogoIcon height={60} width={252} />
<div
className={clsx(
"re-flex",
"re-flex-col",
"re-items-center",
"re-justify-center",
"re-gap-6",
"re-w-full",
)}
>
{inputs.map(({ name, label, required, placeholder }) => (
<Input
key={name}
label={label}
required={required}
placeholder={placeholder}
value={values[name]}
onChange={(value) =>
setValues((prev) => ({
...prev,
[name]: value,
}))
}
className="re-w-full"
/>
))}
<div
className={clsx(
"re-w-full",
"re-flex",
"re-items-center",
"re-justify-end",
)}
>
<Button
onClick={() => {
updateMe(values).then(() => {
navigate("/overview");
});
}}
>
Continue
</Button>
</div>
</div>
<div
className={clsx(
"re-flex",
"re-items-center",
"re-justify-between",
"re-w-full",
)}
>
{links.map((link) => (
<a
key={link.name}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className={clsx(
"re-text-gray-500",
"re-underline",
"re-text-xs",
)}
>
{link.name}
</a>
))}
</div>
</div>
);
};

View File

@@ -0,0 +1,51 @@
import React from "react";
import clsx from "clsx";
import { Feed } from "src/components/feed";
import { Packages } from "src/components/packages";
export const Overview = () => {
return (
<div
className={clsx(
"flex-1",
"re-h-full",
"re-overflow-auto",
"large:re-overflow-clip",
)}
>
<div
className={clsx(
"re-h-auto",
"large:re-h-full",
"re-flex",
"re-flex-col",
"large:re-flex-row",
"re-gap-8",
"re-w-full",
"re-max-w-full",
"re-overflow-hidden",
"re-px-10",
"large:re-px-0",
"re-max-w-[640px]",
"large:re-max-w-none",
"re-mx-auto",
"large:re-mx-0",
)}
>
<Feed />
<div
className={clsx(
"re-hidden",
"large:re-block",
"re-h-auto",
"re-w-px",
"re-bg-gray-700",
"re-flex-shrink-0",
)}
/>
<Packages />
</div>
</div>
);
};