fix: UI visibility issues, export errors, filtering, and audit log enhancement
Some checks failed
Auto Tag / auto-tag (push) Successful in 5s
Release / build-linux-arm64 (push) Failing after 2m19s
Test / rust-fmt-check (push) Failing after 2m18s
Release / build-macos-arm64 (push) Successful in 7m41s
Test / rust-clippy (push) Successful in 12m4s
Test / rust-tests (push) Successful in 12m40s
Test / frontend-typecheck (push) Successful in 1m43s
Test / frontend-tests (push) Successful in 1m21s
Release / build-linux-amd64 (push) Successful in 20m49s
Release / build-windows-amd64 (push) Successful in 13m59s
Some checks failed
Auto Tag / auto-tag (push) Successful in 5s
Release / build-linux-arm64 (push) Failing after 2m19s
Test / rust-fmt-check (push) Failing after 2m18s
Release / build-macos-arm64 (push) Successful in 7m41s
Test / rust-clippy (push) Successful in 12m4s
Test / rust-tests (push) Successful in 12m40s
Test / frontend-typecheck (push) Successful in 1m43s
Test / frontend-tests (push) Successful in 1m21s
Release / build-linux-amd64 (push) Successful in 20m49s
Release / build-windows-amd64 (push) Successful in 13m59s
- Fix download icons (PDF/DOCX) not visible in dark theme by adding text-foreground class - Fix "Read-only file system" error by using Downloads directory for exports with proper fallback - Fix Search button visibility in History page by changing variant and adding icon - Fix domain-only filtering in History page by adding missing filter.domain handling - Enhance audit log to capture full transmitted data (provider details, messages, content previews) - Add dirs crate dependency for cross-platform directory detection - Add success/error feedback for document exports with file path display - Update Security page to display pretty-printed JSON audit details Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b356eef44f
commit
0235541c9b
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ secrets.yml
|
||||
secrets.yaml
|
||||
artifacts/
|
||||
*.png
|
||||
/screenshots/
|
||||
|
||||
155
TICKET_SUMMARY.md
Normal file
155
TICKET_SUMMARY.md
Normal file
@ -0,0 +1,155 @@
|
||||
# Ticket Summary - UI Fixes and Audit Log Enhancement
|
||||
|
||||
## Description
|
||||
|
||||
This ticket addresses multiple UI and functionality issues reported in the tftsr-devops_investigation application:
|
||||
|
||||
1. **Download Icons Visibility**: Download icons (PDF, DOCX) in RCA and Post-Mortem pages were not visible in dark theme
|
||||
2. **Export File System Error**: "Read-only file system (os error 30)" error when attempting to export documents
|
||||
3. **History Search Button**: Search button not visible in the History page
|
||||
4. **Domain Filtering**: Domain-only filtering not working in History page
|
||||
5. **Audit Log Enhancement**: Audit log showed only internal IDs, lacking actual transmitted data for security auditing
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Download icons are visible in both light and dark themes on RCA and Post-Mortem pages
|
||||
- [ ] Documents can be exported successfully to Downloads directory without filesystem errors
|
||||
- [ ] Search button is visible with proper styling in History page
|
||||
- [ ] Domain filter works independently without requiring a search query
|
||||
- [ ] Audit log displays full transmitted data including:
|
||||
- AI chat messages with provider details, user message, and response preview
|
||||
- Document generation with content preview and metadata
|
||||
- All entries show properly formatted JSON with details
|
||||
|
||||
## Work Implemented
|
||||
|
||||
### 1. Download Icons Visibility Fix
|
||||
**Files Modified:**
|
||||
- `src/components/DocEditor.tsx:60-67`
|
||||
|
||||
**Changes:**
|
||||
- Added `text-foreground` class to Download icons for PDF and DOCX buttons
|
||||
- Ensures icons inherit the current theme's foreground color for visibility
|
||||
|
||||
### 2. Export File System Error Fix
|
||||
**Files Modified:**
|
||||
- `src-tauri/Cargo.toml:38` - Added `dirs = "5"` dependency
|
||||
- `src-tauri/src/commands/docs.rs:127-170` - Rewrote `export_document` function
|
||||
- `src/pages/RCA/index.tsx:53-60` - Updated error handling and user feedback
|
||||
- `src/pages/Postmortem/index.tsx:52-59` - Updated error handling and user feedback
|
||||
|
||||
**Changes:**
|
||||
- Modified `export_document` to use Downloads directory by default instead of "."
|
||||
- Falls back to `app_data_dir/exports` if Downloads directory unavailable
|
||||
- Added proper directory creation with error handling
|
||||
- Updated frontend to show success message with file path
|
||||
- Empty `output_dir` parameter now triggers default behavior
|
||||
|
||||
### 3. Search Button Visibility Fix
|
||||
**Files Modified:**
|
||||
- `src/pages/History/index.tsx:124-127`
|
||||
|
||||
**Changes:**
|
||||
- Changed button from `variant="outline"` to default variant
|
||||
- Added Search icon to button for better visibility
|
||||
- Button now has proper contrast in both themes
|
||||
|
||||
### 4. Domain-Only Filtering Fix
|
||||
**Files Modified:**
|
||||
- `src-tauri/src/commands/db.rs:305-312`
|
||||
|
||||
**Changes:**
|
||||
- Added missing `filter.domain` handling in `list_issues` function
|
||||
- Domain filter now properly filters by `i.category` field
|
||||
- Filter works independently of search query
|
||||
|
||||
### 5. Audit Log Enhancement
|
||||
**Files Modified:**
|
||||
- `src-tauri/src/commands/ai.rs:242-266` - Enhanced AI chat audit logging
|
||||
- `src-tauri/src/commands/docs.rs:44-73` - Enhanced RCA generation audit logging
|
||||
- `src-tauri/src/commands/docs.rs:90-119` - Enhanced postmortem generation audit logging
|
||||
- `src/pages/Settings/Security.tsx:191-206` - Enhanced audit log display
|
||||
|
||||
**Changes:**
|
||||
- AI chat audit now captures:
|
||||
- Provider name, model, and API URL
|
||||
- Full user message
|
||||
- Response preview (first 200 chars)
|
||||
- Token count
|
||||
- Document generation audit now captures:
|
||||
- Issue ID and title
|
||||
- Document type and title
|
||||
- Content length and preview (first 300 chars)
|
||||
- Security page now displays:
|
||||
- Pretty-printed JSON with proper formatting
|
||||
- Entry ID and entity type below the data
|
||||
- Better layout with whitespace handling
|
||||
|
||||
## Testing Needed
|
||||
|
||||
### Manual Testing
|
||||
|
||||
1. **Download Icons Visibility**
|
||||
- [ ] Open RCA page in light theme
|
||||
- [ ] Verify PDF and DOCX download icons are visible
|
||||
- [ ] Switch to dark theme
|
||||
- [ ] Verify PDF and DOCX download icons are still visible
|
||||
|
||||
2. **Export Functionality**
|
||||
- [ ] Generate an RCA document
|
||||
- [ ] Click "PDF" export button
|
||||
- [ ] Verify file is created in Downloads directory
|
||||
- [ ] Verify success message displays with file path
|
||||
- [ ] Check file opens correctly
|
||||
- [ ] Repeat for "MD" and "DOCX" formats
|
||||
- [ ] Test on Post-Mortem page as well
|
||||
|
||||
3. **History Search Button**
|
||||
- [ ] Navigate to History page
|
||||
- [ ] Verify Search button is visible
|
||||
- [ ] Verify button has search icon
|
||||
- [ ] Test button in both light and dark themes
|
||||
|
||||
4. **Domain Filtering**
|
||||
- [ ] Navigate to History page
|
||||
- [ ] Select a domain from dropdown (e.g., "Linux")
|
||||
- [ ] Do NOT enter any search text
|
||||
- [ ] Verify issues are filtered by selected domain
|
||||
- [ ] Change domain selection
|
||||
- [ ] Verify filtering updates correctly
|
||||
|
||||
5. **Audit Log**
|
||||
- [ ] Perform an AI chat interaction
|
||||
- [ ] Navigate to Settings > Security > Audit Log
|
||||
- [ ] Click "View" on a recent entry
|
||||
- [ ] Verify transmitted data shows:
|
||||
- Provider details
|
||||
- User message
|
||||
- Response preview
|
||||
- [ ] Generate an RCA or Post-Mortem
|
||||
- [ ] Check audit log for document generation entry
|
||||
- [ ] Verify content preview and metadata are visible
|
||||
|
||||
### Automated Testing
|
||||
|
||||
```bash
|
||||
# Type checking
|
||||
npx tsc --noEmit
|
||||
|
||||
# Rust compilation
|
||||
cargo check --manifest-path src-tauri/Cargo.toml
|
||||
|
||||
# Rust linting
|
||||
cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings
|
||||
|
||||
# Frontend tests (if applicable)
|
||||
npm run test:run
|
||||
```
|
||||
|
||||
### Edge Cases to Test
|
||||
|
||||
- Export when Downloads directory doesn't exist
|
||||
- Export with very long document titles (special character handling)
|
||||
- Domain filter with empty result set
|
||||
- Audit log with very large payloads (>1000 chars)
|
||||
- Audit log JSON parsing errors (malformed data)
|
||||
88
src-tauri/Cargo.lock
generated
88
src-tauri/Cargo.lock
generated
@ -918,6 +918,15 @@ dependencies = [
|
||||
"dirs-sys 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
@ -948,6 +957,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.4.6",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
@ -5293,6 +5314,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"dirs 5.0.1",
|
||||
"futures",
|
||||
"hex",
|
||||
"printpdf",
|
||||
@ -6492,6 +6514,15 @@ dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@ -6543,6 +6574,21 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@ -6600,6 +6646,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@ -6624,6 +6676,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
@ -6648,6 +6706,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
@ -6684,6 +6748,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
@ -6708,6 +6778,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
@ -6726,6 +6802,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@ -6750,6 +6832,12 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
|
||||
@ -36,6 +36,7 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.3"
|
||||
async-trait = "0.1"
|
||||
base64 = "0.22"
|
||||
dirs = "5"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
|
||||
@ -239,12 +239,24 @@ pub async fn chat_message(
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Audit
|
||||
// Audit - capture full transmission details
|
||||
let audit_details = serde_json::json!({
|
||||
"provider": provider_config.name,
|
||||
"model": provider_config.model,
|
||||
"api_url": provider_config.api_url,
|
||||
"user_message": user_msg.content,
|
||||
"response_preview": if response.content.len() > 200 {
|
||||
format!("{}...", &response.content[..200])
|
||||
} else {
|
||||
response.content.clone()
|
||||
},
|
||||
"token_count": user_msg.token_count,
|
||||
});
|
||||
let entry = AuditEntry::new(
|
||||
"ai_chat".to_string(),
|
||||
"issue".to_string(),
|
||||
issue_id,
|
||||
serde_json::json!({ "provider": provider_config.name }).to_string(),
|
||||
audit_details.to_string(),
|
||||
);
|
||||
let _ = db.execute(
|
||||
"INSERT INTO audit_log (id, timestamp, action, entity_type, entity_id, user_id, details) \
|
||||
|
||||
@ -306,6 +306,10 @@ pub async fn list_issues(
|
||||
sql.push_str(&format!(" AND i.category = ?{}", params.len() + 1));
|
||||
params.push(Box::new(category.clone()));
|
||||
}
|
||||
if let Some(ref domain) = filter.domain {
|
||||
sql.push_str(&format!(" AND i.category = ?{}", params.len() + 1));
|
||||
params.push(Box::new(domain.clone()));
|
||||
}
|
||||
if let Some(ref search) = filter.search {
|
||||
let pattern = format!("%{search}%");
|
||||
sql.push_str(&format!(
|
||||
|
||||
@ -40,13 +40,25 @@ pub async fn generate_rca(
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
// Audit
|
||||
// Audit - capture document metadata
|
||||
let db = state.db.lock().map_err(|e| e.to_string())?;
|
||||
let audit_details = serde_json::json!({
|
||||
"issue_id": issue_id,
|
||||
"issue_title": issue_detail.issue.title,
|
||||
"doc_type": "rca",
|
||||
"doc_title": document.title,
|
||||
"content_length": content_md.len(),
|
||||
"content_preview": if content_md.len() > 300 {
|
||||
format!("{}...", &content_md[..300])
|
||||
} else {
|
||||
content_md.clone()
|
||||
},
|
||||
});
|
||||
let entry = AuditEntry::new(
|
||||
"generate_rca".to_string(),
|
||||
"document".to_string(),
|
||||
doc_id,
|
||||
serde_json::json!({ "issue_id": issue_id }).to_string(),
|
||||
audit_details.to_string(),
|
||||
);
|
||||
let _ = db.execute(
|
||||
"INSERT INTO audit_log (id, timestamp, action, entity_type, entity_id, user_id, details) \
|
||||
@ -87,13 +99,25 @@ pub async fn generate_postmortem(
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
// Audit
|
||||
// Audit - capture document metadata
|
||||
let db = state.db.lock().map_err(|e| e.to_string())?;
|
||||
let audit_details = serde_json::json!({
|
||||
"issue_id": issue_id,
|
||||
"issue_title": issue_detail.issue.title,
|
||||
"doc_type": "postmortem",
|
||||
"doc_title": document.title,
|
||||
"content_length": content_md.len(),
|
||||
"content_preview": if content_md.len() > 300 {
|
||||
format!("{}...", &content_md[..300])
|
||||
} else {
|
||||
content_md.clone()
|
||||
},
|
||||
});
|
||||
let entry = AuditEntry::new(
|
||||
"generate_postmortem".to_string(),
|
||||
"document".to_string(),
|
||||
doc_id,
|
||||
serde_json::json!({ "issue_id": issue_id }).to_string(),
|
||||
audit_details.to_string(),
|
||||
);
|
||||
let _ = db.execute(
|
||||
"INSERT INTO audit_log (id, timestamp, action, entity_type, entity_id, user_id, details) \
|
||||
@ -129,7 +153,27 @@ pub async fn export_document(
|
||||
content_md: String,
|
||||
format: String,
|
||||
output_dir: String,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<String, String> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Determine the output directory
|
||||
let base_dir = if output_dir.is_empty() || output_dir == "." {
|
||||
// Try to use the Downloads directory, fall back to app data dir
|
||||
dirs::download_dir()
|
||||
.unwrap_or_else(|| {
|
||||
let app_data = state.app_data_dir.clone();
|
||||
app_data.join("exports")
|
||||
})
|
||||
} else {
|
||||
PathBuf::from(&output_dir)
|
||||
};
|
||||
|
||||
// Ensure the directory exists
|
||||
std::fs::create_dir_all(&base_dir).map_err(|e| {
|
||||
format!("Failed to create export directory {}: {}", base_dir.display(), e)
|
||||
})?;
|
||||
|
||||
let safe_title: String = title
|
||||
.chars()
|
||||
.map(|c| {
|
||||
@ -143,14 +187,16 @@ pub async fn export_document(
|
||||
|
||||
let output_path = match format.as_str() {
|
||||
"markdown" | "md" => {
|
||||
let path = format!("{output_dir}/{safe_title}.md");
|
||||
exporter::export_markdown(&content_md, &path).map_err(|e| e.to_string())?;
|
||||
path
|
||||
let path = base_dir.join(format!("{safe_title}.md"));
|
||||
exporter::export_markdown(&content_md, path.to_str().unwrap())
|
||||
.map_err(|e| e.to_string())?;
|
||||
path.to_string_lossy().to_string()
|
||||
}
|
||||
"pdf" => {
|
||||
let path = format!("{output_dir}/{safe_title}.pdf");
|
||||
exporter::export_pdf(&content_md, &title, &path).map_err(|e| e.to_string())?;
|
||||
path
|
||||
let path = base_dir.join(format!("{safe_title}.pdf"));
|
||||
exporter::export_pdf(&content_md, &title, path.to_str().unwrap())
|
||||
.map_err(|e| e.to_string())?;
|
||||
path.to_string_lossy().to_string()
|
||||
}
|
||||
_ => return Err(format!("Unsupported export format: {format}")),
|
||||
};
|
||||
|
||||
@ -58,11 +58,11 @@ export function DocEditor({ content, onChange, version, updatedAt, onExport }: D
|
||||
MD
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => onExport("pdf")}>
|
||||
<Download className="w-3 h-3 mr-1" />
|
||||
<Download className="w-3 h-3 mr-1 text-foreground" />
|
||||
PDF
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => onExport("docx")}>
|
||||
<Download className="w-3 h-3 mr-1" />
|
||||
<Download className="w-3 h-3 mr-1 text-foreground" />
|
||||
DOCX
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -121,7 +121,8 @@ export default function History() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Button variant="outline" onClick={handleSearch}>
|
||||
<Button onClick={handleSearch}>
|
||||
<Search className="w-4 h-4 mr-2" />
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -52,9 +52,11 @@ export default function Postmortem() {
|
||||
const handleExport = async (format: "md" | "pdf" | "docx") => {
|
||||
if (!doc) return;
|
||||
try {
|
||||
await exportDocumentCmd(doc.id, doc.title, content, format, ".");
|
||||
const path = await exportDocumentCmd(doc.id, doc.title, content, format, "");
|
||||
setError(`Document exported to: ${path}`);
|
||||
setTimeout(() => setError(null), 5000);
|
||||
} catch (err) {
|
||||
setError(String(err));
|
||||
setError(`Export failed: ${String(err)}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -53,9 +53,11 @@ export default function RCA() {
|
||||
const handleExport = async (format: "md" | "pdf" | "docx") => {
|
||||
if (!doc) return;
|
||||
try {
|
||||
await exportDocumentCmd(doc.id, doc.title, content, format, ".");
|
||||
const path = await exportDocumentCmd(doc.id, doc.title, content, format, "");
|
||||
setError(`Document exported to: ${path}`);
|
||||
setTimeout(() => setError(null), 5000);
|
||||
} catch (err) {
|
||||
setError(String(err));
|
||||
setError(`Export failed: ${String(err)}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -191,11 +191,16 @@ export default function Security() {
|
||||
{isExpanded && (
|
||||
<tr className="border-b bg-accent/20">
|
||||
<td colSpan={5} className="px-3 py-3">
|
||||
<div className="text-xs">
|
||||
<p className="font-medium text-foreground mb-1">Transmitted Data:</p>
|
||||
<pre className="bg-background/50 p-2 rounded text-xs overflow-x-auto text-foreground/80">
|
||||
{entry.details}
|
||||
<div className="text-xs space-y-2">
|
||||
<p className="font-medium text-foreground">Transmitted Data:</p>
|
||||
<pre className="bg-background/50 p-3 rounded text-xs overflow-x-auto text-foreground/80 whitespace-pre-wrap">
|
||||
{JSON.stringify(JSON.parse(entry.details), null, 2)}
|
||||
</pre>
|
||||
<div className="flex items-center gap-2 text-muted-foreground pt-1">
|
||||
<span>Entry ID: {entry.id}</span>
|
||||
<span>•</span>
|
||||
<span>Type: {entry.entity_type}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user