Feat: App folder and permission (#1726)
* app folder * feat: app foldere * fix: run app param error * perf: select app ux * perf: folder rerender * fix: ts * fix: parentId * fix: permission * perf: loading ux * perf: per select ux * perf: clb context * perf: query extension tip * fix: ts * perf: app detail per * perf: default per
This commit is contained in:
14
packages/global/common/parentFolder/type.d.ts
vendored
14
packages/global/common/parentFolder/type.d.ts
vendored
@@ -2,3 +2,17 @@ export type ParentTreePathItemType = {
|
||||
parentId: string;
|
||||
parentName: string;
|
||||
};
|
||||
export type ParentIdType = string | null | undefined;
|
||||
|
||||
export type GetResourceFolderListProps = {
|
||||
parentId: ParentIdType;
|
||||
};
|
||||
export type GetResourceFolderListItemResponse = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type GetResourceListItemResponse = GetResourceFolderListItemResponse & {
|
||||
avatar: string;
|
||||
isFolder: boolean;
|
||||
};
|
||||
|
||||
17
packages/global/common/parentFolder/utils.ts
Normal file
17
packages/global/common/parentFolder/utils.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ParentIdType } from './type';
|
||||
|
||||
export const parseParentIdInMongo = (parentId: ParentIdType) => {
|
||||
if (parentId === undefined) return {};
|
||||
|
||||
if (parentId === null || parentId === '')
|
||||
return {
|
||||
parentId: null
|
||||
};
|
||||
|
||||
const pattern = /^[0-9a-fA-F]{24}$/;
|
||||
if (pattern.test(parentId))
|
||||
return {
|
||||
parentId
|
||||
};
|
||||
return {};
|
||||
};
|
||||
@@ -1,10 +1,14 @@
|
||||
import { AppTTSConfigType, AppWhisperConfigType } from './type';
|
||||
|
||||
export enum AppTypeEnum {
|
||||
folder = 'folder',
|
||||
simple = 'simple',
|
||||
advanced = 'advanced'
|
||||
}
|
||||
export const AppTypeMap = {
|
||||
[AppTypeEnum.folder]: {
|
||||
label: 'folder'
|
||||
},
|
||||
[AppTypeEnum.simple]: {
|
||||
label: 'simple'
|
||||
},
|
||||
|
||||
9
packages/global/core/app/type.d.ts
vendored
9
packages/global/core/app/type.d.ts
vendored
@@ -1,5 +1,4 @@
|
||||
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type';
|
||||
|
||||
import { AppTypeEnum } from './constants';
|
||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||
import { VariableInputEnum } from '../workflow/constants';
|
||||
@@ -9,14 +8,17 @@ import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/use
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
import { PermissionValueType } from '../../support/permission/type';
|
||||
import { AppPermission } from '../../support/permission/app/controller';
|
||||
import { ParentIdType } from '../../common/parentFolder/type';
|
||||
|
||||
export type AppSchema = {
|
||||
_id: string;
|
||||
parentId?: ParentIdType;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
name: string;
|
||||
type: `${AppTypeEnum}`;
|
||||
type: AppTypeEnum;
|
||||
version?: 'v1' | 'v2';
|
||||
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
updateTime: number;
|
||||
@@ -39,6 +41,7 @@ export type AppListItemType = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
type: AppTypeEnum;
|
||||
defaultPermission: PermissionValueType;
|
||||
permission: AppPermission;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => ({
|
||||
limit: 1500,
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
usingReRank: false,
|
||||
datasetSearchUsingExtensionQuery: true,
|
||||
datasetSearchUsingExtensionQuery: false,
|
||||
datasetSearchExtensionBg: ''
|
||||
},
|
||||
selectedTools: [],
|
||||
|
||||
@@ -35,7 +35,10 @@ export const RunAppModule: FlowNodeTemplateType = {
|
||||
required: true
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput
|
||||
{
|
||||
...Input_Template_UserChatInput,
|
||||
toolDescription: '用户问题'
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
|
||||
@@ -105,8 +105,8 @@ export type NodeSourceNodeItemType = {
|
||||
/* --------------- function type -------------------- */
|
||||
export type SelectAppItemType = {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
// name: string;
|
||||
// logo?: string;
|
||||
};
|
||||
|
||||
/* agent */
|
||||
|
||||
@@ -17,4 +17,4 @@ export const AppPermissionList: PermissionListType = {
|
||||
}
|
||||
};
|
||||
|
||||
export const AppDefaultPermission = NullPermission;
|
||||
export const AppDefaultPermissionVal = NullPermission;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { PerConstructPros, Permission } from '../controller';
|
||||
import { AppDefaultPermission } from './constant';
|
||||
import { AppDefaultPermissionVal } from './constant';
|
||||
|
||||
export class AppPermission extends Permission {
|
||||
constructor(props?: PerConstructPros) {
|
||||
if (!props) {
|
||||
props = {
|
||||
per: AppDefaultPermission
|
||||
per: AppDefaultPermissionVal
|
||||
};
|
||||
} else if (!props?.per) {
|
||||
props.per = AppDefaultPermission;
|
||||
props.per = AppDefaultPermissionVal;
|
||||
}
|
||||
super(props);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Permission } from './controller';
|
||||
import { PermissionValueType } from './type';
|
||||
|
||||
export type CollaboratorItemType = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
permission: PermissionValueType;
|
||||
permission: Permission;
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ export const TeamPermissionList = {
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '可邀请, 删除成员'
|
||||
description: '可创建资源、邀请、删除成员'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getLLMModel } from '../ai/model';
|
||||
import { MongoAppVersion } from './version/schema';
|
||||
import { MongoApp } from './schema';
|
||||
|
||||
export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined>({
|
||||
nodes
|
||||
@@ -65,3 +66,40 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
||||
chatConfig: app?.chatConfig || {}
|
||||
};
|
||||
};
|
||||
|
||||
/* Get apps */
|
||||
export async function findAppAndAllChildren({
|
||||
teamId,
|
||||
appId,
|
||||
fields
|
||||
}: {
|
||||
teamId: string;
|
||||
appId: string;
|
||||
fields?: string;
|
||||
}): Promise<AppSchema[]> {
|
||||
const find = async (id: string) => {
|
||||
const children = await MongoApp.find(
|
||||
{
|
||||
teamId,
|
||||
parentId: id
|
||||
},
|
||||
fields
|
||||
).lean();
|
||||
|
||||
let apps = children;
|
||||
|
||||
for (const child of children) {
|
||||
const grandChildrenIds = await find(child._id);
|
||||
apps = apps.concat(grandChildrenIds);
|
||||
}
|
||||
|
||||
return apps;
|
||||
};
|
||||
const [app, childDatasets] = await Promise.all([MongoApp.findById(appId, fields), find(appId)]);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('Dataset not found');
|
||||
}
|
||||
|
||||
return [app, ...childDatasets];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppTypeMap } from '@fastgpt/global/core/app/constants';
|
||||
import { AppTypeEnum, AppTypeMap } from '@fastgpt/global/core/app/constants';
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { AppDefaultPermission } from '@fastgpt/global/support/permission/app/constant';
|
||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
|
||||
export const AppCollectionName = 'apps';
|
||||
|
||||
@@ -22,6 +22,11 @@ export const chatConfigType = {
|
||||
};
|
||||
|
||||
const AppSchema = new Schema({
|
||||
parentId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: AppCollectionName,
|
||||
default: null
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
@@ -38,8 +43,8 @@ const AppSchema = new Schema({
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'advanced',
|
||||
enum: Object.keys(AppTypeMap)
|
||||
default: AppTypeEnum.advanced,
|
||||
enum: Object.values(AppTypeEnum)
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
@@ -104,13 +109,13 @@ const AppSchema = new Schema({
|
||||
// the default permission of a app
|
||||
defaultPermission: {
|
||||
type: Number,
|
||||
default: AppDefaultPermission
|
||||
default: AppDefaultPermissionVal
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
AppSchema.index({ updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@@ -17,6 +17,8 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
|
||||
import { getHistories } from '../utils';
|
||||
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { authAppByTmbId } from '../../../../support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
@@ -32,27 +34,25 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
res,
|
||||
teamId,
|
||||
tmbId,
|
||||
stream,
|
||||
detail,
|
||||
histories,
|
||||
query,
|
||||
params: { userChatInput, history, app }
|
||||
} = props;
|
||||
let start = Date.now();
|
||||
|
||||
if (!userChatInput) {
|
||||
return Promise.reject('Input is empty');
|
||||
}
|
||||
|
||||
const appData = await MongoApp.findOne({
|
||||
_id: app.id,
|
||||
teamId
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId: app.id,
|
||||
teamId,
|
||||
tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
|
||||
if (!appData) {
|
||||
return Promise.reject('App not found');
|
||||
}
|
||||
|
||||
if (res && stream) {
|
||||
responseWrite({
|
||||
res,
|
||||
@@ -64,6 +64,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
}
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
const { files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||
...props,
|
||||
@@ -71,11 +72,11 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||
histories: chatHistories,
|
||||
query,
|
||||
variables: {
|
||||
...props.variables,
|
||||
userChatInput
|
||||
}
|
||||
query: runtimePrompt2ChatsValue({
|
||||
files,
|
||||
text: userChatInput
|
||||
}),
|
||||
variables: props.variables
|
||||
});
|
||||
|
||||
const completeMessages = chatHistories.concat([
|
||||
|
||||
@@ -5,6 +5,7 @@ import { MongoDataset } from '../../core/dataset/schema';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { SystemErrEnum } from '@fastgpt/global/common/error/code/system';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
|
||||
export const checkDatasetLimit = async ({
|
||||
teamId,
|
||||
@@ -66,7 +67,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
export const checkTeamAppLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, appCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoApp.count({ teamId })
|
||||
MongoApp.count({ teamId, type: { $in: [AppTypeEnum.advanced, AppTypeEnum.simple] } })
|
||||
]);
|
||||
|
||||
if (standardConstants && appCount >= standardConstants.maxAppAmount) {
|
||||
|
||||
@@ -46,6 +46,7 @@ export const iconPaths = {
|
||||
'common/refreshLight': () => import('./icons/common/refreshLight.svg'),
|
||||
'common/resultLight': () => import('./icons/common/resultLight.svg'),
|
||||
'common/retryLight': () => import('./icons/common/retryLight.svg'),
|
||||
'common/rightArrowFill': () => import('./icons/common/rightArrowFill.svg'),
|
||||
'common/rightArrowLight': () => import('./icons/common/rightArrowLight.svg'),
|
||||
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
|
||||
'common/saveFill': () => import('./icons/common/saveFill.svg'),
|
||||
@@ -71,6 +72,7 @@ export const iconPaths = {
|
||||
'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'),
|
||||
'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'),
|
||||
'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'),
|
||||
'core/app/simpleBot': () => import('./icons/core/app/simpleBot.svg'),
|
||||
'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'),
|
||||
'core/app/simpleMode/chat': () => import('./icons/core/app/simpleMode/chat.svg'),
|
||||
'core/app/simpleMode/dataset': () => import('./icons/core/app/simpleMode/dataset.svg'),
|
||||
|
||||
@@ -1,52 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style=" background: rgb(255, 255, 255); display: block; shape-rendering: auto;" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(30 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(60 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(90 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(120 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(150 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(180 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(210 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(240 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(270 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(300 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(330 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="330" height="330"
|
||||
style="shape-rendering: auto; display: block; background: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.9166666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(30 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.8333333333333334s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(60 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.75s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(90 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.6666666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(120 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.5833333333333334s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(150 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.5s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(180 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.4166666666666667s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(210 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.3333333333333333s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(240 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.25s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(270 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.16666666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(300 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.08333333333333333s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(330 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="0s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g></g>
|
||||
</g><!-- [ldio] generated by https://loading.io -->
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
|
||||
<path
|
||||
d="M5.56201 4.59992C5.56201 3.41205 6.9982 2.81716 7.83815 3.65711L11.2382 7.0572C11.7589 7.5779 11.7589 8.42212 11.2382 8.94282L7.83815 12.3429C6.9982 13.1829 5.56201 12.588 5.56201 11.4001V4.59992Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -0,0 +1,19 @@
|
||||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="5.9541" fill="url(#paint0_linear_5533_28322)" />
|
||||
<path
|
||||
d="M15.1466 6.22388C15.593 5.96615 16.3168 5.96615 16.7632 6.22388L23.5846 10.1622C24.4774 10.6776 24.4774 11.5134 23.5846 12.0288L16.853 15.9153C16.4066 16.1731 15.6828 16.1731 15.2364 15.9153L8.41507 11.977C7.52225 11.4616 7.52225 10.6258 8.41506 10.1104L15.1466 6.22388Z"
|
||||
fill="white" />
|
||||
<path
|
||||
d="M7.18506 14.3387C7.18506 13.617 7.6917 13.3245 8.31667 13.6853L14.6222 17.3258C14.9793 17.532 15.2688 18.0335 15.2688 18.4458V25.1119C15.2688 25.8336 14.7622 26.1261 14.1372 25.7653L7.83169 22.1248C7.47457 21.9186 7.18506 21.4172 7.18506 21.0048V14.3387Z"
|
||||
fill="white" />
|
||||
<path
|
||||
d="M16.7302 18.4962C16.7302 18.0838 17.0197 17.5823 17.3768 17.3762L23.6833 13.7351C24.3083 13.3742 24.8149 13.6668 24.8149 14.3884V21.0537C24.8149 21.4661 24.5254 21.9675 24.1683 22.1737L17.8618 25.8148C17.2368 26.1756 16.7302 25.8831 16.7302 25.1614V18.4962Z"
|
||||
fill="white" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_5533_28322" x1="16" y1="0" x2="4.88889" y2="29.3333"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#67BFFF" />
|
||||
<stop offset="1" stop-color="#5BA6FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -3,7 +3,14 @@ import { Divider, type DividerProps } from '@chakra-ui/react';
|
||||
|
||||
const MyDivider = (props: DividerProps) => {
|
||||
const { h } = props;
|
||||
return <Divider my={4} borderBottomWidth={h || '1x'} {...props}></Divider>;
|
||||
return (
|
||||
<Divider
|
||||
my={4}
|
||||
borderBottomWidth={h || '2px'}
|
||||
borderColor={props.color || 'myGray.200'}
|
||||
{...props}
|
||||
></Divider>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyDivider;
|
||||
|
||||
@@ -6,7 +6,7 @@ const Loading = ({
|
||||
text = '',
|
||||
bg = 'rgba(255,255,255,0.5)',
|
||||
zIndex = 1000,
|
||||
size = 'xl'
|
||||
size = 'lg'
|
||||
}: {
|
||||
fixed?: boolean;
|
||||
text?: string;
|
||||
@@ -17,7 +17,7 @@ const Loading = ({
|
||||
return (
|
||||
<Flex
|
||||
position={fixed ? 'fixed' : 'absolute'}
|
||||
zIndex={zIndex}
|
||||
zIndex={fixed ? zIndex : 10}
|
||||
bg={bg}
|
||||
borderRadius={'md'}
|
||||
top={0}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuList,
|
||||
@@ -10,21 +10,24 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '../Icon';
|
||||
import MyDivider from '../MyDivider';
|
||||
import type { IconNameType } from '../Icon/type';
|
||||
|
||||
type MenuItemType = 'primary' | 'danger';
|
||||
export type MenuItemType = 'primary' | 'danger';
|
||||
|
||||
export type Props = {
|
||||
width?: number | string;
|
||||
offset?: [number, number];
|
||||
Button: React.ReactNode;
|
||||
trigger?: 'hover' | 'click';
|
||||
iconSize?: string;
|
||||
menuList: {
|
||||
label?: string;
|
||||
children: {
|
||||
isActive?: boolean;
|
||||
type?: MenuItemType;
|
||||
icon?: string;
|
||||
icon?: IconNameType | string;
|
||||
label: string | React.ReactNode;
|
||||
description?: string;
|
||||
onClick: () => any;
|
||||
}[];
|
||||
}[];
|
||||
@@ -33,7 +36,8 @@ export type Props = {
|
||||
const MyMenu = ({
|
||||
width = 'auto',
|
||||
trigger = 'hover',
|
||||
offset = [0, 5],
|
||||
offset,
|
||||
iconSize = '1rem',
|
||||
Button,
|
||||
menuList
|
||||
}: Props) => {
|
||||
@@ -45,8 +49,8 @@ const MyMenu = ({
|
||||
}
|
||||
},
|
||||
danger: {
|
||||
color: 'red.600',
|
||||
_hover: {
|
||||
color: 'red.600',
|
||||
background: 'red.1'
|
||||
}
|
||||
}
|
||||
@@ -70,8 +74,14 @@ const MyMenu = ({
|
||||
}
|
||||
});
|
||||
|
||||
const computeOffset = useMemo<[number, number]>(() => {
|
||||
if (offset) return offset;
|
||||
if (typeof width === 'number') return [-width / 2, 5];
|
||||
return [0, 5];
|
||||
}, [offset]);
|
||||
|
||||
return (
|
||||
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'} isLazy>
|
||||
<Menu offset={computeOffset} isOpen={isOpen} autoSelect={false} direction={'ltr'} isLazy>
|
||||
<Box
|
||||
ref={ref}
|
||||
onMouseEnter={() => {
|
||||
@@ -90,7 +100,8 @@ const MyMenu = ({
|
||||
>
|
||||
<Box
|
||||
position={'relative'}
|
||||
onClickCapture={() => {
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
if (trigger === 'click') {
|
||||
setIsOpen(!isOpen);
|
||||
}
|
||||
@@ -124,8 +135,7 @@ const MyMenu = ({
|
||||
<MenuItem
|
||||
key={index}
|
||||
{...menuItemStyles}
|
||||
{...typeMapStyle[child.type || 'primary']}
|
||||
onClick={(e) => {
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
child.onClick && child.onClick();
|
||||
@@ -133,9 +143,19 @@ const MyMenu = ({
|
||||
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
_notLast={{ mb: 0.5 }}
|
||||
{...typeMapStyle[child.type || 'primary']}
|
||||
>
|
||||
{!!child.icon && <MyIcon name={child.icon as any} w={'16px'} mr={2} />}
|
||||
<Box>{child.label}</Box>
|
||||
{!!child.icon && <MyIcon name={child.icon as any} w={iconSize} mr={3} />}
|
||||
<Box>
|
||||
<Box color={child.description ? 'myGray.900' : 'inherit'} fontSize={'sm'}>
|
||||
{child.label}
|
||||
</Box>
|
||||
{child.description && (
|
||||
<Box color={'myGray.500'} fontSize={'mini'}>
|
||||
{child.description}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
92
packages/web/components/common/MyModal/EditFolderModal.tsx
Normal file
92
packages/web/components/common/MyModal/EditFolderModal.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalFooter, ModalBody, Input, Button, Box, Textarea } from '@chakra-ui/react';
|
||||
import MyModal from './index';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRequest2 } from '../../../hooks/useRequest';
|
||||
import FormLabel from '../MyBox/FormLabel';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
export type EditFolderFormType = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
intro?: string;
|
||||
};
|
||||
type CommitType = {
|
||||
name: string;
|
||||
intro?: string;
|
||||
};
|
||||
|
||||
const EditFolderModal = ({
|
||||
onClose,
|
||||
onCreate,
|
||||
onEdit,
|
||||
id,
|
||||
name,
|
||||
intro
|
||||
}: EditFolderFormType & {
|
||||
onClose: () => void;
|
||||
onCreate: (data: CommitType) => any;
|
||||
onEdit: (data: CommitType & { id: string }) => any;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const isEdit = !!id;
|
||||
const { register, handleSubmit } = useForm<EditFolderFormType>({
|
||||
defaultValues: {
|
||||
name,
|
||||
intro
|
||||
}
|
||||
});
|
||||
|
||||
const typeMap = useMemo(
|
||||
() =>
|
||||
isEdit
|
||||
? {
|
||||
title: t('dataset.Edit Folder')
|
||||
}
|
||||
: {
|
||||
title: t('dataset.Create Folder')
|
||||
},
|
||||
[isEdit, t]
|
||||
);
|
||||
|
||||
const { run: onSave, loading } = useRequest2(
|
||||
({ name = '', intro }: EditFolderFormType) => {
|
||||
if (!name) return;
|
||||
|
||||
if (isEdit) return onEdit({ id, name, intro });
|
||||
return onCreate({ name, intro });
|
||||
},
|
||||
{
|
||||
onSuccess: (res) => {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} iconSrc="common/folderFill" title={typeMap.title}>
|
||||
<ModalBody>
|
||||
<Box>
|
||||
<FormLabel mb={1}>{t('common.Input name')}</FormLabel>
|
||||
<Input
|
||||
{...register('name', { required: true })}
|
||||
bg={'myGray.50'}
|
||||
autoFocus
|
||||
maxLength={20}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={4}>
|
||||
<FormLabel mb={1}>{t('common.Input folder description')}</FormLabel>
|
||||
<Textarea {...register('intro')} bg={'myGray.50'} maxLength={200} />
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button isLoading={loading} onClick={handleSubmit(onSave)}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditFolderModal;
|
||||
@@ -78,7 +78,7 @@ const MyModal = ({
|
||||
{title}
|
||||
<Box flex={1} />
|
||||
{onClose && (
|
||||
<ModalCloseButton position={'relative'} fontSize={'sm'} top={0} right={0} />
|
||||
<ModalCloseButton position={'relative'} fontSize={'xs'} top={0} right={0} />
|
||||
)}
|
||||
</ModalHeader>
|
||||
)}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Flex, type FlexProps } from '@chakra-ui/react';
|
||||
|
||||
type ColorSchemaType = 'blue' | 'green' | 'red' | 'yellow' | 'gray' | 'purple' | 'adora';
|
||||
type ColorSchemaType = 'white' | 'blue' | 'green' | 'red' | 'yellow' | 'gray' | 'purple' | 'adora';
|
||||
|
||||
interface Props extends FlexProps {
|
||||
export type TagProps = FlexProps & {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
colorSchema?: ColorSchemaType;
|
||||
type?: 'fill' | 'borderFill' | 'borderSolid';
|
||||
}
|
||||
};
|
||||
|
||||
const colorMap: Record<
|
||||
ColorSchemaType,
|
||||
@@ -17,6 +17,11 @@ const colorMap: Record<
|
||||
color: string;
|
||||
}
|
||||
> = {
|
||||
white: {
|
||||
borderColor: 'myGray.400',
|
||||
bg: 'white',
|
||||
color: 'myGray.700'
|
||||
},
|
||||
yellow: {
|
||||
borderColor: 'yellow.200',
|
||||
bg: 'yellow.50',
|
||||
@@ -54,7 +59,7 @@ const colorMap: Record<
|
||||
}
|
||||
};
|
||||
|
||||
const MyTag = ({ children, colorSchema = 'blue', type = 'fill', ...props }: Props) => {
|
||||
const MyTag = ({ children, colorSchema = 'blue', type = 'fill', ...props }: TagProps) => {
|
||||
const theme = useMemo(() => {
|
||||
return colorMap[colorSchema];
|
||||
}, [colorSchema]);
|
||||
|
||||
@@ -72,6 +72,7 @@ export const useConfirm = (props?: {
|
||||
}) => {
|
||||
const timer = useRef<any>();
|
||||
const [countDownAmount, setCountDownAmount] = useState(countDown);
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
timer.current = setInterval(() => {
|
||||
@@ -85,14 +86,15 @@ export const useConfirm = (props?: {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '500px']}>
|
||||
<ModalBody pt={5} whiteSpace={'pre-wrap'}>
|
||||
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '400px']}>
|
||||
<ModalBody pt={5} whiteSpace={'pre-wrap'} fontSize={'sm'}>
|
||||
{customContent}
|
||||
</ModalBody>
|
||||
{!hideFooter && (
|
||||
<ModalFooter>
|
||||
{showCancel && (
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'whiteBase'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
@@ -104,13 +106,18 @@ export const useConfirm = (props?: {
|
||||
)}
|
||||
|
||||
<Button
|
||||
size={'sm'}
|
||||
bg={bg ? bg : map.bg}
|
||||
isDisabled={countDownAmount > 0}
|
||||
ml={4}
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
typeof confirmCb.current === 'function' && confirmCb.current();
|
||||
isLoading={isLoading || requesting}
|
||||
onClick={async () => {
|
||||
setRequesting(true);
|
||||
try {
|
||||
typeof confirmCb.current === 'function' && (await confirmCb.current());
|
||||
onClose();
|
||||
} catch (error) {}
|
||||
setRequesting(false);
|
||||
}}
|
||||
>
|
||||
{countDownAmount > 0 ? `${countDownAmount}s` : confirmText}
|
||||
|
||||
@@ -53,12 +53,13 @@ export const useRequest2 = <TData, TParams extends any[]>(
|
||||
plugin?: UseRequestFunProps<TData, TParams>[2]
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
const { errorToast, successToast, ...rest } = options || {};
|
||||
const { errorToast = 'Error', successToast, ...rest } = options || {};
|
||||
const { toast } = useToast();
|
||||
|
||||
const res = ahooksUseRequest<TData, TParams>(
|
||||
server,
|
||||
{
|
||||
manual: true,
|
||||
...rest,
|
||||
onError: (err, params) => {
|
||||
rest?.onError?.(err, params);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@lexical/selection": "^0.14.5",
|
||||
"@lexical/text": "0.12.6",
|
||||
"@lexical/utils": "0.12.6",
|
||||
"react-hook-form": "7.43.1",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@tanstack/react-query": "^4.24.10",
|
||||
"ahooks": "^3.7.11",
|
||||
|
||||
@@ -37,7 +37,7 @@ const Button = defineStyleConfig({
|
||||
sizes: {
|
||||
xs: {
|
||||
fontSize: 'xs',
|
||||
px: '8px',
|
||||
px: '2',
|
||||
py: '0',
|
||||
h: '24px',
|
||||
fontWeight: 'normal',
|
||||
@@ -54,7 +54,7 @@ const Button = defineStyleConfig({
|
||||
},
|
||||
sm: {
|
||||
fontSize: 'sm',
|
||||
px: '14px',
|
||||
px: '3',
|
||||
py: 0,
|
||||
fontWeight: 'normal',
|
||||
h: '30px',
|
||||
@@ -71,9 +71,9 @@ const Button = defineStyleConfig({
|
||||
},
|
||||
md: {
|
||||
fontSize: 'sm',
|
||||
px: '20px',
|
||||
px: '4',
|
||||
py: 0,
|
||||
h: '36px',
|
||||
h: '34px',
|
||||
fontWeight: 'normal',
|
||||
borderRadius: '8px'
|
||||
},
|
||||
@@ -81,14 +81,14 @@ const Button = defineStyleConfig({
|
||||
fontSize: 'sm',
|
||||
px: '0',
|
||||
py: 0,
|
||||
h: '36px',
|
||||
w: '36px',
|
||||
h: '34px',
|
||||
w: '34px',
|
||||
fontWeight: 'normal',
|
||||
borderRadius: '6px'
|
||||
},
|
||||
lg: {
|
||||
fontSize: 'md',
|
||||
px: '20px',
|
||||
px: '4',
|
||||
py: 0,
|
||||
h: '40px',
|
||||
fontWeight: 'normal',
|
||||
@@ -252,7 +252,7 @@ const Button = defineStyleConfig({
|
||||
transparentBase: {
|
||||
color: 'myGray.800',
|
||||
fontWeight: '500',
|
||||
bg: 'white',
|
||||
bg: 'transparent',
|
||||
transition: 'background 0.1s',
|
||||
_hover: {
|
||||
bg: 'myGray.150'
|
||||
@@ -263,6 +263,22 @@ const Button = defineStyleConfig({
|
||||
_disabled: {
|
||||
color: 'myGray.800 !important'
|
||||
}
|
||||
},
|
||||
transparentDanger: {
|
||||
color: 'myGray.800',
|
||||
fontWeight: '500',
|
||||
bg: 'transparent',
|
||||
transition: 'background 0.1s',
|
||||
_hover: {
|
||||
bg: 'myGray.150',
|
||||
color: 'red.600'
|
||||
},
|
||||
_active: {
|
||||
bg: 'myGray.150'
|
||||
},
|
||||
_disabled: {
|
||||
color: 'myGray.800 !important'
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
@@ -459,8 +475,8 @@ const Checkbox = checkBoxMultiStyle({
|
||||
const Modal = modalMultiStyle({
|
||||
baseStyle: modalPart({
|
||||
body: {
|
||||
py: [2, 4],
|
||||
px: [5, 7]
|
||||
py: 4,
|
||||
px: 7
|
||||
},
|
||||
footer: {
|
||||
pt: 2
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../**/*.d.ts"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../**/*.d.ts", "../../projects/app/src/components/common/Modal/EditResourceModal.tsx"],
|
||||
"exclude": ["node_modules","./components/common/Icon/constants.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user