lula2 0.6.2-nightly.1 → 0.6.3-nightly.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/_app/immutable/chunks/{DsS_Ymqb.js → CnwkJbO2.js} +1 -1
- package/dist/_app/immutable/chunks/{DYH881pG.js → MTDiNHJy.js} +1 -1
- package/dist/_app/immutable/chunks/{D9NS9sy6.js → rc9CiIba.js} +2 -2
- package/dist/_app/immutable/entry/{app.C2_q7ef5.js → app.C9fkwjMz.js} +2 -2
- package/dist/_app/immutable/entry/start.D2hMBZNv.js +1 -0
- package/dist/_app/immutable/nodes/{0.7TiLCc65.js → 0.DTEvfwsS.js} +1 -1
- package/dist/_app/immutable/nodes/{1.qK8_Tc6M.js → 1.CVMum2jU.js} +1 -1
- package/dist/_app/immutable/nodes/{2.C0l3mgWP.js → 2.D-DlyZaW.js} +1 -1
- package/dist/_app/immutable/nodes/{3.bzLm8zUT.js → 3.CEMIejLc.js} +1 -1
- package/dist/_app/immutable/nodes/4.BRRWt-Y5.js +11 -0
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/crawl.js +90 -151
- package/dist/cli/commands/ui.js +45 -6
- package/dist/cli/server/index.js +45 -6
- package/dist/cli/server/server.js +45 -6
- package/dist/cli/server/spreadsheetRoutes.js +44 -5
- package/dist/cli/server/websocketServer.js +45 -6
- package/dist/index.html +6 -6
- package/dist/index.js +136 -151
- package/package.json +2 -2
- package/src/lib/components/setup/SpreadsheetImport.svelte +40 -15
- package/dist/_app/immutable/entry/start.CPOuKwpy.js +0 -1
- package/dist/_app/immutable/nodes/4.5BLVnpgw.js +0 -11
package/dist/index.js
CHANGED
|
@@ -2949,7 +2949,7 @@ function processImportParameters(reqBody) {
|
|
|
2949
2949
|
frontendFieldSchema
|
|
2950
2950
|
};
|
|
2951
2951
|
}
|
|
2952
|
-
async function parseUploadedFile(file) {
|
|
2952
|
+
async function parseUploadedFile(file, sheetName) {
|
|
2953
2953
|
const fileName = file.originalname || "";
|
|
2954
2954
|
const isCSV = fileName.toLowerCase().endsWith(".csv");
|
|
2955
2955
|
let rawData = [];
|
|
@@ -2958,10 +2958,13 @@ async function parseUploadedFile(file) {
|
|
|
2958
2958
|
rawData = parseCSV(csvContent);
|
|
2959
2959
|
} else {
|
|
2960
2960
|
const workbook = XLSX.read(file.buffer, { type: "buffer" });
|
|
2961
|
-
const worksheetName = workbook.SheetNames[0];
|
|
2961
|
+
const worksheetName = sheetName || workbook.SheetNames[0];
|
|
2962
2962
|
if (!worksheetName) {
|
|
2963
2963
|
throw new Error("No worksheet found in file");
|
|
2964
2964
|
}
|
|
2965
|
+
if (sheetName && !workbook.SheetNames.includes(sheetName)) {
|
|
2966
|
+
throw new Error(`Sheet "${sheetName}" not found in workbook`);
|
|
2967
|
+
}
|
|
2965
2968
|
const worksheet = workbook.Sheets[worksheetName];
|
|
2966
2969
|
rawData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null });
|
|
2967
2970
|
}
|
|
@@ -3568,12 +3571,14 @@ function exportAsJSON(controls, metadata, res) {
|
|
|
3568
3571
|
res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
|
|
3569
3572
|
res.json(exportData);
|
|
3570
3573
|
}
|
|
3571
|
-
var router, upload, spreadsheetRoutes_default;
|
|
3574
|
+
var MAX_HEADER_CANDIDATES, PREVIEW_COLUMNS, router, upload, spreadsheetRoutes_default;
|
|
3572
3575
|
var init_spreadsheetRoutes = __esm({
|
|
3573
3576
|
"cli/server/spreadsheetRoutes.ts"() {
|
|
3574
3577
|
"use strict";
|
|
3575
3578
|
init_debug();
|
|
3576
3579
|
init_serverState();
|
|
3580
|
+
MAX_HEADER_CANDIDATES = 5;
|
|
3581
|
+
PREVIEW_COLUMNS = 4;
|
|
3577
3582
|
router = express.Router();
|
|
3578
3583
|
upload = multer({
|
|
3579
3584
|
storage: multer.memoryStorage(),
|
|
@@ -3586,7 +3591,8 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3586
3591
|
return res.status(400).json({ error: "No file uploaded" });
|
|
3587
3592
|
}
|
|
3588
3593
|
const params = processImportParameters(req.body);
|
|
3589
|
-
const
|
|
3594
|
+
const sheetName = req.body.sheetName;
|
|
3595
|
+
const rawData = await parseUploadedFile(req.file, sheetName);
|
|
3590
3596
|
const startRowIndex = parseInt(params.startRow) - 1;
|
|
3591
3597
|
if (rawData.length <= startRowIndex) {
|
|
3592
3598
|
return res.status(400).json({ error: "Start row exceeds sheet data" });
|
|
@@ -3798,9 +3804,9 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3798
3804
|
const worksheet = workbook.Sheets[worksheetName];
|
|
3799
3805
|
rows = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null });
|
|
3800
3806
|
}
|
|
3801
|
-
const headerCandidates = rows.slice(0,
|
|
3807
|
+
const headerCandidates = rows.slice(0, MAX_HEADER_CANDIDATES).map((row, index) => ({
|
|
3802
3808
|
row: index + 1,
|
|
3803
|
-
preview: row.slice(0,
|
|
3809
|
+
preview: row.slice(0, PREVIEW_COLUMNS).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3804
3810
|
}));
|
|
3805
3811
|
res.json({
|
|
3806
3812
|
sheets,
|
|
@@ -3857,6 +3863,39 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3857
3863
|
res.status(500).json({ error: "Failed to parse Excel sheet" });
|
|
3858
3864
|
}
|
|
3859
3865
|
});
|
|
3866
|
+
router.post("/parse-excel-sheet-previews", upload.single("file"), async (req, res) => {
|
|
3867
|
+
try {
|
|
3868
|
+
const { sheetName } = req.body;
|
|
3869
|
+
if (!req.file) {
|
|
3870
|
+
return res.status(400).json({ error: "No file uploaded" });
|
|
3871
|
+
}
|
|
3872
|
+
const fileName = req.file.originalname || "";
|
|
3873
|
+
const isCSV = fileName.toLowerCase().endsWith(".csv");
|
|
3874
|
+
let rows = [];
|
|
3875
|
+
if (isCSV) {
|
|
3876
|
+
const csvContent = req.file.buffer.toString("utf-8");
|
|
3877
|
+
rows = parseCSV(csvContent);
|
|
3878
|
+
} else {
|
|
3879
|
+
const workbook = XLSX.read(req.file.buffer, { type: "buffer" });
|
|
3880
|
+
if (!workbook.SheetNames.includes(sheetName)) {
|
|
3881
|
+
return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
|
|
3882
|
+
}
|
|
3883
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
3884
|
+
rows = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null });
|
|
3885
|
+
}
|
|
3886
|
+
const headerCandidates = rows.slice(0, MAX_HEADER_CANDIDATES).map((row, index) => ({
|
|
3887
|
+
row: index + 1,
|
|
3888
|
+
preview: row.slice(0, PREVIEW_COLUMNS).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3889
|
+
}));
|
|
3890
|
+
res.json({
|
|
3891
|
+
rowPreviews: headerCandidates,
|
|
3892
|
+
totalRows: rows.length
|
|
3893
|
+
});
|
|
3894
|
+
} catch (error) {
|
|
3895
|
+
console.error("Error getting sheet previews:", error);
|
|
3896
|
+
res.status(500).json({ error: "Failed to get sheet previews" });
|
|
3897
|
+
}
|
|
3898
|
+
});
|
|
3860
3899
|
spreadsheetRoutes_default = router;
|
|
3861
3900
|
}
|
|
3862
3901
|
});
|
|
@@ -5599,209 +5638,155 @@ function containsLulaAnnotations(text) {
|
|
|
5599
5638
|
const lines = text.split("\n");
|
|
5600
5639
|
return lines.some((line) => line.includes("@lulaStart") || line.includes("@lulaEnd"));
|
|
5601
5640
|
}
|
|
5602
|
-
function
|
|
5603
|
-
return
|
|
5641
|
+
function crawlCommand() {
|
|
5642
|
+
return new Command().command("crawl").description("Detect compliance-related changes between @lulaStart and @lulaEnd in PR files").addOption(
|
|
5643
|
+
new Option("--post-mode <mode>", "How to post findings").choices(["review", "comment"]).default("review")
|
|
5644
|
+
).action(async (opts) => {
|
|
5645
|
+
let leavePost = false;
|
|
5646
|
+
const { owner, repo, pull_number } = getPRContext();
|
|
5647
|
+
console.log(`Analyzing PR #${pull_number} in ${owner}/${repo} for compliance changes...`);
|
|
5648
|
+
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
|
5649
|
+
const pr = await octokit.pulls.get({ owner, repo, pull_number });
|
|
5650
|
+
const prBranch = pr.data.head.ref;
|
|
5651
|
+
const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
|
|
5652
|
+
let commentBody = `${LULA_SIGNATURE}
|
|
5604
5653
|
## Lula Compliance Overview
|
|
5605
5654
|
|
|
5606
5655
|
Please review the changes to ensure they meet compliance standards.
|
|
5607
5656
|
|
|
5608
5657
|
### Reviewed Changes
|
|
5609
5658
|
|
|
5610
|
-
Lula reviewed ${
|
|
5659
|
+
Lula reviewed ${files.length} files changed that affect compliance.
|
|
5611
5660
|
|
|
5612
5661
|
`;
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5662
|
+
const deletedFilesWithAnnotations = [];
|
|
5663
|
+
for (const file of files) {
|
|
5664
|
+
if (file.status === "removed") {
|
|
5665
|
+
try {
|
|
5666
|
+
const oldText = await fetchRawFileViaAPI({
|
|
5667
|
+
octokit,
|
|
5668
|
+
owner,
|
|
5669
|
+
repo,
|
|
5670
|
+
path: file.filename,
|
|
5671
|
+
ref: "main"
|
|
5672
|
+
});
|
|
5673
|
+
if (containsLulaAnnotations(oldText)) {
|
|
5674
|
+
deletedFilesWithAnnotations.push(file.filename);
|
|
5675
|
+
}
|
|
5676
|
+
} catch (err) {
|
|
5677
|
+
console.error(`Error checking deleted file ${file.filename}: ${err}`);
|
|
5629
5678
|
}
|
|
5630
|
-
} catch (err) {
|
|
5631
|
-
console.error(`Error checking deleted file ${file.filename}: ${err}`);
|
|
5632
5679
|
}
|
|
5633
5680
|
}
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
}
|
|
5638
|
-
let warningContent = `
|
|
5681
|
+
if (deletedFilesWithAnnotations.length > 0) {
|
|
5682
|
+
leavePost = true;
|
|
5683
|
+
commentBody += `
|
|
5639
5684
|
|
|
5640
5685
|
**Compliance Warning: Files with Lula annotations were deleted**
|
|
5641
5686
|
|
|
5642
5687
|
`;
|
|
5643
|
-
|
|
5688
|
+
commentBody += `The following files contained compliance annotations (\`@lulaStart\`/\`@lulaEnd\`) and were deleted in this PR. This may affect compliance coverage:
|
|
5644
5689
|
|
|
5645
5690
|
`;
|
|
5646
|
-
|
|
5647
|
-
|
|
5691
|
+
for (const filename of deletedFilesWithAnnotations) {
|
|
5692
|
+
commentBody += `- \`${filename}\`
|
|
5648
5693
|
`;
|
|
5649
|
-
|
|
5650
|
-
|
|
5694
|
+
}
|
|
5695
|
+
commentBody += `
|
|
5651
5696
|
Please review whether:
|
|
5652
5697
|
`;
|
|
5653
|
-
|
|
5698
|
+
commentBody += `- The compliance coverage provided by these files is still needed
|
|
5654
5699
|
`;
|
|
5655
|
-
|
|
5700
|
+
commentBody += `- Alternative compliance measures have been implemented
|
|
5656
5701
|
`;
|
|
5657
|
-
|
|
5702
|
+
commentBody += `- The deletion is intentional and compliance-approved
|
|
5658
5703
|
|
|
5659
5704
|
`;
|
|
5660
|
-
|
|
5705
|
+
commentBody += `---
|
|
5661
5706
|
|
|
5662
5707
|
`;
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5708
|
+
}
|
|
5709
|
+
for (const file of files) {
|
|
5710
|
+
if (file.status === "added" || file.status === "removed") continue;
|
|
5711
|
+
try {
|
|
5712
|
+
const [oldText, newText] = await Promise.all([
|
|
5713
|
+
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
|
|
5714
|
+
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: prBranch })
|
|
5715
|
+
]);
|
|
5716
|
+
const changedBlocks = getChangedBlocks(oldText, newText);
|
|
5717
|
+
const removedBlocks = getRemovedBlocks(oldText, newText);
|
|
5718
|
+
for (const block of changedBlocks) {
|
|
5719
|
+
console.log(`Commenting regarding \`${file.filename}\`.`);
|
|
5720
|
+
leavePost = true;
|
|
5721
|
+
commentBody += `
|
|
5670
5722
|
|
|
5671
5723
|
---
|
|
5672
5724
|
| File | Lines Changed |
|
|
5673
5725
|
| ---- | ------------- |
|
|
5674
5726
|
`;
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5727
|
+
const newBlockText = newText.split("\n").slice(block.startLine, block.endLine).join("\n");
|
|
5728
|
+
const blockSha256 = createHash2("sha256").update(newBlockText).digest("hex");
|
|
5729
|
+
commentBody += `| \`${file.filename}\` | \`${block.startLine + 1}\u2013${block.endLine}\` |
|
|
5678
5730
|
> **uuid**-\`${block.uuid}\`
|
|
5679
5731
|
**sha256** \`${blockSha256}\`
|
|
5680
5732
|
|
|
5681
5733
|
`;
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
return "";
|
|
5688
|
-
}
|
|
5689
|
-
console.log(`Found removed annotations in \`${filename}\`.`);
|
|
5690
|
-
let content = `
|
|
5734
|
+
}
|
|
5735
|
+
if (removedBlocks.length > 0) {
|
|
5736
|
+
leavePost = true;
|
|
5737
|
+
console.log(`Found removed annotations in \`${file.filename}\`.`);
|
|
5738
|
+
commentBody += `
|
|
5691
5739
|
|
|
5692
|
-
**Compliance Warning: Lula annotations were removed from \`${filename}\`**
|
|
5740
|
+
**Compliance Warning: Lula annotations were removed from \`${file.filename}\`**
|
|
5693
5741
|
|
|
5694
5742
|
`;
|
|
5695
|
-
|
|
5743
|
+
commentBody += `The following compliance annotation blocks were present in the original file but are missing in the updated version:
|
|
5696
5744
|
|
|
5697
5745
|
`;
|
|
5698
|
-
|
|
5746
|
+
commentBody += `| File | Original Lines | UUID |
|
|
5699
5747
|
`;
|
|
5700
|
-
|
|
5748
|
+
commentBody += `| ---- | -------------- | ---- |
|
|
5701
5749
|
`;
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5750
|
+
for (const block of removedBlocks) {
|
|
5751
|
+
const oldBlockText = oldText.split("\n").slice(block.startLine, block.endLine).join("\n");
|
|
5752
|
+
const blockSha256 = createHash2("sha256").update(oldBlockText).digest("hex");
|
|
5753
|
+
commentBody += `| \`${file.filename}\` | \`${block.startLine + 1}\u2013${block.endLine}\` | \`${block.uuid}\` |
|
|
5706
5754
|
`;
|
|
5707
|
-
|
|
5755
|
+
commentBody += `> **sha256** \`${blockSha256}\`
|
|
5708
5756
|
|
|
5709
5757
|
`;
|
|
5710
|
-
|
|
5711
|
-
|
|
5758
|
+
}
|
|
5759
|
+
commentBody += `Please review whether:
|
|
5712
5760
|
`;
|
|
5713
|
-
|
|
5761
|
+
commentBody += `- The removal of these compliance annotations is intentional
|
|
5714
5762
|
`;
|
|
5715
|
-
|
|
5763
|
+
commentBody += `- Alternative compliance measures have been implemented
|
|
5716
5764
|
`;
|
|
5717
|
-
|
|
5765
|
+
commentBody += `- The compliance coverage is still adequate
|
|
5718
5766
|
|
|
5719
5767
|
`;
|
|
5720
|
-
|
|
5768
|
+
commentBody += `---
|
|
5721
5769
|
|
|
5722
5770
|
`;
|
|
5723
|
-
|
|
5724
|
-
}
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
let changesContent = "";
|
|
5728
|
-
let hasFindings = false;
|
|
5729
|
-
for (const file of files) {
|
|
5730
|
-
if (file.status === "added" || file.status === "removed") continue;
|
|
5731
|
-
try {
|
|
5732
|
-
const [oldText, newText] = await Promise.all([
|
|
5733
|
-
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
|
|
5734
|
-
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: prBranch })
|
|
5735
|
-
]);
|
|
5736
|
-
const changedBlocks = getChangedBlocks(oldText, newText);
|
|
5737
|
-
const removedBlocks = getRemovedBlocks(oldText, newText);
|
|
5738
|
-
if (changedBlocks.length > 0) {
|
|
5739
|
-
hasFindings = true;
|
|
5740
|
-
changesContent += generateChangedBlocksContent(file.filename, changedBlocks, newText);
|
|
5741
|
-
}
|
|
5742
|
-
if (removedBlocks.length > 0) {
|
|
5743
|
-
hasFindings = true;
|
|
5744
|
-
changesContent += generateRemovedBlocksContent(file.filename, removedBlocks, oldText);
|
|
5745
|
-
}
|
|
5746
|
-
} catch (err) {
|
|
5747
|
-
console.error(`Error processing ${file.filename}: ${err}`);
|
|
5771
|
+
}
|
|
5772
|
+
} catch (err) {
|
|
5773
|
+
console.error(`Error processing ${file.filename}: ${err}`);
|
|
5774
|
+
}
|
|
5748
5775
|
}
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
}
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
if (deletedAnalysis.hasFindings) {
|
|
5757
|
-
hasFindings = true;
|
|
5758
|
-
commentBody += deletedAnalysis.warningContent;
|
|
5759
|
-
}
|
|
5760
|
-
const modifiedAnalysis = await analyzeModifiedFiles(context);
|
|
5761
|
-
if (modifiedAnalysis.hasFindings) {
|
|
5762
|
-
hasFindings = true;
|
|
5763
|
-
commentBody += modifiedAnalysis.changesContent;
|
|
5764
|
-
}
|
|
5765
|
-
return { hasFindings, commentBody };
|
|
5766
|
-
}
|
|
5767
|
-
async function cleanupOldPosts(context, postMode) {
|
|
5768
|
-
const { octokit, owner, repo, pull_number } = context;
|
|
5769
|
-
if (postMode === "comment") {
|
|
5770
|
-
await deleteOldIssueComments({ octokit, owner, repo, pull_number });
|
|
5771
|
-
} else {
|
|
5772
|
-
await dismissOldReviews({ octokit, owner, repo, pull_number });
|
|
5773
|
-
await deleteOldReviewComments({ octokit, owner, repo, pull_number });
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5776
|
-
function crawlCommand() {
|
|
5777
|
-
return new Command().command("crawl").description("Detect compliance-related changes between @lulaStart and @lulaEnd in PR files").addOption(
|
|
5778
|
-
new Option("--post-mode <mode>", "How to post findings").choices(["review", "comment"]).default("review")
|
|
5779
|
-
).action(async (opts) => {
|
|
5780
|
-
const { owner, repo, pull_number } = getPRContext();
|
|
5781
|
-
console.log(`Analyzing PR #${pull_number} in ${owner}/${repo} for compliance changes...`);
|
|
5782
|
-
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
|
5783
|
-
const pr = await octokit.pulls.get({ owner, repo, pull_number });
|
|
5784
|
-
const prBranch = pr.data.head.ref;
|
|
5785
|
-
const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
|
|
5786
|
-
const context = {
|
|
5787
|
-
octokit,
|
|
5788
|
-
owner,
|
|
5789
|
-
repo,
|
|
5790
|
-
pull_number,
|
|
5791
|
-
prBranch,
|
|
5792
|
-
files
|
|
5793
|
-
};
|
|
5794
|
-
const analysisResult = await performComplianceAnalysis(context);
|
|
5795
|
-
await cleanupOldPosts(context, opts.postMode);
|
|
5796
|
-
if (analysisResult.hasFindings) {
|
|
5797
|
-
const finalBody = analysisResult.commentBody + closingBody;
|
|
5776
|
+
if (opts.postMode === "comment") {
|
|
5777
|
+
await deleteOldIssueComments({ octokit, owner, repo, pull_number });
|
|
5778
|
+
} else {
|
|
5779
|
+
await dismissOldReviews({ octokit, owner, repo, pull_number });
|
|
5780
|
+
await deleteOldReviewComments({ octokit, owner, repo, pull_number });
|
|
5781
|
+
}
|
|
5782
|
+
if (leavePost) {
|
|
5798
5783
|
await postFinding({
|
|
5799
5784
|
octokit,
|
|
5800
5785
|
postMode: opts.postMode,
|
|
5801
5786
|
owner,
|
|
5802
5787
|
repo,
|
|
5803
5788
|
pull_number,
|
|
5804
|
-
body:
|
|
5789
|
+
body: commentBody + closingBody
|
|
5805
5790
|
});
|
|
5806
5791
|
const header = `Posted (${opts.postMode})`;
|
|
5807
5792
|
const underline = "-".repeat(header.length);
|
|
@@ -5809,7 +5794,7 @@ function crawlCommand() {
|
|
|
5809
5794
|
${header}
|
|
5810
5795
|
${underline}
|
|
5811
5796
|
|
|
5812
|
-
${
|
|
5797
|
+
${commentBody + closingBody}
|
|
5813
5798
|
|
|
5814
5799
|
`);
|
|
5815
5800
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lula2",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3-nightly.0",
|
|
4
4
|
"description": "A tool for managing compliance as code in your GitHub repositories.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lula2": "./dist/lula2"
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"esbuild": "^0.25.9",
|
|
80
80
|
"eslint": "^9.35.0",
|
|
81
81
|
"eslint-config-prettier": "^10.1.8",
|
|
82
|
-
"eslint-plugin-jsdoc": "^
|
|
82
|
+
"eslint-plugin-jsdoc": "^61.0.0",
|
|
83
83
|
"eslint-plugin-svelte": "^3.12.2",
|
|
84
84
|
"globals": "^16.3.0",
|
|
85
85
|
"husky": "^9.1.7",
|
|
@@ -155,6 +155,27 @@
|
|
|
155
155
|
controlIdField = ''; // Reset control ID field selection
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
|
+
const previewFormData = new FormData();
|
|
159
|
+
previewFormData.append('file', fileData);
|
|
160
|
+
previewFormData.append('sheetName', selectedSheet);
|
|
161
|
+
|
|
162
|
+
const previewResponse = await fetch('/api/parse-excel-sheet-previews', {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
body: previewFormData
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (previewResponse.ok) {
|
|
168
|
+
const previewResult = await previewResponse.json();
|
|
169
|
+
rowPreviews = previewResult.rowPreviews || [];
|
|
170
|
+
|
|
171
|
+
if (rowPreviews.length > 0 && !rowPreviews.some((p) => p.row === headerRow)) {
|
|
172
|
+
headerRow = rowPreviews[0].row;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
const error = await previewResponse.json();
|
|
176
|
+
throw new Error(error.error || 'Failed to load sheet previews');
|
|
177
|
+
}
|
|
178
|
+
|
|
158
179
|
const formData = new FormData();
|
|
159
180
|
formData.append('file', fileData);
|
|
160
181
|
formData.append('sheetName', selectedSheet);
|
|
@@ -413,6 +434,7 @@
|
|
|
413
434
|
// Add configuration
|
|
414
435
|
formData.append('controlIdField', controlIdField);
|
|
415
436
|
formData.append('startRow', headerRow.toString());
|
|
437
|
+
formData.append('sheetName', selectedSheet);
|
|
416
438
|
formData.append('namingConvention', 'kebab-case');
|
|
417
439
|
formData.append('skipEmpty', 'true');
|
|
418
440
|
formData.append('skipEmptyRows', 'true');
|
|
@@ -437,21 +459,24 @@
|
|
|
437
459
|
JSON.stringify(justificationFields.map((field) => cleanFieldName(field)))
|
|
438
460
|
);
|
|
439
461
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
462
|
+
let response: Response;
|
|
463
|
+
try {
|
|
464
|
+
response = await fetch('/api/import-spreadsheet', {
|
|
465
|
+
method: 'POST',
|
|
466
|
+
body: formData
|
|
467
|
+
});
|
|
468
|
+
if (!response.ok) {
|
|
469
|
+
const error = await response.json();
|
|
470
|
+
throw new Error(error.error || 'Import failed');
|
|
471
|
+
}
|
|
449
472
|
|
|
450
|
-
|
|
451
|
-
|
|
473
|
+
const result = await response.json();
|
|
474
|
+
successMessage = `Successfully imported ${result.controlCount} controls into ${result.families.length} families`;
|
|
452
475
|
|
|
453
|
-
|
|
454
|
-
|
|
476
|
+
dispatch('created', { path: result.outputDir });
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error('Error importing spreadsheet:', error);
|
|
479
|
+
}
|
|
455
480
|
} catch (error) {
|
|
456
481
|
errorMessage = 'Error importing spreadsheet: ' + (error as Error).message;
|
|
457
482
|
} finally {
|
|
@@ -615,8 +640,8 @@
|
|
|
615
640
|
<select
|
|
616
641
|
id="sheet"
|
|
617
642
|
bind:value={selectedSheet}
|
|
618
|
-
on:change={() => {
|
|
619
|
-
loadSheetData();
|
|
643
|
+
on:change={async () => {
|
|
644
|
+
await loadSheetData();
|
|
620
645
|
}}
|
|
621
646
|
class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"
|
|
622
647
|
>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{l as o,a as r}from"../chunks/D9NS9sy6.js";export{o as load_css,r as start};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import"../chunks/DsnmJJEf.js";import{i as Je}from"../chunks/Cq7PwVfU.js";import{p as Ye,am as Te,d as o,T as k,G as ge,h as e,an as $r,ao as Ve,c as a,n as _e,r as t,b as d,e as We,J as F,K as E,ap as Lr,f as h,s as i,aq as g,a as ye,ar as mr,aj as Me,a3 as De,ak as hr,al as rr,as as yr,k as de,o as ot,W as lt}from"../chunks/DTWPdvjs.js";import{l as tr,p as Ue,i as M,a as st,s as it}from"../chunks/BXUi170M.js";import{a as Nr,s as se,r as Vr,e as ce,b as qe,c as jr,d as _r,i as Cr,f as Sr,w as wr}from"../chunks/e7OeeeKP.js";import{g as nt}from"../chunks/D9NS9sy6.js";function dt(pe){return function(...q){var z=q[0];return z.stopPropagation(),pe?.apply(this,q)}}var vt=Ve("<title> </title>"),ct=Ve('<svg><!><path d="M11 18L12.41 19.41 15 16.83 15 29 17 29 17 16.83 19.59 19.41 21 18 16 13 11 18z"></path><path d="M23.5,22H23V20h.5a4.5,4.5,0,0,0,.36-9L23,11l-.1-.82a7,7,0,0,0-13.88,0L9,11,8.14,11a4.5,4.5,0,0,0,.36,9H9v2H8.5A6.5,6.5,0,0,1,7.2,9.14a9,9,0,0,1,17.6,0A6.5,6.5,0,0,1,23.5,22Z"></path></svg>');function gt(pe,q){const z=tr(q,["children","$$slots","$$events","$$legacy"]),X=tr(z,["size","title"]);Ye(q,!1);const O=k(),L=k();let N=Ue(q,"size",8,16),f=Ue(q,"title",8,void 0);Te(()=>(ge(z),ge(f())),()=>{o(O,z["aria-label"]||z["aria-labelledby"]||f())}),Te(()=>(e(O),ge(z)),()=>{o(L,{"aria-hidden":e(O)?void 0:!0,role:e(O)?"img":void 0,focusable:Number(z.tabindex)===0?!0:void 0})}),$r(),Je();var S=ct();Nr(S,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:N(),height:N(),...e(L),...X}));var K=a(S);{var re=P=>{var p=vt(),U=a(p,!0);t(p),F(()=>E(U,f())),d(P,p)};M(K,P=>{f()&&P(re)})}_e(2),t(S),d(pe,S),We()}var pt=Ve("<title> </title>"),ut=Ve('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function kr(pe,q){const z=tr(q,["children","$$slots","$$events","$$legacy"]),X=tr(z,["size","title"]);Ye(q,!1);const O=k(),L=k();let N=Ue(q,"size",8,16),f=Ue(q,"title",8,void 0);Te(()=>(ge(z),ge(f())),()=>{o(O,z["aria-label"]||z["aria-labelledby"]||f())}),Te(()=>(e(O),ge(z)),()=>{o(L,{"aria-hidden":e(O)?void 0:!0,role:e(O)?"img":void 0,focusable:Number(z.tabindex)===0?!0:void 0})}),$r(),Je();var S=ut();Nr(S,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:N(),height:N(),...e(L),...X}));var K=a(S);{var re=P=>{var p=pt(),U=a(p,!0);t(p),F(()=>E(U,f())),d(P,p)};M(K,P=>{f()&&P(re)})}_e(),t(S),d(pe,S),We()}var bt=h('<div class="p-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <div><span class="font-medium">File loaded:</span> <div class="mt-1"><span class="font-medium">Sheets:</span> <span class="font-medium">Fields:</span> <span class="font-medium">Controls found:</span> </div></div></div></div>'),ft=h('<div class="p-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),xt=h('<div class="p-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),mt=h("<option> </option>"),ht=h("<option> </option>"),yt=h("<option> </option>"),_t=h('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),wt=h('<div draggable="true" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded text-sm cursor-move hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors opacity-75"><svg class="w-3 h-3 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg> <span class="truncate line-through"> </span> <!></div>'),kt=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),Ct=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),St=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),$t=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Dt=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Mt=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),zt=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),At=h('<div draggable="false" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 rounded text-sm hover:bg-orange-200 dark:hover:bg-orange-800/30 transition-colors"><span class="truncate"> </span> <button class="ml-auto text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400" title="Remove from mappings">×</button></div>'),Ft=h('<div role="region" aria-label="Justification field drop zone"><p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p></div>'),Ot=h('<th class="px-4 py-2"> </th>'),Pt=h('<td class="px-4 py-2"> </td>'),Ht=h('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),Et=h('<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sample Data Preview</h3> <div class="overflow-x-auto"><table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"><thead class="text-xs text-gray-700 uppercase bg-gray-100 dark:bg-gray-600 dark:text-gray-400"><tr></tr></thead><tbody></tbody></table></div></div>'),It=h('<span class="flex items-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Importing...</span>'),Tt=h(`<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Import Options</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"><div><label for="controlSetName" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control Set Name <span class="text-red-500">*</span></label> <input type="text" id="controlSetName" placeholder="e.g., NIST 800-53 Rev 4" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white" required/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">This will be used as the display name and folder name</p></div> <div><label for="controlSetDescription" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label> <input type="text" id="controlSetDescription" placeholder="Optional description" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Brief description of this control set</p></div></div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label for="sheet" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sheet</label> <select id="sheet" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select which worksheet contains your control data</p></div> <div><label for="headerRow" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Header Row</label> <select id="headerRow" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the row containing column headers</p></div> <div><label for="controlIdField" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control ID Field <span class="text-red-500">*</span></label> <select id="controlIdField" required><option disabled>Select Control ID field</option><!></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Column containing unique control identifiers (e.g., AC-1, SC-7)</p></div></div></div> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Organize Fields</h3> <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Drag fields to organize them. <strong>Overview fields</strong> will appear as table columns in
|
|
2
|
-
the controls list.</p> <div class="grid grid-cols-1 lg:grid-cols-5 gap-4"><div class="border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 rounded-t-lg"><h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Excluded Fields</h4> <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Not imported</p></div> <div role="region" aria-label="Excluded fields drop zone"><!> <!></div></div> <div class="border border-blue-300 dark:border-blue-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-blue-700 dark:text-blue-300">Overview Tab</h4> <p class="text-xs text-blue-600 dark:text-blue-400 mt-1">Shows in details & table columns</p></div> <div role="region" aria-label="Overview tab drop zone"><!> <!></div></div> <div class="border border-green-300 dark:border-green-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-green-700 dark:text-green-300">Implementation Tab</h4> <p class="text-xs text-green-600 dark:text-green-400 mt-1">Status & compliance</p></div> <div role="region" aria-label="Implementation tab drop zone"><!> <!></div></div> <div class="border border-purple-300 dark:border-purple-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-purple-200 dark:border-purple-800 bg-purple-50 dark:bg-purple-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-purple-700 dark:text-purple-300">Custom Tab</h4> <p class="text-xs text-purple-600 dark:text-purple-400 mt-1">Additional fields</p></div> <div role="region" aria-label="Custom fields drop zone"><!> <!></div></div> <div class="border border-orange-300 dark:border-orange-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-orange-700 dark:text-orange-300">Mappings Tab</h4> <p class="text-xs text-orange-600 dark:text-orange-400 mt-1">Pre-populate justification for a control mapping</p></div> <div role="region" aria-label="Justifications tab drop zone"><div class="space-y-2"><!></div></div></div></div></div> <!> <div class="flex justify-center"><button class="px-5 py-2.5 text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm disabled:opacity-50 disabled:cursor-not-allowed"><!></button></div>`,1),Vt=h('<div class="space-y-6"><div role="button" tabindex="0" class="relative"><label><div class="flex flex-col items-center justify-center pt-5 pb-6"><!> <p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p> <p class="text-xs text-gray-500 dark:text-gray-400">XLSX, XLS or CSV files</p></div> <input type="file" class="hidden" accept=".xlsx,.xls,.csv"/></label></div> <!> <!> <!> <!></div>');function jt(pe,q){Ye(q,!1);const z=Lr();let X=k(null),O=k(""),L=k(""),N=k([]),f=k([]),S=k([]),K=k(0),re=k([]),P=k([]),p=k(new Map),U=k(1),V=k(""),fe=k(""),J=k(""),G=k(!1),Q=k(""),ue=k(""),we=k(!1),te=k(!1),$=k(null),B=k(null),ae=k(null);function ke(){o(f,[]),o(S,[]),o(K,0),e(p).clear(),o(p,new Map),o(P,[]),o(V,""),o(Q,""),o(ue,""),o($,null),o(B,null),o(ae,null)}function u(l){l.preventDefault(),o(te,!0)}function b(){o(te,!1)}function D(l){l.preventDefault(),o(te,!1);const n=l.dataTransfer?.files;n&&n.length>0&&I(n[0])}function T(l){const n=l.target;n.files&&n.files.length>0&&I(n.files[0])}async function I(l){ke(),o(O,l.name),o(X,l),o(Q,""),o(G,!0),o(fe,e(O).replace(/\.[^.]+$/,"").replace(/[-_]/g," ")),o(J,`Imported from ${e(O)}`);try{const n=new FormData;n.append("file",l);const y=await fetch("/api/parse-excel",{method:"POST",body:n});if(!y.ok){const w=await y.json();throw new Error(w.error||"Failed to parse file")}const _=await y.json();o(N,_.sheets||[]),o(L,_.selectedSheet||e(N)[0]),o(re,_.rowPreviews||[]),e(re).length>0&&e(U)===1&&o(U,e(re)[0].row),await Z(),o(we,!0)}catch(n){o(Q,"Error reading file: "+n.message)}finally{o(G,!1)}}async function Z(){if(!(!e(X)||!e(L))){o(G,!0),e(p).clear(),o(p,new Map),o(V,"");try{const l=new FormData;l.append("file",e(X)),l.append("sheetName",e(L)),l.append("headerRow",e(U).toString());const n=await fetch("/api/parse-excel-sheet",{method:"POST",body:l});if(!n.ok){const _=await n.json();throw new Error(_.error||"Failed to parse sheet")}const y=await n.json();if(o(f,y.fields||[]),o(S,y.sampleData||[]),o(K,y.controlCount||0),e(f).forEach((_,w)=>{const v=_.toLowerCase();let H="custom",R="text";v.includes("implementation")||v.includes("status")||v.includes("narrative")||v.includes("guidance")?H="implementation":(v.includes("id")||v.includes("title")||v.includes("family")||v.includes("cci")||v.includes("control")||v.includes("acronym"))&&(H="overview"),v.includes("description")||v.includes("narrative")||v.includes("guidance")||v.includes("statement")?R="textarea":v.includes("status")||v.includes("type")||v.includes("designation")?R="select":v.includes("date")?R="date":(v.includes("count")||v.includes("number"))&&(R="number"),e(p).set(_,{originalName:_,tab:H,displayOrder:w,fieldType:R,required:v.includes("id")||v.includes("title")})}),o(p,e(p)),e(V)&&!e(f).includes(e(V))&&o(V,""),!e(V)&&e(f).includes("AP Acronym")){const _=!e(S).length||e(S).every(ee=>!ee["AP Acronym"]||String(ee["AP Acronym"]).length<25),w=e(S).map(ee=>ee["AP Acronym"]).filter(ee=>ee!=null&&ee!==""&&String(ee).trim()!==""),v=new Set(w),H=!w.length||v.size===w.length,R=w.length>0;_&&H&&R&&o(V,"AP Acronym")}}catch(l){o(Q,"Error loading sheet data: "+l.message)}finally{o(G,!1)}}}function ve(l){if(!l)return l;let n=l.trim().replace(/\r?\n/g," ").replace(/\s+/g," ").trim();return je(n)}function je(l){return l.replace(/\W+/g," ").split(/ |\s/).map(n=>n.toLowerCase()).join("-")}function xe(l,n){o($,n),l.dataTransfer&&(l.dataTransfer.effectAllowed="move",l.dataTransfer.setData("text/plain",n))}function oe(){o($,null),o(B,null),o(ae,null)}function C(l,n){l.preventDefault(),o(B,n),l.dataTransfer&&(l.dataTransfer.dropEffect="move")}function ie(){o(B,null)}function ne(l,n,y){if(l.preventDefault(),e($)&&e(p).has(e($))){const _=e(p).get(e($));if(n==="mappings")e(P).includes(e($))||o(P,[...e(P),e($)]);else{if(_.tab=n,y!==void 0&&n!==null){const v=Array.from(e(p).entries()).filter(([H,R])=>R.tab===n).sort((H,R)=>H[1].displayOrder-R[1].displayOrder).filter(([H])=>H!==e($));v.splice(y,0,[e($),_]),v.forEach(([H,R],ee)=>{R.displayOrder=ee,e(p).set(H,R)})}else if(n!==null){const w=Math.max(0,...Array.from(e(p).values()).filter(v=>v.tab===n).map(v=>v.displayOrder));_.displayOrder=w+1}e(p).set(e($),_),o(p,e(p))}}o($,null),o(B,null)}function ze(l,n){l.preventDefault(),l.stopPropagation(),o(ae,n)}function Ce(){o(ae,null)}function Se(l,n,y){if(l.preventDefault(),l.stopPropagation(),e($)&&e($)!==n){const w=Array.from(e(p).entries()).filter(([v,H])=>H.tab===y).sort((v,H)=>v[1].displayOrder-H[1].displayOrder).findIndex(([v])=>v===n);w!==-1&&ne(l,y,w)}o(ae,null)}async function Le(){if(!(!e(X)||!e(O))){if(!e(V)){o(Q,"Please select a Control ID field before importing"),o(ue,"");return}if(!e(fe)||e(fe).trim()===""){o(Q,"Please enter a Control Set Name before importing"),o(ue,"");return}o(G,!0),o(Q,""),o(ue,"");try{const l=new FormData;l.append("file",e(X),e(O)),l.append("controlIdField",e(V)),l.append("startRow",e(U).toString()),l.append("namingConvention","kebab-case"),l.append("skipEmpty","true"),l.append("skipEmptyRows","true"),l.append("controlSetName",e(fe)||e(O).replace(/\.[^.]+$/,"")),l.append("controlSetDescription",e(J)||`Imported from ${e(O)}`);const n=Array.from(e(p).entries()).filter(([w,v])=>v.tab!==null).map(([w,v])=>({fieldName:ve(w),...v}));l.append("fieldSchema",JSON.stringify(n)),l.append("justificationFields",JSON.stringify(e(P).map(w=>ve(w))));const y=await fetch("/api/import-spreadsheet",{method:"POST",body:l});if(!y.ok){const w=await y.json();throw new Error(w.error||"Import failed")}const _=await y.json();o(ue,`Successfully imported ${_.controlCount} controls into ${_.families.length} families`),z("created",{path:_.outputDir})}catch(l){o(Q,"Error importing spreadsheet: "+l.message)}finally{o(G,!1)}}}Je();var Ne=Vt(),me=a(Ne),Be=a(me),Ae=a(Be),ar=a(Ae);gt(ar,{class:"w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"}),_e(4),t(Ae);var Xe=i(Ae,2);t(Be),t(me);var Fe=i(me,2);{var or=l=>{var n=bt(),y=a(n),_=i(a(y),2),w=i(a(_)),v=i(w),H=i(a(v)),R=i(H,2),ee=i(R,2);t(v),t(_),t(y),t(n),F(()=>{E(w,` ${e(O)??""} `),E(H,` ${e(N).length??""} | `),E(R,` ${e(f).length??""} | `),E(ee,` ${e(K)??""}`)}),d(l,n)};M(Fe,l=>{e(O)&&l(or)})}var Ke=i(Fe,2);{var lr=l=>{var n=ft(),y=a(n),_=i(a(y),2),w=a(_,!0);t(_),t(y),t(n),F(()=>E(w,e(Q))),d(l,n)};M(Ke,l=>{e(Q)&&l(lr)})}var Ze=i(Ke,2);{var sr=l=>{var n=xt(),y=a(n),_=i(a(y),2),w=a(_,!0);t(_),t(y),t(n),F(()=>E(w,e(ue))),d(l,n)};M(Ze,l=>{e(ue)&&l(sr)})}var ir=i(Ze,2);{var nr=l=>{var n=Tt(),y=ye(n),_=i(a(y),2),w=a(_),v=i(a(w),2);Vr(v),_e(2),t(w);var H=i(w,2),R=i(a(H),2);Vr(R),_e(2),t(H),t(_);var ee=i(_,2),dr=a(ee),Ge=i(a(dr),2);F(()=>{e(L),mr(()=>{e(N)})}),ce(Ge,5,()=>e(N),r=>r,(r,s)=>{var x=mt(),m=a(x,!0);t(x);var c={};F(()=>{E(m,e(s)),c!==(c=e(s))&&(x.value=(x.__value=e(s))??"")}),d(r,x)}),t(Ge),_e(2),t(dr);var vr=i(dr,2),Qe=i(a(vr),2);F(()=>{e(U),mr(()=>{e(re)})}),ce(Qe,5,()=>e(re),r=>r.row,(r,s)=>{var x=ht(),m=a(x);t(x);var c={};F(()=>{E(m,`Row ${e(s).row??""}: ${e(s).preview??""}`),c!==(c=e(s).row)&&(x.value=(x.__value=e(s).row)??"")}),d(r,x)}),t(Qe),_e(2),t(vr);var Dr=i(vr,2),er=i(a(Dr),2);F(()=>{e(V),mr(()=>{e(f),e(S)})});var cr=a(er);cr.value=cr.__value="";var Br=i(cr);ce(Br,1,()=>e(f),r=>r,(r,s)=>{const x=De(()=>e(S).length>0&&e(S)[0][e(s)]?String(e(S)[0][e(s)]).slice(0,30):""),m=De(()=>!e(S).length||e(S).every(le=>!le[e(s)]||String(le[e(s)]).length<25)),c=De(()=>e(S).map(le=>le[e(s)]).filter(le=>le!=null&&le!==""&&String(le).trim()!=="")),Y=De(()=>new Set(e(c))),W=De(()=>!e(c).length||e(Y).size===e(c).length),j=De(()=>e(c).length>0);var A=Me(),be=ye(A);{var $e=le=>{var he=yt(),xr=a(he);t(he);var Tr={};F(()=>{E(xr,`${e(s)??""}${e(x)?` (e.g., ${e(x)})`:""}`),Tr!==(Tr=e(s))&&(he.value=(he.__value=e(s))??"")}),d(le,he)};M(be,le=>{e(m)&&e(W)&&e(j)&&le($e)})}d(r,A)}),t(er),_e(2),t(Dr),t(ee),t(y);var gr=i(y,2),Mr=i(a(gr),4),pr=a(Mr),Oe=i(a(pr),2),zr=a(Oe);ce(zr,1,()=>e(f).filter(r=>!e(p).get(r)||e(p).get(r)?.tab===null),r=>r,(r,s)=>{var x=wt(),m=i(a(x),2),c=a(m,!0);t(m);var Y=i(m,2);{var W=j=>{var A=_t();d(j,A)};M(Y,j=>{e(s)===e(V)&&j(W)})}t(x),F(()=>{qe(x,"aria-label",`Drag ${e(s)??""} field`),E(c,e(s))}),g("dragstart",x,j=>xe(j,e(s))),g("dragend",x,oe),d(r,x)});var Zr=i(zr,2);{var Rr=r=>{var s=kt();d(r,s)};M(Zr,r=>{e(f).filter(s=>!e(p).get(s)||e(p).get(s)?.tab===null).length===0&&r(Rr)})}t(Oe),t(pr);var ur=i(pr,2),Pe=i(a(ur),2),Ar=a(Pe);ce(Ar,3,()=>Array.from(e(p).entries()).filter(([r,s])=>s.tab==="overview").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var x=hr(()=>yr(e(s),2));let m=()=>e(x)[0];var c=Ct(),Y=a(c);kr(Y,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(Y,2),j=a(W,!0);t(W),t(c),F(()=>{qe(c,"aria-label",`${m()??""} field in Overview tab`),se(c,1,`flex items-center px-3 py-2 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 rounded text-sm cursor-move hover:bg-blue-200 dark:hover:bg-blue-800/30 transition-colors
|
|
3
|
-
${e(ae)===m()&&e($)!==m()?"border-t-2 border-blue-500":""}`),E(j,m())}),g("dragstart",c,A=>xe(A,m())),g("dragend",c,oe),g("dragover",c,A=>ze(A,m())),g("dragleave",c,Ce),g("drop",c,A=>Se(A,m(),"overview")),d(r,c)});var qr=i(Ar,2);{var Ur=r=>{var s=St();d(r,s)};M(qr,r=>{Array.from(e(p).entries()).filter(([s,x])=>x.tab==="overview").length===0&&r(Ur)})}t(Pe),t(ur);var br=i(ur,2),He=i(a(br),2),Fr=a(He);ce(Fr,3,()=>Array.from(e(p).entries()).filter(([r,s])=>s.tab==="implementation").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var x=hr(()=>yr(e(s),2));let m=()=>e(x)[0];var c=$t(),Y=a(c);kr(Y,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(Y,2),j=a(W,!0);t(W),t(c),F(()=>{qe(c,"aria-label",`${m()??""} field in Implementation tab`),se(c,1,`flex items-center px-3 py-2 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 rounded text-sm cursor-move hover:bg-green-200 dark:hover:bg-green-800/30 transition-colors
|
|
4
|
-
${e(ae)===m()&&e($)!==m()?"border-t-2 border-green-500":""}`),E(j,m())}),g("dragstart",c,A=>xe(A,m())),g("dragend",c,oe),g("dragover",c,A=>ze(A,m())),g("dragleave",c,Ce),g("drop",c,A=>Se(A,m(),"implementation")),d(r,c)});var Jr=i(Fr,2);{var Yr=r=>{var s=Dt();d(r,s)};M(Jr,r=>{Array.from(e(p).entries()).filter(([s,x])=>x.tab==="implementation").length===0&&r(Yr)})}t(He),t(br);var fr=i(br,2),Ee=i(a(fr),2),Or=a(Ee);ce(Or,3,()=>Array.from(e(p).entries()).filter(([r,s])=>s.tab==="custom").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var x=hr(()=>yr(e(s),2));let m=()=>e(x)[0];var c=Mt(),Y=a(c);kr(Y,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(Y,2),j=a(W,!0);t(W),t(c),F(()=>{qe(c,"aria-label",`${m()??""} field in Custom tab`),se(c,1,`flex items-center px-3 py-2 bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300 rounded text-sm cursor-move hover:bg-purple-200 dark:hover:bg-purple-800/30 transition-colors
|
|
5
|
-
${e(ae)===m()&&e($)!==m()?"border-t-2 border-purple-500":""}`),E(j,m())}),g("dragstart",c,A=>xe(A,m())),g("dragend",c,oe),g("dragover",c,A=>ze(A,m())),g("dragleave",c,Ce),g("drop",c,A=>Se(A,m(),"custom")),d(r,c)});var Wr=i(Or,2);{var Xr=r=>{var s=zt();d(r,s)};M(Wr,r=>{Array.from(e(p).entries()).filter(([s,x])=>x.tab==="custom").length===0&&r(Xr)})}t(Ee),t(fr);var Pr=i(fr,2),Ie=i(a(Pr),2),Hr=a(Ie),Kr=a(Hr);{var Gr=r=>{var s=Me(),x=ye(s);ce(x,1,()=>e(P),Cr,(m,c)=>{var Y=At(),W=a(Y),j=a(W,!0);t(W);var A=i(W,2);t(Y),F(()=>E(j,e(c))),g("click",A,dt(()=>{o(P,e(P).filter(be=>be!==e(c)))})),d(m,Y)}),d(r,s)},Qr=r=>{var s=Ft();F(()=>se(s,1,`p-4 transition-colors
|
|
6
|
-
${e(B)==="mappings"?"bg-orange-50 dark:bg-orange-900/10":""}`)),g("dragover",s,x=>{x.preventDefault(),C(x,"mappings")}),g("dragleave",s,ie),g("drop",s,x=>{if(x.preventDefault(),e($)&&e(p).has(e($))){e(P).includes(e($))||o(P,[...e(P),e($)]);const m=e(p).get(e($));m.tab="mappings",e(p).set(e($),m),o(p,new Map(e(p))),o(B,null)}}),d(r,s)};M(Kr,r=>{e(P).length>0?r(Gr):r(Qr,!1)})}t(Hr),t(Ie),t(Pr),t(Mr),t(gr);var Er=i(gr,2);{var et=r=>{var s=Et(),x=i(a(s),2),m=a(x),c=a(m),Y=a(c);ce(Y,5,()=>e(f).slice(0,5),j=>j,(j,A)=>{var be=Ot(),$e=a(be,!0);t(be),F(()=>E($e,e(A))),d(j,be)}),t(Y),t(c);var W=i(c);ce(W,5,()=>e(S),Cr,(j,A)=>{var be=Ht();ce(be,5,()=>e(f).slice(0,5),$e=>$e,($e,le)=>{var he=Pt(),xr=a(he,!0);t(he),F(()=>E(xr,e(A)[e(le)]||"")),d($e,he)}),t(be),d(j,be)}),t(W),t(m),t(x),t(s),d(r,s)};M(Er,r=>{e(S).length>0&&r(et)})}var Ir=i(Er,2),Re=a(Ir),rt=a(Re);{var tt=r=>{var s=It();d(r,s)},at=r=>{var s=rr("Import to Control Set");d(r,s)};M(rt,r=>{e(G)?r(tt):r(at,!1)})}t(Re),t(Ir),F(()=>{se(er,1,`bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white ${e(V)?"":"border-red-500"}`),se(Oe,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
7
|
-
${e(B)===null?"bg-gray-100 dark:bg-gray-800":""}`),se(Pe,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
8
|
-
${e(B)==="overview"?"bg-blue-50 dark:bg-blue-900/10":""}`),se(He,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
9
|
-
${e(B)==="implementation"?"bg-green-50 dark:bg-green-900/10":""}`),se(Ee,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
10
|
-
${e(B)==="custom"?"bg-purple-50 dark:bg-purple-900/10":""}`),se(Ie,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto transition-colors
|
|
11
|
-
${e(B)==="mappings"?"bg-orange-50 dark:bg-orange-900/10":""}`),Re.disabled=e(G)||!e(X)||!e(V),qe(Re,"title",e(V)?"":"Please select a Control ID field")}),jr(v,()=>e(fe),r=>o(fe,r)),jr(R,()=>e(J),r=>o(J,r)),_r(Ge,()=>e(L),r=>o(L,r)),g("change",Ge,()=>{Z()}),_r(Qe,()=>e(U),r=>o(U,r)),g("change",Qe,Z),_r(er,()=>e(V),r=>o(V,r)),g("dragover",Oe,r=>C(r,null)),g("dragleave",Oe,ie),g("drop",Oe,r=>ne(r,null)),g("dragover",Pe,r=>C(r,"overview")),g("dragleave",Pe,ie),g("drop",Pe,r=>ne(r,"overview")),g("dragover",He,r=>C(r,"implementation")),g("dragleave",He,ie),g("drop",He,r=>ne(r,"implementation")),g("dragover",Ee,r=>C(r,"custom")),g("dragleave",Ee,ie),g("drop",Ee,r=>ne(r,"custom")),g("dragover",Ie,r=>C(r,"mappings")),g("dragleave",Ie,ie),g("drop",Ie,r=>ne(r,"mappings")),g("click",Re,Le),d(l,n)};M(ir,l=>{e(we)&&e(f).length>0&&l(nr)})}t(Ne),F(()=>se(Be,1,`flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-lg cursor-pointer transition-all duration-300 ${e(te)?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600"}`)),g("change",Xe,T),g("dragover",me,u),g("dragleave",me,b),g("drop",me,D),d(pe,Ne),We()}var Lt=h('<span class="ml-2"> </span>'),Nt=h('<div class="text-sm text-gray-500 dark:text-gray-400"><span class="font-medium"> </span> <!></div>'),Bt=h('<div class="text-center space-y-6"><div class="p-6 bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 rounded-lg border-2 border-green-500"><svg class="w-12 h-12 text-green-600 mx-auto mb-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg> <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Already Using Control Set</h3> <p class="text-gray-600 dark:text-gray-400 mb-4">You are already using the only available control set in this project.</p> <!></div> <div class="flex justify-center gap-4"><a href="/" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors shadow-lg">Continue to Controls</a> <button class="px-6 py-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-white font-medium rounded-lg transition-colors">Import New Control Set</button></div></div>'),Zt=h('<div class="p-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),Rt=h('<p class="text-sm text-gray-600 dark:text-gray-400 mt-1"> </p>'),qt=h('<span class="text-xs text-gray-500 dark:text-gray-400"><svg class="inline w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"></path></svg> </span>'),Ut=h('<div class="flex flex-col items-center"><svg class="w-8 h-8 text-green-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg> <span class="text-xs text-green-600 font-medium mt-1">Current</span></div>'),Jt=Ve('<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>'),Yt=Ve('<svg class="w-8 h-8 text-gray-300 dark:text-gray-600" fill="none" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><circle cx="10" cy="10" r="9" stroke="currentColor" stroke-width="2"></circle></svg>'),Wt=h('<div role="button" tabindex="0"><div class="flex items-center justify-between"><div class="flex-1"><div class="flex items-center gap-3"><h3 class="text-lg font-semibold text-gray-900 dark:text-white"> </h3> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"> </span></div> <!> <div class="flex items-center gap-4 mt-2"><span class="text-xs text-gray-500 dark:text-gray-400"><svg class="inline w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h12a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zm2-1a1 1 0 00-1 1v8a1 1 0 001 1h12a1 1 0 001-1V6a1 1 0 00-1-1H4z" clip-rule="evenodd"></path></svg> </span> <!></div></div> <div class="flex items-center ml-4"><!></div></div></div>'),Xt=h('<div class="mt-6 flex justify-center"><button class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors shadow-lg">Use Selected Control Set</button></div>'),Kt=h('<div class="space-y-3"></div> <!>',1),Gt=h('<div class="space-y-6"><div class="text-center"><h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Select an Existing Control Set</h2> <p class="text-gray-600 dark:text-gray-400">Choose from control sets found in your project directory</p></div> <!></div>');function Qt(pe,q){Ye(q,!1);const z=()=>st(Sr,"$appState",X),[X,O]=it(),L=k(),N=Lr();let f=Ue(q,"controlSets",24,()=>[]),S=k(""),K=k(null);function re(J){p(J)||o(K,J)}function P(){e(K)&&!p(e(K))&&N("selected",{path:e(K).path})}function p(J){const G=e(L)?.replace(/\/$/,""),Q=J.path?.replace(/\/$/,"");return!!(G&&Q&&G===Q)}Te(()=>z(),()=>{o(L,z().currentPath||"")}),Te(()=>ge(f()),()=>{if(f())if(f().length===0)o(S,"No existing control sets found. Try importing from a spreadsheet instead.");else{const J=f().filter(G=>!p(G));J.length===1?o(K,J[0]):J.length===0&&f().length>0&&o(S,"already-using-only-set")}}),$r(),Je();var U=Gt(),V=i(a(U),2);{var fe=J=>{var G=Me(),Q=ye(G);{var ue=te=>{var $=Bt(),B=a($),ae=i(a(B),6);{var ke=D=>{var T=Nt(),I=a(T),Z=a(I,!0);t(I);var ve=i(I,2);{var je=xe=>{var oe=Lt(),C=a(oe);t(oe),F(()=>E(C,`(${ge(f()),de(()=>f()[0].controlCount)??""} controls)`)),d(xe,oe)};M(ve,xe=>{ge(f()),de(()=>f()[0].controlCount)&&xe(je)})}t(T),F(()=>E(Z,(ge(f()),de(()=>f()[0].name)))),d(D,T)};M(ae,D=>{ge(f()),de(()=>f().length>0)&&D(ke)})}t(B);var u=i(B,2),b=i(a(u),2);t(u),t($),g("click",b,()=>N("tab-change",{tab:"import"})),d(te,$)},we=te=>{var $=Me(),B=ye($);{var ae=u=>{var b=Zt(),D=a(b),T=i(a(D),2),I=a(T,!0);t(T),t(D),t(b),F(()=>E(I,e(S))),d(u,b)},ke=u=>{var b=Me(),D=ye(b);{var T=I=>{var Z=Kt(),ve=ye(Z);ce(ve,5,f,Cr,(oe,C)=>{const ie=De(()=>(e(C),de(()=>p(e(C)))));var ne=Wt(),ze=a(ne),Ce=a(ze),Se=a(Ce),Le=a(Se),Ne=a(Le,!0);t(Le);var me=i(Le,2),Be=a(me);t(me),t(Se);var Ae=i(Se,2);{var ar=l=>{var n=Rt(),y=a(n,!0);t(n),F(()=>E(y,(e(C),de(()=>e(C).description)))),d(l,n)};M(Ae,l=>{e(C),de(()=>e(C).description)&&l(ar)})}var Xe=i(Ae,2),Fe=a(Xe),or=i(a(Fe));t(Fe);var Ke=i(Fe,2);{var lr=l=>{var n=qt(),y=i(a(n));t(n),F(()=>E(y,` ${e(C),de(()=>e(C).file)??""}`)),d(l,n)};M(Ke,l=>{e(C),de(()=>e(C).file)&&l(lr)})}t(Xe),t(Ce);var Ze=i(Ce,2),sr=a(Ze);{var ir=l=>{var n=Ut();d(l,n)},nr=l=>{var n=Me(),y=ye(n);{var _=v=>{var H=Jt();d(v,H)},w=v=>{var H=Yt();d(v,H)};M(y,v=>{e(K)===e(C)?v(_):v(w,!1)},!0)}d(l,n)};M(sr,l=>{e(ie)?l(ir):l(nr,!1)})}t(Ze),t(ze),t(ne),F(()=>{se(ne,1,`p-4 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg border-2 transition-all duration-200 ${e(ie)?"border-green-500 !bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 cursor-not-allowed opacity-75":e(K)===e(C)?"border-blue-500 !bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 shadow-lg cursor-pointer":"border-gray-200 dark:border-gray-700 hover:border-blue-300 dark:hover:border-blue-700 hover:shadow-md cursor-pointer"}`),E(Ne,(e(C),de(()=>e(C).name))),E(Be,`${e(C),de(()=>e(C).controlCount||0)??""} controls`),E(or,` ${e(C),de(()=>e(C).path||"root")??""}`)}),g("click",ne,()=>!e(ie)&&re(e(C))),g("keydown",ne,l=>l.key==="Enter"&&!e(ie)&&re(e(C))),d(oe,ne)}),t(ve);var je=i(ve,2);{var xe=oe=>{var C=Xt(),ie=a(C);t(C),g("click",ie,P),d(oe,C)};M(je,oe=>{e(K)&&oe(xe)})}d(I,Z)};M(D,I=>{ge(f()),de(()=>f().length>0)&&I(T)},!0)}d(u,b)};M(B,u=>{e(S)?u(ae):u(ke,!1)},!0)}d(te,$)};M(Q,te=>{e(S)==="already-using-only-set"?te(ue):te(we,!1)},!0)}d(J,G)};M(V,J=>{J(fe,!1)})}t(U),d(pe,U),We(),O()}var ea=h('<div class="flex justify-center mb-8"><div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 inline-flex"><button data-testid="tab-existing">Select Existing Control Set</button> <button data-testid="tab-import">Import New from Spreadsheet</button></div></div>'),ra=h('<div data-testid="pane-import"><!></div>'),ta=h('<div data-testid="pane-existing"><!></div>'),aa=h('<div data-testid="switch-overlay" class="absolute inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center rounded-lg"><div class="bg-white dark:bg-gray-700 rounded-lg p-6 text-center"><svg class="animate-spin h-8 w-8 text-blue-600 mx-auto mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> <p class="text-gray-900 dark:text-white">Switching control set...</p></div></div>'),oa=h('<div class=" p-4"><div class="max-w-6xl mx-auto"><div class="text-center py-8"><h1 class="text-4xl font-extrabold text-gray-900 dark:text-white mb-4 flex items-center justify-center gap-3"><img src="/lula.png" class="h-12 w-12" alt="Lula"/> <span id="title">Lula</span></h1> <p id="description" class="text-lg text-gray-600 dark:text-gray-400"><!></p></div> <!> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg shadow-xl p-6 relative border border-gray-200 dark:border-gray-700"><!> <!></div></div></div>');function ca(pe,q){Ye(q,!1);let z=k("import"),X=k(""),O=k(!1),L=k(!1),N=k([]);ot(()=>{wr.connect();const u=Sr.subscribe(D=>{D.name&&D.name!=="Unknown Control Set"&&D.id!=="unknown"&&D.id!=="default"&&o(X,D.currentPath||"")}),b=async D=>{const T=D.detail;if(T&&T.controlSets){if(o(N,T.controlSets||[]),o(O,e(N).length>0),e(N).length===1){const I=e(N)[0];if(!(e(X)&&e(X).includes(I.path))&&!e(X)){console.log("Only one control set found and none loaded, auto-loading:",I.path),await f(I.path);return}}e(O)&&o(z,"existing")}};return window.addEventListener("control-sets-list",b),wr.scanControlSets().catch(D=>{console.error("Error scanning control sets:",D)}),()=>{u(),window.removeEventListener("control-sets-list",b)}});async function f(u){console.log("Starting control set switch to:",u),o(L,!0);try{await wr.switchControlSet(u),console.log("WebSocket command sent, waiting for state update..."),await new Promise(b=>{let D=0;const T=50,I=setInterval(()=>{D++;const Z=lt(Sr);!Z.isSwitchingControlSet&&Z.currentPath&&Z.currentPath.includes(u)?(clearInterval(I),console.log("Control set switch completed successfully"),b()):D>=T&&(clearInterval(I),console.error("Control set switch timed out"),alert("Control set switch timed out. Please try again."),b())},100)}),o(L,!1),nt("/")}catch(b){console.error("Error switching control set:",b),alert("Failed to switch control set: "+b.message),o(L,!1)}}async function S(u){const{path:b}=u.detail;await f(b)}async function K(u){const{path:b}=u.detail;await f(b)}function re(u){const{tab:b}=u.detail;b&&o(z,b)}Je();var P=oa(),p=a(P),U=a(p),V=i(a(U),2),fe=a(V);{var J=u=>{var b=rr("You have an existing control set. You can continue using it or create a new one.");d(u,b)},G=u=>{var b=Me(),D=ye(b);{var T=Z=>{var ve=rr("Select an existing control set or import a new one from a spreadsheet.");d(Z,ve)},I=Z=>{var ve=rr("Let's get started by importing a control set from a spreadsheet.");d(Z,ve)};M(D,Z=>{e(O)?Z(T):Z(I,!1)},!0)}d(u,b)};M(fe,u=>{e(X)?u(J):u(G,!1)})}t(V),t(U);var Q=i(U,2);{var ue=u=>{var b=ea(),D=a(b),T=a(D),I=i(T,2);t(D),t(b),F(()=>{se(T,1,`px-6 py-3 rounded-l-lg font-medium transition-colors ${e(z)==="existing"?"bg-blue-600 text-white":"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"}`),se(I,1,`px-6 py-3 rounded-r-lg font-medium transition-colors ${e(z)==="import"?"bg-blue-600 text-white":"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"}`)}),g("click",T,()=>o(z,"existing")),g("click",I,()=>o(z,"import")),d(u,b)};M(Q,u=>{e(O)&&u(ue)})}var we=i(Q,2),te=a(we);{var $=u=>{var b=ra(),D=a(b);jt(D,{$$events:{created:S}}),t(b),d(u,b)},B=u=>{var b=ta(),D=a(b);Qt(D,{get controlSets(){return e(N)},$$events:{selected:K,"tab-change":re}}),t(b),d(u,b)};M(te,u=>{e(z)==="import"?u($):u(B,!1)})}var ae=i(te,2);{var ke=u=>{var b=aa();d(u,b)};M(ae,u=>{e(L)&&u(ke)})}t(we),t(p),t(P),d(pe,P),We()}export{ca as component};
|