-
Notifications
You must be signed in to change notification settings - Fork 792
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Mubashir Shariq
authored and
Mubashir Shariq
committed
Sep 21, 2024
1 parent
64c806d
commit b88145f
Showing
6 changed files
with
377 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import RunbookIncidentTable from './runbook-table'; | ||
|
||
export default function RunbookPage() { | ||
return ( | ||
<div> | ||
<RunbookIncidentTable /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
"use client"; | ||
|
||
import React, { useState } from "react"; | ||
import Modal from "react-modal"; // Add this import for react-modal | ||
import { | ||
Button, | ||
Badge, | ||
Table as TremorTable, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeaderCell, | ||
TableRow, | ||
} from "@tremor/react"; | ||
import { DisplayColumnDef } from "@tanstack/react-table"; | ||
import { GenericTable } from "@/components/table/GenericTable"; | ||
|
||
|
||
const customStyles = { | ||
content: { | ||
top: '50%', | ||
left: '50%', | ||
right: 'auto', | ||
bottom: 'auto', | ||
marginRight: '-50%', | ||
transform: 'translate(-50%, -50%)', | ||
width: '400px', | ||
}, | ||
}; | ||
|
||
interface Incident { | ||
id: number; | ||
name: string; | ||
} | ||
|
||
interface Runbook { | ||
id: number; | ||
title: string; | ||
incidents: Incident[]; | ||
} | ||
|
||
const runbookData: Runbook[] = [ | ||
{ | ||
id: 1, | ||
title: "Database Recovery", | ||
incidents: [ | ||
{ id: 101, name: "DB Outage on 2024-01-01" }, | ||
{ id: 102, name: "DB Backup Failure" }, | ||
], | ||
}, | ||
{ | ||
id: 2, | ||
title: "API Health Check", | ||
incidents: [ | ||
{ id: 201, name: "API Latency Issue" }, | ||
], | ||
}, | ||
{ | ||
id: 3, | ||
title: "Server Restart Guide", | ||
incidents: [ | ||
{ id: 301, name: "Unexpected Server Crash" }, | ||
{ id: 302, name: "Scheduled Maintenance" }, | ||
], | ||
}, | ||
]; | ||
|
||
const columns: DisplayColumnDef<Runbook>[] = [ | ||
{ | ||
accessorKey: 'title', | ||
header: 'Runbook Title', | ||
cell: info => info.getValue(), | ||
}, | ||
{ | ||
accessorKey: 'incidents', | ||
header: 'Incidents', | ||
cell: info => ( | ||
<div> | ||
{info.getValue().map((incident: Incident) => ( | ||
<Badge key={incident.id} color="green" className="mr-2 mb-1"> | ||
{incident.name} | ||
</Badge> | ||
))} | ||
</div> | ||
), | ||
}, | ||
]; | ||
|
||
function RunbookIncidentTable() { | ||
const [offset, setOffset] = useState(0); | ||
const [limit, setLimit] = useState(10); | ||
|
||
// Modal state management | ||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
const [repositoryName, setRepositoryName] = useState(''); | ||
const [pathToMdFiles, setPathToMdFiles] = useState(''); | ||
|
||
const handlePaginationChange = (newLimit: number, newOffset: number) => { | ||
setLimit(newLimit); | ||
setOffset(newOffset); | ||
}; | ||
|
||
// Open modal handler | ||
const openModal = () => { | ||
setIsModalOpen(true); | ||
}; | ||
|
||
// Close modal handler | ||
const closeModal = () => { | ||
setIsModalOpen(false); | ||
}; | ||
|
||
// Handle save action from modal | ||
const handleSave = () => { | ||
// You can handle saving the data here (e.g., API call or updating state) | ||
console.log('Repository:', repositoryName); | ||
console.log('Path to MD Files:', pathToMdFiles); | ||
closeModal(); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button onClick={openModal}>Settings</Button> | ||
|
||
<GenericTable<Runbook> | ||
data={runbookData} | ||
columns={columns} | ||
rowCount={runbookData.length} | ||
offset={offset} | ||
limit={limit} | ||
onPaginationChange={handlePaginationChange} | ||
onRowClick={(row) => { | ||
console.log("Runbook clicked:", row); | ||
}} | ||
/> | ||
|
||
{/* Modal for Settings */} | ||
<Modal | ||
isOpen={isModalOpen} | ||
onRequestClose={closeModal} | ||
style={customStyles} | ||
contentLabel="Settings Modal" | ||
> | ||
<h2>Runbook Settings</h2> | ||
|
||
<div> | ||
<label>Repository Name</label> | ||
<input | ||
type="text" | ||
value={repositoryName} | ||
onChange={(e) => setRepositoryName(e.target.value)} | ||
placeholder="Enter repository name" | ||
style={{ width: '100%', padding: '8px', marginBottom: '10px' }} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<label>Path to MD Files</label> | ||
<input | ||
type="text" | ||
value={pathToMdFiles} | ||
onChange={(e) => setPathToMdFiles(e.target.value)} | ||
placeholder="Enter path to markdown files" | ||
style={{ width: '100%', padding: '8px', marginBottom: '10px' }} | ||
/> | ||
</div> | ||
|
||
<div style={{ textAlign: 'right' }}> | ||
<Button onClick={closeModal} style={{ marginRight: '10px' }}>Cancel</Button> | ||
<Button onClick={handleSave} color="blue">Save</Button> | ||
</div> | ||
</Modal> | ||
</div> | ||
); | ||
} | ||
|
||
export default RunbookIncidentTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from datetime import datetime | ||
from typing import List, Optional | ||
from uuid import UUID, uuid4 | ||
|
||
from pydantic import BaseModel | ||
from sqlalchemy import DateTime, ForeignKey, Column, TEXT, JSON | ||
from sqlmodel import Field, Relationship, SQLModel | ||
from keep.api.models.db.tenant import Tenant | ||
|
||
# Runbook Model | ||
class Runbook(SQLModel, table=True): | ||
id: UUID = Field(default_factory=uuid4, primary_key=True) | ||
tenant_id: str = Field(foreign_key="tenant.id") | ||
tenant: Tenant = Relationship() | ||
|
||
title: str = Field(nullable=False) # Title of the runbook | ||
link: str = Field(nullable=False) # Link to the .md file | ||
|
||
incidents: List["Incident"] = Relationship( | ||
back_populates="runbooks", link_model=RunbookToIncident | ||
) | ||
|
||
created_at: datetime = Field(default_factory=datetime.utcnow) | ||
|
||
class Config: | ||
arbitrary_types_allowed = True | ||
|
||
|
||
# Link Model between Runbook and Incident | ||
class RunbookToIncident(SQLModel, table=True): | ||
tenant_id: str = Field(foreign_key="tenant.id") | ||
runbook_id: UUID = Field(foreign_key="runbook.id", primary_key=True) | ||
incident_id: UUID = Field(foreign_key="incident.id", primary_key=True) | ||
|
||
incident_id: UUID = Field( | ||
sa_column=Column( | ||
UUID(binary=False), | ||
ForeignKey("incident.id", ondelete="CASCADE"), | ||
primary_key=True, | ||
) | ||
) | ||
|
||
|
||
# Incident Model | ||
class Incident(SQLModel, table=True): | ||
id: UUID = Field(default_factory=uuid4, primary_key=True) | ||
tenant_id: str = Field(foreign_key="tenant.id") | ||
tenant: Tenant = Relationship() | ||
|
||
user_generated_name: Optional[str] = None | ||
ai_generated_name: Optional[str] = None | ||
|
||
user_summary: Optional[str] = Field(sa_column=Column(TEXT), nullable=True) | ||
generated_summary: Optional[str] = Field(sa_column=Column(TEXT), nullable=True) | ||
|
||
assignee: Optional[str] = None | ||
severity: int = Field(default=IncidentSeverity.CRITICAL.order) | ||
|
||
creation_time: datetime = Field(default_factory=datetime.utcnow) | ||
|
||
start_time: Optional[datetime] = None | ||
end_time: Optional[datetime] = None | ||
last_seen_time: Optional[datetime] = None | ||
|
||
runbooks: List["Runbook"] = Relationship( | ||
back_populates="incidents", link_model=RunbookToIncident | ||
) | ||
|
||
is_predicted: bool = Field(default=False) | ||
is_confirmed: bool = Field(default=False) | ||
|
||
alerts_count: int = Field(default=0) | ||
affected_services: List = Field(sa_column=Column(JSON), default_factory=list) | ||
sources: List = Field(sa_column=Column(JSON), default_factory=list) | ||
|
||
rule_id: Optional[UUID] = Field( | ||
sa_column=Column( | ||
UUID(binary=False), | ||
ForeignKey("rule.id", ondelete="CASCADE"), | ||
nullable=True, | ||
), | ||
) | ||
|
||
rule_fingerprint: str = Field(default="", sa_column=Column(TEXT)) | ||
|
||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
if "runbooks" not in kwargs: | ||
self.runbooks = [] | ||
|
||
class Config: | ||
arbitrary_types_allowed = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.