mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
35f07c508d
- Appointment date picker disables days before today. - Invoice issue-date picker disables past days by default, with an opt-in "Back-date" checkbox for recording older invoices (edit mode keeps past dates). Added invoices.dialog.backdate to all locales. - Removed the inert, outdated "Features" section (patient-owned storage / require signed toggles) from Settings and its now-unused i18n keys. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
111 lines
2.8 KiB
JavaScript
111 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Get migration guides for HeroUI components (v2 to v3).
|
|
*
|
|
* Usage:
|
|
* node get_component_migration_guides.mjs button
|
|
* node get_component_migration_guides.mjs button card modal
|
|
*
|
|
* Output:
|
|
* MDX migration guide content for each component
|
|
*/
|
|
|
|
const DOCS_BASE =
|
|
process.env.HEROUI_MIGRATION_DOCS_BASE ||
|
|
"https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration";
|
|
const APP_PARAM = "app=migration-skills";
|
|
|
|
/**
|
|
* Convert PascalCase or mixed case to kebab-case.
|
|
*/
|
|
function toKebabCase(name) {
|
|
return name
|
|
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
.replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
|
|
.toLowerCase()
|
|
.trim();
|
|
}
|
|
|
|
async function fetchDoc(filename) {
|
|
const url = `${DOCS_BASE}/${filename}?${APP_PARAM}`;
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
headers: {"User-Agent": "HeroUI-Migration-Skill/1.0"},
|
|
signal: AbortSignal.timeout(30000),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
return await response.text();
|
|
} catch (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.length === 0) {
|
|
console.error("Usage: node get_component_migration_guides.mjs <Component1> [Component2] ...");
|
|
console.error("Example: node get_component_migration_guides.mjs button card modal");
|
|
console.error("\nUse list_migration_guides.mjs to see all available components.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const components = args.map((c) => toKebabCase(c));
|
|
|
|
console.error(`# Fetching migration guides for: ${components.join(", ")}...`);
|
|
|
|
const results = [];
|
|
|
|
for (const component of components) {
|
|
const filename = `${component}.mdx`;
|
|
|
|
try {
|
|
const content = await fetchDoc(filename);
|
|
const title = component.charAt(0).toUpperCase() + component.slice(1).replace(/-/g, " ");
|
|
|
|
results.push({
|
|
component,
|
|
content: `# ${title} Migration Guide\n\n**Component:** ${component}\n**Source:** ${DOCS_BASE}/${filename}\n\n---\n\n${content}`,
|
|
});
|
|
} catch (error) {
|
|
results.push({
|
|
component,
|
|
error: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
const failed = results.filter((r) => r.error);
|
|
|
|
if (failed.length > 0) {
|
|
failed.forEach((r) => console.error(`# Error for ${r.component}: ${r.error}`));
|
|
}
|
|
|
|
if (results.length === 1) {
|
|
const r = results[0];
|
|
|
|
if (r.content) {
|
|
console.log(r.content);
|
|
} else {
|
|
console.log(JSON.stringify(r, null, 2));
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
const output = results
|
|
.map((r) => (r.content ? r.content : `# ${r.component} Migration Guide\n\nError: ${r.error}`))
|
|
.join("\n\n---\n\n");
|
|
|
|
console.log(output);
|
|
if (failed.length === results.length) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
main();
|