Skip to content

Commit

Permalink
[front] - feature: add user email to data source edit history
Browse files Browse the repository at this point in the history
 - Include the email address of the user who last edited a data source for better tracking

[front] - feature: introduce RequestDataSourcesModal component

 - Implement a new modal allowing non-admin users to request new data sources
 - Add a button to open the RequestDataSourcesModal in the data sources view
 - Adjust managed connectors to include the full EditedByUser object

[types] - refactor: update EditedByUser type to include email field

 - Enhance the EditedByUser type with an optional email field for additional user context
  • Loading branch information
Jules authored and Jules committed Aug 6, 2024
1 parent fc7c02e commit 4f74759
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 33 deletions.
1 change: 1 addition & 0 deletions front/lib/resources/data_source_resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export class DataSourceResource extends ResourceWithVault<DataSource> {
editedAt: editedAt.getTime(),
fullName: formatUserFullName(editedByUser),
imageUrl: editedByUser.imageUrl,
email: editedByUser.email,
},
};
}
Expand Down
86 changes: 55 additions & 31 deletions front/pages/w/[wId]/builder/data-sources/managed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import {
InformationCircleIcon,
Modal,
Page,
PlusIcon,
RobotIcon,
Searchbar,
} from "@dust-tt/sparkle";
import type {
ConnectorProvider,
DataSourceType,
EditedByUser,
ManageDataSourcesLimitsType,
Result,
UserType,
WhitelistableFeature,
WorkspaceType,
} from "@dust-tt/types";
Expand All @@ -42,6 +45,7 @@ import { useContext, useEffect, useMemo, useState } from "react";
import * as React from "react";

import ConnectorSyncingChip from "@app/components/data_source/DataSourceSyncChip";
import { RequestDataSourcesModal } from "@app/components/data_source/RequestDataSourcesModal";
import { subNavigationBuild } from "@app/components/navigation/config";
import AppLayout from "@app/components/sparkle/AppLayout";
import { SendNotificationsContext } from "@app/components/sparkle/Notification";
Expand All @@ -57,7 +61,7 @@ import type { PostManagedDataSourceRequestBody } from "@app/pages/api/w/[wId]/da

const { GA_TRACKING_ID = "" } = process.env;

