mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-25 01:57:01 +00:00
Fix stack overflow error in condition group rendering by improving component structure and adding controls.
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/15a8b3d4-ed52-4589-a78f-46735f4177f5.jpg
This commit is contained in:
@@ -383,7 +383,99 @@ const ConditionBuilder: React.FC<ConditionBuilderProps> = ({
|
|||||||
{...attributes}
|
{...attributes}
|
||||||
>
|
>
|
||||||
{condition.isGroup ? (
|
{condition.isGroup ? (
|
||||||
renderConditionGroup(condition, index, level)
|
<div
|
||||||
|
key={`group-${index}`}
|
||||||
|
className="border border-border rounded-md p-3 mb-3"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => condition.id && toggleGroupExpand(condition.id)}
|
||||||
|
className="p-1 hover:bg-accent rounded-sm"
|
||||||
|
>
|
||||||
|
{condition.id && expandedGroups.has(condition.id) ? (
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
Condition Group ({condition.logicalOperator})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2" {...listeners}>
|
||||||
|
<Move className="h-4 w-4 text-muted-foreground mr-2 cursor-grab" />
|
||||||
|
|
||||||
|
<Select
|
||||||
|
value={condition.logicalOperator || 'AND'}
|
||||||
|
onValueChange={(value) => updateCondition(index, 'logicalOperator', value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-20 h-8">
|
||||||
|
<SelectValue placeholder="Operator" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{logicalOperators.map((op) => (
|
||||||
|
<SelectItem key={op.value} value={op.value}>
|
||||||
|
{op.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => addCondition(condition.id)}
|
||||||
|
title="Add Condition"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => addConditionGroup(condition.id)}
|
||||||
|
title="Add Group"
|
||||||
|
>
|
||||||
|
<FolderPlus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => removeCondition(index)}
|
||||||
|
title="Remove Group"
|
||||||
|
>
|
||||||
|
<Trash className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{condition.id && expandedGroups.has(condition.id) && (
|
||||||
|
<div className="pl-2">
|
||||||
|
{condition.id ? (
|
||||||
|
(() => {
|
||||||
|
const childConditions = getConditionsForParent(condition.id);
|
||||||
|
return childConditions.length === 0 ? (
|
||||||
|
<div className="text-sm text-muted-foreground p-2">
|
||||||
|
No conditions in this group. Add one using the buttons above.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
childConditions.map((child, childIndex) => {
|
||||||
|
const originalIndex = conditions.findIndex(c => c === child);
|
||||||
|
return child.isGroup
|
||||||
|
? <SortableItem key={`child-${originalIndex}`} condition={child} index={originalIndex} level={level + 1} />
|
||||||
|
: <SortableItem key={`child-${originalIndex}`} condition={child} index={originalIndex} level={level + 1} />;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})()
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
renderConditionContent(condition, index, level, listeners)
|
renderConditionContent(condition, index, level, listeners)
|
||||||
)}
|
)}
|
||||||
@@ -539,9 +631,11 @@ const ConditionBuilder: React.FC<ConditionBuilderProps> = ({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{conditions.map((condition, index) => (
|
{conditions.map((condition, index) => (
|
||||||
condition.parentId === null || condition.parentId === undefined ? (
|
condition.parentId === null || condition.parentId === undefined ? (
|
||||||
condition.isGroup ?
|
<SortableItem
|
||||||
renderConditionGroup(condition, index) :
|
key={`root-${index}`}
|
||||||
<SortableItem condition={condition} index={index} />
|
condition={condition}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
) : null
|
) : null
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,6 +45,29 @@ export interface IStorage {
|
|||||||
getRole(id: number): Promise<Role | undefined>;
|
getRole(id: number): Promise<Role | undefined>;
|
||||||
getDefaultRole(): Promise<Role | undefined>;
|
getDefaultRole(): Promise<Role | undefined>;
|
||||||
|
|
||||||
|
// Dynamic Group Rules
|
||||||
|
listDynamicGroupRules(): Promise<DynamicGroupRule[]>;
|
||||||
|
getDynamicGroupRule(id: number): Promise<DynamicGroupRule | undefined>;
|
||||||
|
createDynamicGroupRule(rule: InsertDynamicGroupRule): Promise<DynamicGroupRule>;
|
||||||
|
updateDynamicGroupRule(id: number, rule: Partial<DynamicGroupRule>): Promise<DynamicGroupRule | undefined>;
|
||||||
|
deleteDynamicGroupRule(id: number): Promise<boolean>;
|
||||||
|
|
||||||
|
// Dynamic Group Conditions
|
||||||
|
listConditionsForRule(ruleId: number): Promise<DynamicGroupCondition[]>;
|
||||||
|
createDynamicGroupCondition(condition: InsertDynamicGroupCondition): Promise<DynamicGroupCondition>;
|
||||||
|
updateDynamicGroupCondition(id: number, condition: Partial<DynamicGroupCondition>): Promise<DynamicGroupCondition | undefined>;
|
||||||
|
deleteDynamicGroupCondition(id: number): Promise<boolean>;
|
||||||
|
|
||||||
|
// Dynamic Group Rule Connections
|
||||||
|
linkRuleToConnections(ruleId: number, connectionIds: number[]): Promise<void>;
|
||||||
|
getConnectionsForRule(ruleId: number): Promise<number[]>;
|
||||||
|
|
||||||
|
// Schedule Rules
|
||||||
|
getSchedulesForRule(ruleId: number): Promise<ScheduleRule[]>;
|
||||||
|
createScheduleRule(schedule: InsertScheduleRule): Promise<ScheduleRule>;
|
||||||
|
updateScheduleRule(id: number, schedule: Partial<ScheduleRule>): Promise<ScheduleRule | undefined>;
|
||||||
|
deleteScheduleRule(id: number): Promise<boolean>;
|
||||||
|
|
||||||
// API Token management
|
// API Token management
|
||||||
getApiToken(id: number): Promise<ApiToken | undefined>;
|
getApiToken(id: number): Promise<ApiToken | undefined>;
|
||||||
getApiTokenByToken(token: string): Promise<ApiToken | undefined>;
|
getApiTokenByToken(token: string): Promise<ApiToken | undefined>;
|
||||||
@@ -1215,6 +1238,132 @@ export class DatabaseStorage implements IStorage {
|
|||||||
|
|
||||||
return { data, metadata };
|
return { data, metadata };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dynamic Group Rules methods
|
||||||
|
async listDynamicGroupRules(): Promise<DynamicGroupRule[]> {
|
||||||
|
return db.select().from(dynamicGroupRules).orderBy(dynamicGroupRules.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDynamicGroupRule(id: number): Promise<DynamicGroupRule | undefined> {
|
||||||
|
const result = await db.select().from(dynamicGroupRules).where(eq(dynamicGroupRules.id, id));
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createDynamicGroupRule(rule: InsertDynamicGroupRule): Promise<DynamicGroupRule> {
|
||||||
|
const result = await db.insert(dynamicGroupRules).values({
|
||||||
|
...rule,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}).returning();
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDynamicGroupRule(id: number, rule: Partial<DynamicGroupRule>): Promise<DynamicGroupRule | undefined> {
|
||||||
|
const result = await db.update(dynamicGroupRules)
|
||||||
|
.set({
|
||||||
|
...rule,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
.where(eq(dynamicGroupRules.id, id))
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteDynamicGroupRule(id: number): Promise<boolean> {
|
||||||
|
const result = await db.delete(dynamicGroupRules)
|
||||||
|
.where(eq(dynamicGroupRules.id, id))
|
||||||
|
.returning({ id: dynamicGroupRules.id });
|
||||||
|
return result.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic Group Conditions methods
|
||||||
|
async listConditionsForRule(ruleId: number): Promise<DynamicGroupCondition[]> {
|
||||||
|
return db.select()
|
||||||
|
.from(dynamicGroupConditions)
|
||||||
|
.where(eq(dynamicGroupConditions.ruleId, ruleId))
|
||||||
|
.orderBy(dynamicGroupConditions.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createDynamicGroupCondition(condition: InsertDynamicGroupCondition): Promise<DynamicGroupCondition> {
|
||||||
|
const result = await db.insert(dynamicGroupConditions).values(condition).returning();
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDynamicGroupCondition(id: number, condition: Partial<DynamicGroupCondition>): Promise<DynamicGroupCondition | undefined> {
|
||||||
|
const result = await db.update(dynamicGroupConditions)
|
||||||
|
.set(condition)
|
||||||
|
.where(eq(dynamicGroupConditions.id, id))
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteDynamicGroupCondition(id: number): Promise<boolean> {
|
||||||
|
const result = await db.delete(dynamicGroupConditions)
|
||||||
|
.where(eq(dynamicGroupConditions.id, id))
|
||||||
|
.returning({ id: dynamicGroupConditions.id });
|
||||||
|
return result.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic Group Rule Connections methods
|
||||||
|
async linkRuleToConnections(ruleId: number, connectionIds: number[]): Promise<void> {
|
||||||
|
// First delete existing connections
|
||||||
|
await db.delete(dynamicGroupRuleConnections)
|
||||||
|
.where(eq(dynamicGroupRuleConnections.ruleId, ruleId));
|
||||||
|
|
||||||
|
// Then insert new connections
|
||||||
|
if (connectionIds.length > 0) {
|
||||||
|
await db.insert(dynamicGroupRuleConnections)
|
||||||
|
.values(connectionIds.map(connectionId => ({
|
||||||
|
ruleId,
|
||||||
|
connectionId
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConnectionsForRule(ruleId: number): Promise<number[]> {
|
||||||
|
const connections = await db.select({ connectionId: dynamicGroupRuleConnections.connectionId })
|
||||||
|
.from(dynamicGroupRuleConnections)
|
||||||
|
.where(eq(dynamicGroupRuleConnections.ruleId, ruleId));
|
||||||
|
|
||||||
|
return connections.map(c => c.connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule Rules methods
|
||||||
|
async getSchedulesForRule(ruleId: number): Promise<ScheduleRule[]> {
|
||||||
|
return db.select()
|
||||||
|
.from(scheduleRules)
|
||||||
|
.where(eq(scheduleRules.ruleId, ruleId))
|
||||||
|
.orderBy(scheduleRules.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createScheduleRule(schedule: InsertScheduleRule): Promise<ScheduleRule> {
|
||||||
|
const result = await db.insert(scheduleRules)
|
||||||
|
.values({
|
||||||
|
...schedule,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateScheduleRule(id: number, schedule: Partial<ScheduleRule>): Promise<ScheduleRule | undefined> {
|
||||||
|
const result = await db.update(scheduleRules)
|
||||||
|
.set({
|
||||||
|
...schedule,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
.where(eq(scheduleRules.id, id))
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteScheduleRule(id: number): Promise<boolean> {
|
||||||
|
const result = await db.delete(scheduleRules)
|
||||||
|
.where(eq(scheduleRules.id, id))
|
||||||
|
.returning({ id: scheduleRules.id });
|
||||||
|
return result.length > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storage = new DatabaseStorage();
|
export const storage = new DatabaseStorage();
|
||||||
|
|||||||
Reference in New Issue
Block a user