mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
add table filters model
This commit is contained in:
parent
7c941da7df
commit
c833bcb37a
@ -55,6 +55,9 @@ import IncidentInternalNoteService, {
|
||||
import IncidentNoteTemplateService, {
|
||||
Service as IncidentNoteTemplateServiceType,
|
||||
} from "Common/Server/Services/IncidentNoteTemplateService";
|
||||
import TableViewService, {
|
||||
Service as TableViewServiceType,
|
||||
} from "Common/Server/Services/TableViewService";
|
||||
import IncidentOwnerTeamService, {
|
||||
Service as IncidentOwnerTeamServiceType,
|
||||
} from "Common/Server/Services/IncidentOwnerTeamService";
|
||||
@ -443,6 +446,7 @@ import ScheduledMaintenanceTemplateOwnerTeamService, {
|
||||
import ScheduledMaintenanceTemplateOwnerUserService, {
|
||||
Service as ScheduledMaintenanceTemplateOwnerUserServiceType,
|
||||
} from "Common/Server/Services/ScheduledMaintenanceTemplateOwnerUserService";
|
||||
import TableView from "Common/Models/DatabaseModels/TableView";
|
||||
|
||||
const BaseAPIFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
@ -776,6 +780,14 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
).getRouter(),
|
||||
);
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAPI<TableView, TableViewServiceType>(
|
||||
TableView,
|
||||
TableViewService,
|
||||
).getRouter(),
|
||||
);
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAPI<IncidentState, IncidentStateServiceType>(
|
||||
|
@ -307,7 +307,7 @@ const AllModelTypes: Array<{
|
||||
|
||||
TelemetryException,
|
||||
|
||||
TableView
|
||||
TableView,
|
||||
];
|
||||
|
||||
const modelTypeMap: { [key: string]: { new (): BaseModel } } = {};
|
||||
|
@ -168,7 +168,6 @@ export default class TableView extends BaseModel {
|
||||
})
|
||||
public name?: string = undefined;
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
@ -383,7 +382,6 @@ export default class TableView extends BaseModel {
|
||||
})
|
||||
public filters?: Query<BaseModel> = undefined;
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
@ -417,7 +415,6 @@ export default class TableView extends BaseModel {
|
||||
})
|
||||
public sort?: Sort<BaseModel> = undefined;
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
@ -448,8 +445,7 @@ export default class TableView extends BaseModel {
|
||||
type: ColumnType.Number,
|
||||
unique: false,
|
||||
nullable: false,
|
||||
default: 10
|
||||
default: 10,
|
||||
})
|
||||
public itemsOnPage?: number = undefined;
|
||||
|
||||
}
|
||||
|
@ -1,22 +1,39 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1727877987596 implements MigrationInterface {
|
||||
public name = 'MigrationName1727877987596'
|
||||
public name = "MigrationName1727877987596";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "TableView" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "name" character varying(100) NOT NULL, "tableId" character varying(100) NOT NULL, "description" character varying(500), "createdByUserId" uuid, "deletedByUserId" uuid, "filters" jsonb NOT NULL, "sort" jsonb NOT NULL, "itemsOnPage" integer NOT NULL DEFAULT '10', CONSTRAINT "PK_1e17a8834a65403cc87c4ead0cc" PRIMARY KEY ("_id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_a8691d4e023d907b5c73a43e42" ON "TableView" ("projectId") `);
|
||||
await queryRunner.query(`ALTER TABLE "TableView" ADD CONSTRAINT "FK_a8691d4e023d907b5c73a43e424" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "TableView" ADD CONSTRAINT "FK_2f2b7d1e199910951a0b5933d92" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "TableView" ADD CONSTRAINT "FK_b36d769f3d3d6fe4f9c35984551" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "TableView" DROP CONSTRAINT "FK_b36d769f3d3d6fe4f9c35984551"`);
|
||||
await queryRunner.query(`ALTER TABLE "TableView" DROP CONSTRAINT "FK_2f2b7d1e199910951a0b5933d92"`);
|
||||
await queryRunner.query(`ALTER TABLE "TableView" DROP CONSTRAINT "FK_a8691d4e023d907b5c73a43e424"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_a8691d4e023d907b5c73a43e42"`);
|
||||
await queryRunner.query(`DROP TABLE "TableView"`);
|
||||
}
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "TableView" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "name" character varying(100) NOT NULL, "tableId" character varying(100) NOT NULL, "description" character varying(500), "createdByUserId" uuid, "deletedByUserId" uuid, "filters" jsonb NOT NULL, "sort" jsonb NOT NULL, "itemsOnPage" integer NOT NULL DEFAULT '10', CONSTRAINT "PK_1e17a8834a65403cc87c4ead0cc" PRIMARY KEY ("_id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_a8691d4e023d907b5c73a43e42" ON "TableView" ("projectId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" ADD CONSTRAINT "FK_a8691d4e023d907b5c73a43e424" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" ADD CONSTRAINT "FK_2f2b7d1e199910951a0b5933d92" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" ADD CONSTRAINT "FK_b36d769f3d3d6fe4f9c35984551" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" DROP CONSTRAINT "FK_b36d769f3d3d6fe4f9c35984551"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" DROP CONSTRAINT "FK_2f2b7d1e199910951a0b5933d92"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TableView" DROP CONSTRAINT "FK_a8691d4e023d907b5c73a43e424"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_a8691d4e023d907b5c73a43e42"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "TableView"`);
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,8 @@ import ScheduledMaintenanceTemplateService from "./ScheduledMaintenanceTemplateS
|
||||
import ScheduledMaintenanceTemplateOwnerTeamService from "./ScheduledMaintenanceTemplateOwnerTeamService";
|
||||
import ScheduledMaintenanceTemplateOwnerUserService from "./ScheduledMaintenanceTemplateOwnerUserService";
|
||||
|
||||
import TableViewService from "./TableViewService";
|
||||
|
||||
const services: Array<BaseService> = [
|
||||
AcmeCertificateService,
|
||||
PromoCodeService,
|
||||
@ -285,6 +287,8 @@ const services: Array<BaseService> = [
|
||||
ScheduledMaintenanceTemplateService,
|
||||
ScheduledMaintenanceTemplateOwnerTeamService,
|
||||
ScheduledMaintenanceTemplateOwnerUserService,
|
||||
|
||||
TableViewService,
|
||||
];
|
||||
|
||||
export const AnalyticsServices: Array<
|
||||
|
10
Common/Server/Services/TableViewService.ts
Normal file
10
Common/Server/Services/TableViewService.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import TableView from "Common/Models/DatabaseModels/TableView";
|
||||
|
||||
export class Service extends DatabaseService<TableView> {
|
||||
public constructor() {
|
||||
super(TableView);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
@ -57,42 +57,46 @@ const Card: FunctionComponent<ComponentProps> = (
|
||||
</div>
|
||||
<div className="flex w-fit">
|
||||
{props.rightElement}
|
||||
{props.buttons?.map((button: CardButtonSchema | ReactElement, i: number) => {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
i > 0
|
||||
? {
|
||||
marginLeft: "10px",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
|
||||
{React.isValidElement(button) ? button : null}
|
||||
{React.isValidElement(button) ? null : (
|
||||
<Button
|
||||
key={i}
|
||||
title={(button as CardButtonSchema).title}
|
||||
buttonStyle={(button as CardButtonSchema).buttonStyle}
|
||||
className={(button as CardButtonSchema).className}
|
||||
onClick={() => {
|
||||
if ((button as CardButtonSchema).onClick) {
|
||||
(button as CardButtonSchema).onClick();
|
||||
{props.buttons?.map(
|
||||
(button: CardButtonSchema | ReactElement, i: number) => {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
i > 0
|
||||
? {
|
||||
marginLeft: "10px",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
{React.isValidElement(button) ? button : null}
|
||||
{React.isValidElement(button) ? null : (
|
||||
<Button
|
||||
key={i}
|
||||
title={(button as CardButtonSchema).title}
|
||||
buttonStyle={
|
||||
(button as CardButtonSchema).buttonStyle
|
||||
}
|
||||
}}
|
||||
disabled={(button as CardButtonSchema).disabled}
|
||||
icon={(button as CardButtonSchema).icon}
|
||||
shortcutKey={(button as CardButtonSchema).shortcutKey}
|
||||
dataTestId="card-button"
|
||||
isLoading={(button as CardButtonSchema).isLoading}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
className={(button as CardButtonSchema).className}
|
||||
onClick={() => {
|
||||
if ((button as CardButtonSchema).onClick) {
|
||||
(button as CardButtonSchema).onClick();
|
||||
}
|
||||
}}
|
||||
disabled={(button as CardButtonSchema).disabled}
|
||||
icon={(button as CardButtonSchema).icon}
|
||||
shortcutKey={
|
||||
(button as CardButtonSchema).shortcutKey
|
||||
}
|
||||
dataTestId="card-button"
|
||||
isLoading={(button as CardButtonSchema).isLoading}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -39,7 +39,9 @@ const CardModelDetail: <TBaseModel extends BaseModel>(
|
||||
) => ReactElement = <TBaseModel extends BaseModel>(
|
||||
props: ComponentProps<TBaseModel>,
|
||||
): ReactElement => {
|
||||
const [cardButtons, setCardButtons] = useState<Array<CardButtonSchema | ReactElement>>([]);
|
||||
const [cardButtons, setCardButtons] = useState<
|
||||
Array<CardButtonSchema | ReactElement>
|
||||
>([]);
|
||||
const [showModel, setShowModal] = useState<boolean>(false);
|
||||
const [item, setItem] = useState<TBaseModel | null>(null);
|
||||
const [refresher, setRefresher] = useState<boolean>(false);
|
||||
|
@ -93,7 +93,6 @@ export enum ShowAs {
|
||||
OrderedStatesList,
|
||||
}
|
||||
|
||||
|
||||
export interface SaveFilterProps {
|
||||
tableId: string;
|
||||
}
|
||||
@ -263,7 +262,9 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
||||
Array<ClassicFilterType<TBaseModel>>
|
||||
>([]);
|
||||
|
||||
const [cardButtons, setCardButtons] = useState<Array<CardButtonSchema | ReactElement>>([]);
|
||||
const [cardButtons, setCardButtons] = useState<
|
||||
Array<CardButtonSchema | ReactElement>
|
||||
>([]);
|
||||
|
||||
const [actionButtonSchema, setActionButtonSchema] = useState<
|
||||
Array<ActionButtonSchema<TBaseModel>>
|
||||
@ -793,21 +794,21 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
||||
return selectFields;
|
||||
};
|
||||
|
||||
|
||||
const getSaveFilterDropdown: GetReactElementFunction = (): ReactElement => {
|
||||
if(!props.saveFilterProps){
|
||||
return <></>
|
||||
if (!props.saveFilterProps) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if(props.saveFilterProps && props.saveFilterProps.tableId){
|
||||
return (<MoreMenu>
|
||||
<MoreMenuItem text="Save Filter" onClick={() => {
|
||||
}}></MoreMenuItem>
|
||||
</MoreMenu>)
|
||||
if (props.saveFilterProps && props.saveFilterProps.tableId) {
|
||||
return (
|
||||
<MoreMenu>
|
||||
<MoreMenuItem text="Save Filter" onClick={() => {}}></MoreMenuItem>
|
||||
</MoreMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>
|
||||
}
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const setHeaderButtons: VoidFunction = (): void => {
|
||||
// add header buttons.
|
||||
@ -863,7 +864,6 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (showFilterButton) {
|
||||
headerbuttons.push({
|
||||
title: "",
|
||||
@ -880,7 +880,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
||||
});
|
||||
}
|
||||
|
||||
if(props.saveFilterProps){
|
||||
if (props.saveFilterProps) {
|
||||
headerbuttons.push(getSaveFilterDropdown());
|
||||
}
|
||||
|
||||
@ -1641,8 +1641,6 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getCardComponent: GetReactElementFunction = (): ReactElement => {
|
||||
if (showAs === ShowAs.Table || showAs === ShowAs.List) {
|
||||
return (
|
||||
|
@ -1,12 +1,7 @@
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const MoreMenuItem: FunctionComponent = (
|
||||
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div className="py-1" role="none">
|
||||
</div>
|
||||
);
|
||||
const MoreMenuItem: FunctionComponent = (): ReactElement => {
|
||||
return <div className="py-1" role="none"></div>;
|
||||
};
|
||||
|
||||
export default MoreMenuItem;
|
||||
|
@ -1,41 +1,54 @@
|
||||
import React, { FunctionComponent, ReactElement, useEffect, useState } from "react";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import Button, { ButtonStyleType } from "../Button/Button";
|
||||
import IconProp from "../../../Types/Icon/IconProp";
|
||||
import useComponentOutsideClick from "../../Types/UseComponentOutsideClick";
|
||||
|
||||
export interface ComponentProps {
|
||||
children: Array<ReactElement> | ReactElement;
|
||||
children: Array<ReactElement> | ReactElement;
|
||||
}
|
||||
|
||||
const MoreMenu: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
|
||||
const { ref, isComponentVisible, setIsComponentVisible } =
|
||||
const { ref, isComponentVisible, setIsComponentVisible } =
|
||||
useComponentOutsideClick(false);
|
||||
|
||||
|
||||
const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
setDropdownVisible(isComponentVisible);
|
||||
}, [isComponentVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
setDropdownVisible(isComponentVisible);
|
||||
}, [isComponentVisible]);
|
||||
return (
|
||||
<div className="relative inline-block text-left">
|
||||
<div>
|
||||
<Button
|
||||
icon={IconProp.More}
|
||||
buttonStyle={ButtonStyleType.ICON}
|
||||
onClick={() => {
|
||||
setIsComponentVisible(!isDropdownVisible);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div className="relative inline-block text-left">
|
||||
<div>
|
||||
<Button icon={IconProp.More} buttonStyle={ButtonStyleType.ICON} onClick={()=>{
|
||||
setIsComponentVisible(!isDropdownVisible);
|
||||
}} />
|
||||
</div>
|
||||
|
||||
{isComponentVisible && <div ref={ref} className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button">
|
||||
{props.children}
|
||||
</div>}
|
||||
{isComponentVisible && (
|
||||
<div
|
||||
ref={ref}
|
||||
className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="menu-button"
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
);
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreMenu;
|
||||
|
@ -3,22 +3,31 @@ import IconProp from "../../../Types/Icon/IconProp";
|
||||
import Icon from "../Icon/Icon";
|
||||
|
||||
export interface ComponentProps {
|
||||
icon?: IconProp | undefined;
|
||||
text: string;
|
||||
onClick: () => void;
|
||||
icon?: IconProp | undefined;
|
||||
text: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const MoreMenuItem: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<a className="cursor-pointer group flex items-center px-4 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-50" role="menuitem" onClick={()=>{
|
||||
props.onClick();
|
||||
}}>
|
||||
{props.icon && <Icon icon={props.icon} className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500" />}
|
||||
{props.text}
|
||||
</a>
|
||||
);
|
||||
return (
|
||||
<a
|
||||
className="cursor-pointer group flex items-center px-4 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-50"
|
||||
role="menuitem"
|
||||
onClick={() => {
|
||||
props.onClick();
|
||||
}}
|
||||
>
|
||||
{props.icon && (
|
||||
<Icon
|
||||
icon={props.icon}
|
||||
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||
/>
|
||||
)}
|
||||
{props.text}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreMenuItem;
|
||||
|
Loading…
Reference in New Issue
Block a user