type DataSourceIntegration = {
export type DataSourceIntegration = {
name: string;
dataSourceName: string | null;
connector: ConnectorType | null;
Expand All @@ -72,7 +76,7 @@ type DataSourceIntegration = {
synchronizedAgo: string | null;
setupWithSuffix: string | null;
usage: number | null;
editedByUser: string | null;
editedByUser?: EditedByUser | null;
};

type Info = {
Expand All @@ -81,7 +85,7 @@ type Info = {
icon: ComponentType;
name: string;
usage: number | null;
editedByUser: string | null;
editedByUser?: EditedByUser | null;
connector: ConnectorType | null;
workspaceId: string | undefined;
dataSourceName: string | undefined;
Expand Down Expand Up @@ -112,6 +116,16 @@ type GetTableRowParams = {
setShowPreviewPopupForProvider: (provider: ConnectorProvider | null) => void;
};

type ManagedConnector = {
dataSourceName: string;
provider: ConnectorProvider;
connector: ConnectorType | null;
fetchConnectorError: boolean;
fetchConnectorErrorMessage: string | null;
editedByUser?: EditedByUser | null;
usage: number | null;
};

const REDIRECT_TO_EDIT_PERMISSIONS = [
"confluence",
"google_drive",
Expand Down Expand Up @@ -150,10 +164,6 @@ export async function setupConnection({
return new Ok(connectionId);
}

function getUserImageUrl(dataSource: DataSourceType) {
return dataSource.editedByUser?.imageUrl ?? null;
}

export const getServerSideProps = withDefaultUserAuthRequirements<{
owner: WorkspaceType;
subscription: SubscriptionType;
Expand All @@ -163,12 +173,14 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
plan: PlanType;
gaTrackingId: string;
dustClientFacingUrl: string;
user: UserType;
}>(async (context, auth) => {
const owner = auth.workspace();
const plan = auth.plan();
const subscription = auth.subscription();
const user = auth.user();

if (!owner || !plan || !subscription) {
if (!owner || !plan || !subscription || !user) {
return {
notFound: true,
};
Expand All @@ -182,15 +194,7 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
.filter((ds) => ds.connectorId)
.filter((ds) => ds.connectorProvider !== "webcrawler");

const managedConnector: {
dataSourceName: string;
provider: ConnectorProvider;
connector: ConnectorType | null;
fetchConnectorError: boolean;
fetchConnectorErrorMessage: string | null;
editedByUser: string | null;
usage: number | null;
}[] = await Promise.all(
const managedConnector: ManagedConnector[] = await Promise.all(
managedDataSources.map(async (mds) => {
if (!mds.connectorId || !mds.connectorProvider) {
throw new Error(
Expand All @@ -211,7 +215,7 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
connector: null,
fetchConnectorError: true,
fetchConnectorErrorMessage: statusRes.error.message,
editedByUser: getUserImageUrl(mds),
editedByUser: mds.editedByUser,
usage: await getDataSourceUsage({
auth,
dataSource: mds,
Expand All @@ -224,7 +228,7 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
connector: statusRes.value,
fetchConnectorError: false,
fetchConnectorErrorMessage: null,
editedByUser: getUserImageUrl(mds),
editedByUser: mds.editedByUser,
usage: await getDataSourceUsage({
auth,
dataSource: mds,
Expand All @@ -240,7 +244,7 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
connector: null,
fetchConnectorError: true,
fetchConnectorErrorMessage: "Synchonization service is down",
editedByUser: getUserImageUrl(mds),
editedByUser: mds.editedByUser,
usage: null,
};
}
Expand Down Expand Up @@ -319,6 +323,7 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
return {
props: {
owner,
user,
subscription,
readOnly,
isAdmin,
Expand Down Expand Up @@ -458,6 +463,7 @@ function ConfirmationModal({

export default function DataSourcesView({
owner,
user,
subscription,
readOnly,
isAdmin,
Expand All @@ -480,9 +486,10 @@ export default function DataSourcesView({
useState<ConnectorProvider | null>(null);
const [showConfirmConnection, setShowConfirmConnection] =
useState<DataSourceIntegration | null>(null);
const [isRequestDataSourceModalOpen, setIsRequestDataSourceModalOpen] =
useState(false);

const { admins, isAdminsLoading } = useAdmins(owner);

const planConnectionsLimits = plan.limits.connections;
const handleEnableManagedDataSource = async (
provider: ConnectorProvider,
Expand Down Expand Up @@ -699,21 +706,36 @@ export default function DataSourcesView({
</Hoverable>
</ContentMessage>
)}
<Searchbar
ref={searchBarRef}
name="search"
placeholder="Search (Name)"
value={dataSourceSearch}
onChange={(s) => {
setDataSourceSearch(s);
}}
/>
<div className="flex gap-2">
{!isAdmin && (
<Button
label="Request"
icon={PlusIcon}
onClick={() => setIsRequestDataSourceModalOpen(true)}
/>
)}
<Searchbar
ref={searchBarRef}
name="search"
placeholder="Search (Name)"
value={dataSourceSearch}
onChange={(s) => {
setDataSourceSearch(s);
}}
/>
</div>
<DataTable
data={connectionRows}
columns={getTableColumns()}
filter={dataSourceSearch}
filterColumn={"name"}
/>
<RequestDataSourcesModal
isOpen={isRequestDataSourceModalOpen}
onClose={() => setIsRequestDataSourceModalOpen(false)}
dataSourceIntegrations={dataSourceIntegrations}
currentUserEmail={user.email}
/>
</Page.Vertical>
</AppLayout>
);
Expand Down Expand Up @@ -746,7 +768,9 @@ function getTableColumns() {
{
header: "Managed by",
cell: (info: Info) => (
<DataTable.Cell avatarUrl={info.row.original.editedByUser ?? ""} />
<DataTable.Cell
avatarUrl={info.row.original.editedByUser?.imageUrl ?? ""}
/>
),
},
{
Expand Down
5 changes: 3 additions & 2 deletions types/src/front/data_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ export function isConnectorProvider(val: string): val is ConnectorProvider {

export const PROVIDERS_WITH_SETTINGS: ConnectorProvider[] = ["webcrawler"];

export interface EditedByUser {
export type EditedByUser = {
editedAt: number | null;
fullName: string | null;
imageUrl: string | null;
}
email: string | null;
};

export type DataSourceType = {
id: ModelId;
Expand Down

0 comments on commit 4f74759

Please sign in to comment.