@tera-system/core 0.1.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/LICENSE.md +13 -0
- package/MANIFEST.json +24 -0
- package/README.md +67 -0
- package/RELEASES.md +28 -0
- package/WELCOME.md +35 -0
- package/agents/application-blueprint.md +407 -0
- package/agents/auditor.md +665 -0
- package/agents/design-reviewer.md +392 -0
- package/agents/domain-expert-agent.md +510 -0
- package/agents/domain-research-agent.md +360 -0
- package/agents/engineering-agent-dotnet.md +218 -0
- package/agents/engineering-agent-phaser.md +275 -0
- package/agents/engineering-agent-typescript.md +300 -0
- package/agents/engineering-agent.md +143 -0
- package/agents/monitor.md +395 -0
- package/agents/production-erp-expert.md +506 -0
- package/agents/project-knowledge-agent.md +271 -0
- package/agents/qa-agent.md +498 -0
- package/agents/tera-business-transformation-consultant.md +290 -0
- package/agents/tera-client-engagement.md +891 -0
- package/agents/tera-software-designer.md +237 -0
- package/agents/tera-strategic-advisor.md +325 -0
- package/agents/tera-system-evolution.md +759 -0
- package/agents/tera.md +520 -0
- package/agents/ui-designer.md +426 -0
- package/commands/tera-approve.md +34 -0
- package/commands/tera-diagnose.md +47 -0
- package/commands/tera-gate.md +40 -0
- package/commands/tera-help.md +41 -0
- package/commands/tera-new-project.md +35 -0
- package/commands/tera-plan.md +34 -0
- package/commands/tera-request-build.md +45 -0
- package/commands/tera-resume.md +34 -0
- package/commands/tera-review.md +52 -0
- package/commands/tera-status.md +38 -0
- package/commands/tera-update.md +48 -0
- package/core/project-control/templates/TASK_TEMPLATE.md +146 -0
- package/core/tera-system/AGENT_ACTIVATION_MATRIX.md +285 -0
- package/core/tera-system/AGENT_DEPENDENCY_MAP.md +117 -0
- package/core/tera-system/AGENT_GENERATION_TEMPLATE.md +312 -0
- package/core/tera-system/AGENT_PERMISSION_MODEL.md +343 -0
- package/core/tera-system/AIS_PROTOCOL.md +191 -0
- package/core/tera-system/TERA_AGENT_CONDUCT.md +102 -0
- package/core/tera-system/TERA_CONTINUOUS_IMPROVEMENT_POLICY.md +111 -0
- package/core/tera-system/TERA_DISTRIBUTION_POLICY.md +291 -0
- package/core/tera-system/TERA_PROJECT_DECISION.md +281 -0
- package/core/tera-system/TERA_USER_GUIDE.md +462 -0
- package/core/tera-system/TOOLING_AND_MCP_POLICY.md +285 -0
- package/core/tera-system/TeraApplicationQuestionBank.md +362 -0
- package/core/tera-system/TeraArchitectureMap.md +91 -0
- package/core/tera-system/TeraClientPolicy.md +366 -0
- package/core/tera-system/TeraHelperAgents.md +970 -0
- package/core/tera-system/TeraPolicyMap.md +131 -0
- package/core/tera-system/TeraPreExecutionGate.md +818 -0
- package/core/tera-system/TeraPreparationDocumentationGovernance.md +370 -0
- package/core/tera-system/TeraPricingPolicy.md +674 -0
- package/core/tera-system/TeraProjectIntakePolicy.md +164 -0
- package/core/tera-system/TeraScenarioStressTests.md +168 -0
- package/core/tera-system/TeraSubAgents.md +854 -0
- package/core/tera-system/TeraSystemMaintenanceChecklist.md +80 -0
- package/core/tera-system/TeraTokenPolicy.md +362 -0
- package/core/tera-system/Tera_Project_Preparation_Files.md +1045 -0
- package/core/tera-system/agent-helpers/application-blueprint-details.md +177 -0
- package/core/tera-system/client-helpers/tera-client-engagement-discovery-domains.md +99 -0
- package/core/tera-system/client-helpers/tera-client-engagement-gates.md +258 -0
- package/core/tera-system/client-helpers/tera-client-engagement-pricing.md +341 -0
- package/core/tera-system/client-helpers/tera-client-engagement-protocols.md +692 -0
- package/core/tera-system/consulting-helpers/BTCA_METHODOLOGY_FRAMEWORK.md +195 -0
- package/core/tera-system/consulting-helpers/BTCA_REPORT_TEMPLATES.md +266 -0
- package/core/tera-system/design-system/ACCESSIBILITY_RULES.md +31 -0
- package/core/tera-system/design-system/COMPONENT_LIBRARY_SCHEMA.md +46 -0
- package/core/tera-system/design-system/DESIGN_MD_INTEGRATION.md +59 -0
- package/core/tera-system/design-system/DESIGN_REVIEW_STANDARDS.md +241 -0
- package/core/tera-system/design-system/DESIGN_SOURCE_PROTOCOL.md +61 -0
- package/core/tera-system/design-system/DESIGN_SYSTEM_OVERVIEW.md +66 -0
- package/core/tera-system/design-system/DESIGN_TOKENS_SCHEMA.md +66 -0
- package/core/tera-system/design-system/EXTERNAL_REFERENCE_ANALYSIS.md +52 -0
- package/core/tera-system/design-system/FIGMA_INTEGRATION.md +138 -0
- package/core/tera-system/design-system/INTERNAL_KITS_INDEX.md +26 -0
- package/core/tera-system/design-system/LAYOUT_PATTERNS.md +52 -0
- package/core/tera-system/design-system/MOBILE_UI_UX_STANDARDS.md +342 -0
- package/core/tera-system/design-system/RTL_LTR_RULES.md +39 -0
- package/core/tera-system/design-system/UI_ACCEPTANCE_GATE.md +80 -0
- package/core/tera-system/design-system/kits/KIT_ADMIN_DASHBOARD.md +102 -0
- package/core/tera-system/engineering-governance/ENGINEERING_AGENT_RESPONSIBILITIES.md +210 -0
- package/core/tera-system/engineering-governance/ENGINEERING_BEST_PRACTICES.md +468 -0
- package/core/tera-system/engineering-governance/ENGINEERING_GOVERNANCE_GATE.md +131 -0
- package/core/tera-system/engineering-governance/ENGINEERING_REVIEW_CHECKLIST.md +129 -0
- package/core/tera-system/engineering-governance/QUALITY_GATE_THRESHOLDS.md +159 -0
- package/core/tera-system/engineering-helpers/engineering-agent-core.md +171 -0
- package/core/tera-system/knowledge-base/OPENHANDS_ARCHITECTURE_REFERENCE.md +243 -0
- package/core/tera-system/knowledge-base/manufacturing/00_INDEX.md +32 -0
- package/core/tera-system/knowledge-base/manufacturing/01_MANUFACTURING_ERP_CORE_CONCEPTS.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/02_SAP_MANUFACTURING_RESEARCH.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/03_DYNAMICS_365_MANUFACTURING_RESEARCH.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/04_ORACLE_MANUFACTURING_RESEARCH.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/05_ODOO_MANUFACTURING_RESEARCH.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/06_ERPNEXT_MANUFACTURING_RESEARCH.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/07_MANUFACTURING_COSTING_GUIDE.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/08_PRODUCTION_DISCOVERY_QUESTIONS.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/09_MANUFACTURING_BLUEPRINT_CHECKLIST.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/10_PRODUCTION_TEST_SCENARIOS.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/11_QUALITY_REWORK_AND_SCRAP_GUIDE.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/12_MRP_AND_PLANNING_GUIDE.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/13_LOCAL_AND_REGIONAL_MANUFACTURING_CONTEXT.md +7 -0
- package/core/tera-system/knowledge-base/manufacturing/14_VENDOR_COMPARISON_MATRIX.md +7 -0
- package/core/tera-system/product-standards/maintenance-apps/BEST_PRACTICES_DOMAIN.md +325 -0
- package/core/tera-system/product-standards/maintenance-apps/STANDARD_DEFINITION.md +142 -0
- package/core/tera-system/profiles/PROFILES_INDEX.md +57 -0
- package/core/tera-system/profiles/TEMPLATE.md +47 -0
- package/core/tera-system/profiles/dotnet-blazor-ef.md +76 -0
- package/core/tera-system/profiles/dotnet-razorpages-adonet.md +137 -0
- package/core/tera-system/profiles/dotnet-wpf-sqlite.md +159 -0
- package/core/tera-system/profiles/effect-bun-opencode.md +109 -0
- package/core/tera-system/profiles/flutter-mobile.md +369 -0
- package/core/tera-system/profiles/nextjs-prisma.md +110 -0
- package/core/tera-system/profiles/phaser-react-node.md +302 -0
- package/core/tera-system/profiles/react-pwa.md +97 -0
- package/core/tera-system/runtime/CLIENT_DISCOVERY_PROTOCOL.md +145 -0
- package/core/tera-system/runtime/DOMAIN_INTELLIGENCE_PROTOCOL.md +124 -0
- package/core/tera-system/runtime/MVP_DEFINITION_PROTOCOL.md +176 -0
- package/core/tera-system/runtime/TERA_RUNTIME_CHECKLISTS.md +646 -0
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS.md +50 -0
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS_CLIENT.md +355 -0
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS_CORE.md +799 -0
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES.md +908 -0
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES_DELIVERY.md +584 -0
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES_PREPARATION.md +376 -0
- package/core/tera-system/runtime/TERA_SOLUTION_PREPARATION_PROTOCOL.md +335 -0
- package/core/tera-system/runtime/TERA_SOLUTION_PREPARATION_TEMPLATES.md +397 -0
- package/core/tera-system/runtime/VERSION_LIFECYCLE_PROTOCOL.md +296 -0
- package/core/tera-system/semgrep-rules/README.md +32 -0
- package/core/tera-system/semgrep-rules/tera-security.yml +66 -0
- package/core/tera-system/semgrep-rules/tera-standards.yml +49 -0
- package/core/tera-system/teranoo-ui/README.md +48 -0
- package/core/tera-system/teranoo-ui/components/button.tsx +51 -0
- package/core/tera-system/teranoo-ui/components/card.tsx +49 -0
- package/core/tera-system/teranoo-ui/components/dashboard-layout.tsx +36 -0
- package/core/tera-system/teranoo-ui/components/data-table.tsx +146 -0
- package/core/tera-system/teranoo-ui/components/empty-state.tsx +31 -0
- package/core/tera-system/teranoo-ui/components/kpi-card.tsx +42 -0
- package/core/tera-system/teranoo-ui/components/page-header.tsx +25 -0
- package/core/tera-system/teranoo-ui/components/search-input.tsx +40 -0
- package/core/tera-system/teranoo-ui/components/sidebar.tsx +66 -0
- package/core/tera-system/teranoo-ui/components/stats-card.tsx +37 -0
- package/core/tera-system/teranoo-ui/registry.json +77 -0
- package/core/tera-system/teranoo-ui/styles/teranoo-theme.css +61 -0
- package/opencode.tera.example.json +30 -0
- package/package.json +37 -0
- package/scripts/build.mjs +110 -0
- package/scripts/install.js +244 -0
- package/scripts/lib/license.mjs +90 -0
- package/scripts/lib/public-key.pem +3 -0
- package/scripts/tera-license.mjs +63 -0
- package/tools/tera-clean.ps1 +97 -0
- package/tools/tera-fetch.ps1 +165 -0
- package/tools/tera-release.ps1 +96 -0
- package/tools/tera-schedule.ps1 +59 -0
- package/tools/tera-update.ps1 +472 -0
- package/tools/tera-watch.ps1 +154 -0
- package/tools/update-client-repositories.ps1 +92 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Semgrep Rules — Tera System
|
|
2
|
+
|
|
3
|
+
قواعد Semgrep الخاصة بمنظومة Tera لجودة وأمان الكود.
|
|
4
|
+
|
|
5
|
+
## كيفية الاستخدام
|
|
6
|
+
|
|
7
|
+
### كـ MCP (للاستخدام من العملاء)
|
|
8
|
+
```bash
|
|
9
|
+
# MCP Server مضبوط مسبقاً — العملاء يستدعون semgrep_scan مباشرة
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### كـ CLI (للمراجعة اليدوية)
|
|
13
|
+
```bash
|
|
14
|
+
# فحص المشروع كاملاً
|
|
15
|
+
semgrep --config=tera-system/semgrep-rules/ --metrics=off .
|
|
16
|
+
|
|
17
|
+
# فحص ملفات محددة
|
|
18
|
+
semgrep --config=tera-system/semgrep-rules/ --metrics=off path/to/file
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## الملفات
|
|
22
|
+
|
|
23
|
+
| الملف | الغرض | Severity |
|
|
24
|
+
|-------|-------|----------|
|
|
25
|
+
| `tera-security.yml` | قواعد أمنية — Secrets, Injection, Eval | ERROR |
|
|
26
|
+
| `tera-standards.yml` | قواعد جودة — Console.log, TODOs, حجم الدوال | WARNING / INFO |
|
|
27
|
+
|
|
28
|
+
## تطوير القواعد
|
|
29
|
+
|
|
30
|
+
- القواعد مبدئية — تُوسع حسب الاحتياج
|
|
31
|
+
- أي إضافة/تعديل → يحتاج SYSTEM_CHANGE_PROPOSAL
|
|
32
|
+
- يُفضل اختبار القاعدة على Semgrep Playground قبل الإضافة
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: tera-no-hardcoded-secrets
|
|
3
|
+
message: >
|
|
4
|
+
SECURITY [QG-SEC-001]: لا تخزن مفاتيح أو ConnectionStrings أو Secrets في الكود.
|
|
5
|
+
استخدم User Secrets / Environment Variables / Azure Key Vault / أي آلية آمنة معتمدة.
|
|
6
|
+
severity: ERROR
|
|
7
|
+
languages:
|
|
8
|
+
- csharp
|
|
9
|
+
- javascript
|
|
10
|
+
- typescript
|
|
11
|
+
- python
|
|
12
|
+
patterns:
|
|
13
|
+
- pattern: |
|
|
14
|
+
$VAR = "..."
|
|
15
|
+
- metavariable-regex:
|
|
16
|
+
metavariable: $VAR
|
|
17
|
+
regex: (?i).*(password|secret|connection.?string|api.?key|apikey|passwd|pwd|credential).*
|
|
18
|
+
|
|
19
|
+
- id: tera-no-eval
|
|
20
|
+
pattern: eval(...)
|
|
21
|
+
message: >
|
|
22
|
+
SECURITY [QG-SEC-004]: eval() ممنوع — يشكل خطر حقن كود.
|
|
23
|
+
استخدم بديلاً آمناً.
|
|
24
|
+
languages:
|
|
25
|
+
- javascript
|
|
26
|
+
- typescript
|
|
27
|
+
- python
|
|
28
|
+
severity: ERROR
|
|
29
|
+
|
|
30
|
+
- id: tera-sql-concatenation
|
|
31
|
+
message: >
|
|
32
|
+
SECURITY [QG-SEC-003]: خطر SQL Injection — لا تركب استعلامات SQL يدوياً.
|
|
33
|
+
استخدم Parameterized Queries / Entity Framework / Dapper مع parameters.
|
|
34
|
+
severity: ERROR
|
|
35
|
+
languages:
|
|
36
|
+
- csharp
|
|
37
|
+
- javascript
|
|
38
|
+
- typescript
|
|
39
|
+
- python
|
|
40
|
+
- java
|
|
41
|
+
patterns:
|
|
42
|
+
- pattern: '"..." + $VAR'
|
|
43
|
+
- metavariable-regex:
|
|
44
|
+
metavariable: $VAR
|
|
45
|
+
regex: (?i).*
|
|
46
|
+
|
|
47
|
+
- id: tera-exposed-credentials
|
|
48
|
+
message: >
|
|
49
|
+
SECURITY [QG-SEC-001]: لا تخزن مفاتيح أو توكنز أو API Keys بشكل صريح في الكود.
|
|
50
|
+
severity: ERROR
|
|
51
|
+
languages:
|
|
52
|
+
- csharp
|
|
53
|
+
- javascript
|
|
54
|
+
- typescript
|
|
55
|
+
- python
|
|
56
|
+
- java
|
|
57
|
+
patterns:
|
|
58
|
+
- pattern-either:
|
|
59
|
+
- pattern: |
|
|
60
|
+
$VAR = "=...="
|
|
61
|
+
- pattern: |
|
|
62
|
+
$VAR = "sk-..."
|
|
63
|
+
- pattern: |
|
|
64
|
+
$VAR = "ghp_..."
|
|
65
|
+
- pattern: |
|
|
66
|
+
$VAR = "AKIA..."
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: tera-console-log
|
|
3
|
+
message: >
|
|
4
|
+
QUALITY: لا تترك console.log / print / Console.WriteLine في كود الإنتاج.
|
|
5
|
+
استخدم مكتبة logging رسمية (Serilog, Winston, structlog, loguru).
|
|
6
|
+
severity: WARNING
|
|
7
|
+
languages:
|
|
8
|
+
- csharp
|
|
9
|
+
- javascript
|
|
10
|
+
- typescript
|
|
11
|
+
- python
|
|
12
|
+
patterns:
|
|
13
|
+
- pattern-either:
|
|
14
|
+
- pattern: console.log(...)
|
|
15
|
+
- pattern: console.error(...)
|
|
16
|
+
- pattern: console.warn(...)
|
|
17
|
+
- pattern: Console.WriteLine(...)
|
|
18
|
+
- pattern: Console.Write(...)
|
|
19
|
+
- pattern: print(...)
|
|
20
|
+
|
|
21
|
+
- id: tera-todo-left
|
|
22
|
+
pattern: TODO
|
|
23
|
+
message: >
|
|
24
|
+
QUALITY: في TODO / FIXME / HACK متروك في الكود.
|
|
25
|
+
إما أنجزه الآن، أو سجله كـ Task/Clearing Issue في TASK_REGISTRY.
|
|
26
|
+
severity: INFO
|
|
27
|
+
languages:
|
|
28
|
+
- csharp
|
|
29
|
+
- javascript
|
|
30
|
+
- typescript
|
|
31
|
+
- python
|
|
32
|
+
- java
|
|
33
|
+
- go
|
|
34
|
+
|
|
35
|
+
- id: tera-debug-statement
|
|
36
|
+
message: >
|
|
37
|
+
QUALITY: debugger / Debug statements متروكة في الكود.
|
|
38
|
+
لا يجوز تسليم كود إنتاج وفيه debugger breakpoints.
|
|
39
|
+
severity: ERROR
|
|
40
|
+
languages:
|
|
41
|
+
- csharp
|
|
42
|
+
- javascript
|
|
43
|
+
- typescript
|
|
44
|
+
patterns:
|
|
45
|
+
- pattern-either:
|
|
46
|
+
- pattern: debugger
|
|
47
|
+
- pattern: Debug.WriteLine(...)
|
|
48
|
+
- pattern: Debug.Print(...)
|
|
49
|
+
- pattern: Debugger.Break()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Teranoo UI Registry 🎨
|
|
2
|
+
|
|
3
|
+
المكونات المعتمدة لتطبيقات Tera — مبنية على [shadcn/ui](https://ui.shadcn.com).
|
|
4
|
+
|
|
5
|
+
## الاستخدام
|
|
6
|
+
|
|
7
|
+
### المتطلبات
|
|
8
|
+
- Next.js + TypeScript + Tailwind CSS
|
|
9
|
+
- تم تشغيل `npx shadcn@latest init` في المشروع
|
|
10
|
+
|
|
11
|
+
### إضافة مكونات shadcn/ui الأساسية
|
|
12
|
+
```bash
|
|
13
|
+
npx shadcn@latest add button card input label form select table dropdown-menu dialog tabs
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### إضافة مكونات Teranoo UI
|
|
17
|
+
```bash
|
|
18
|
+
npx shadcn@latest add button --registry=./tera-system/teranoo-ui
|
|
19
|
+
npx shadcn@latest add dashboard-layout --registry=./tera-system/teranoo-ui
|
|
20
|
+
npx shadcn@latest add kpi-card --registry=./tera-system/teranoo-ui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### تطبيق ثيم Tera
|
|
24
|
+
انسخ ملف `styles/teranoo-theme.css` إلى مجلد المشروع وأضف الـ import:
|
|
25
|
+
```css
|
|
26
|
+
@import './styles/teranoo-theme.css';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## قائمة المكونات
|
|
30
|
+
|
|
31
|
+
| المكون | الوصف | يستخدم shadcn/ui |
|
|
32
|
+
|--------|-------|-------------------|
|
|
33
|
+
| `button` | زر Tera الأساسي | ✅ shadcn button |
|
|
34
|
+
| `card` | بطاقة محتوى | ✅ shadcn card |
|
|
35
|
+
| `dashboard-layout` | هيكل لوحة تحكم كامل | ❌ (هيكل مركب) |
|
|
36
|
+
| `data-table` | جدول متقدم | ✅ shadcn table |
|
|
37
|
+
| `kpi-card` | بطاقة KPI | ✅ shadcn card |
|
|
38
|
+
| `sidebar` | قائمة جانبية | ❌ (مخصص) |
|
|
39
|
+
| `stats-card` | بطاقة إحصائية | ✅ shadcn card |
|
|
40
|
+
| `page-header` | عنوان صفحة | ❌ (مخصص) |
|
|
41
|
+
| `search-input` | حقل بحث | ✅ shadcn input |
|
|
42
|
+
| `empty-state` | حالة فراغ | ❌ (مخصص) |
|
|
43
|
+
|
|
44
|
+
## المبدأ
|
|
45
|
+
|
|
46
|
+
> استخدم مكونات shadcn/ui أولاً.
|
|
47
|
+
> استخدم Teranoo UI Registry للمكونات المخصصة المتكررة.
|
|
48
|
+
> اكتب من الصفر فقط لما لا يوجد في الاثنين.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-[var(--teranoo-radius)] text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--teranoo-ring)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-[var(--teranoo-primary)] text-[var(--teranoo-primary-foreground)] hover:bg-[var(--teranoo-primary-hover)]",
|
|
11
|
+
destructive: "bg-[var(--teranoo-danger)] text-white hover:bg-red-600",
|
|
12
|
+
outline: "border border-[var(--teranoo-border)] bg-[var(--teranoo-background)] hover:bg-[var(--teranoo-muted)]",
|
|
13
|
+
secondary: "bg-[var(--teranoo-secondary)] text-[var(--teranoo-secondary-foreground)] hover:bg-[var(--teranoo-secondary)]/80",
|
|
14
|
+
ghost: "hover:bg-[var(--teranoo-muted)]",
|
|
15
|
+
link: "text-[var(--teranoo-primary)] underline-offset-4 hover:underline",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-10 px-4 py-2",
|
|
19
|
+
sm: "h-9 rounded-md px-3",
|
|
20
|
+
lg: "h-11 rounded-md px-8",
|
|
21
|
+
icon: "h-10 w-10",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
size: "default",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
export interface ButtonProps
|
|
32
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
33
|
+
VariantProps<typeof buttonVariants> {
|
|
34
|
+
asChild?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
38
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
39
|
+
const Comp = asChild ? Slot : "button"
|
|
40
|
+
return (
|
|
41
|
+
<Comp
|
|
42
|
+
className={buttonVariants({ variant, size, className })}
|
|
43
|
+
ref={ref}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
Button.displayName = "Button"
|
|
50
|
+
|
|
51
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
4
|
+
({ className, ...props }, ref) => (
|
|
5
|
+
<div
|
|
6
|
+
ref={ref}
|
|
7
|
+
className="rounded-[var(--teranoo-radius)] border border-[var(--teranoo-border)] bg-[var(--teranoo-background)] shadow-[var(--teranoo-shadow)]"
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
Card.displayName = "Card"
|
|
13
|
+
|
|
14
|
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
15
|
+
({ className, ...props }, ref) => (
|
|
16
|
+
<div ref={ref} className="flex flex-col space-y-1.5 p-6" {...props} />
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
CardHeader.displayName = "CardHeader"
|
|
20
|
+
|
|
21
|
+
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
22
|
+
({ className, ...props }, ref) => (
|
|
23
|
+
<h3 ref={ref} className="text-2xl font-semibold leading-none tracking-tight" {...props} />
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
CardTitle.displayName = "CardTitle"
|
|
27
|
+
|
|
28
|
+
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
29
|
+
({ className, ...props }, ref) => (
|
|
30
|
+
<p ref={ref} className="text-sm text-[var(--teranoo-muted-foreground)]" {...props} />
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
CardDescription.displayName = "CardDescription"
|
|
34
|
+
|
|
35
|
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
36
|
+
({ className, ...props }, ref) => (
|
|
37
|
+
<div ref={ref} className="p-6 pt-0" {...props} />
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
CardContent.displayName = "CardContent"
|
|
41
|
+
|
|
42
|
+
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
43
|
+
({ className, ...props }, ref) => (
|
|
44
|
+
<div ref={ref} className="flex items-center p-6 pt-0" {...props} />
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
CardFooter.displayName = "CardFooter"
|
|
48
|
+
|
|
49
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Sidebar } from "./sidebar"
|
|
3
|
+
import type { SidebarItem } from "./sidebar"
|
|
4
|
+
|
|
5
|
+
interface DashboardLayoutProps {
|
|
6
|
+
sidebarItems: SidebarItem[]
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
header?: React.ReactNode
|
|
9
|
+
footer?: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function DashboardLayout({ sidebarItems, children, header, footer }: DashboardLayoutProps) {
|
|
13
|
+
const [collapsed, setCollapsed] = React.useState(false)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="flex h-screen overflow-hidden bg-[var(--teranoo-muted)]" dir="rtl">
|
|
17
|
+
<Sidebar
|
|
18
|
+
items={sidebarItems}
|
|
19
|
+
collapsed={collapsed}
|
|
20
|
+
onToggle={() => setCollapsed(!collapsed)}
|
|
21
|
+
footer={footer}
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<main className="flex-1 flex flex-col overflow-hidden">
|
|
25
|
+
{header && (
|
|
26
|
+
<header className="border-b border-[var(--teranoo-border)] bg-[var(--teranoo-background)] px-6 py-3">
|
|
27
|
+
{header}
|
|
28
|
+
</header>
|
|
29
|
+
)}
|
|
30
|
+
<div className="flex-1 overflow-y-auto p-6">
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
</main>
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
interface Column<T> {
|
|
4
|
+
key: string
|
|
5
|
+
header: string
|
|
6
|
+
render?: (item: T) => React.ReactNode
|
|
7
|
+
sortable?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface DataTableProps<T> {
|
|
11
|
+
columns: Column<T>[]
|
|
12
|
+
data: T[]
|
|
13
|
+
searchable?: boolean
|
|
14
|
+
pageSize?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function DataTable<T extends Record<string, any>>({
|
|
18
|
+
columns,
|
|
19
|
+
data,
|
|
20
|
+
searchable = true,
|
|
21
|
+
pageSize = 10,
|
|
22
|
+
}: DataTableProps<T>) {
|
|
23
|
+
const [search, setSearch] = React.useState("")
|
|
24
|
+
const [page, setPage] = React.useState(1)
|
|
25
|
+
const [sortKey, setSortKey] = React.useState<string | null>(null)
|
|
26
|
+
const [sortDir, setSortDir] = React.useState<"asc" | "desc">("asc")
|
|
27
|
+
|
|
28
|
+
// تصفية
|
|
29
|
+
const filtered = React.useMemo(() => {
|
|
30
|
+
if (!search) return data
|
|
31
|
+
return data.filter((item) =>
|
|
32
|
+
columns.some((col) =>
|
|
33
|
+
String(item[col.key]).toLowerCase().includes(search.toLowerCase())
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
}, [data, search, columns])
|
|
37
|
+
|
|
38
|
+
// ترتيب
|
|
39
|
+
const sorted = React.useMemo(() => {
|
|
40
|
+
if (!sortKey) return filtered
|
|
41
|
+
return [...filtered].sort((a, b) => {
|
|
42
|
+
const aVal = a[sortKey]
|
|
43
|
+
const bVal = b[sortKey]
|
|
44
|
+
if (aVal < bVal) return sortDir === "asc" ? -1 : 1
|
|
45
|
+
if (aVal > bVal) return sortDir === "asc" ? 1 : -1
|
|
46
|
+
return 0
|
|
47
|
+
})
|
|
48
|
+
}, [filtered, sortKey, sortDir])
|
|
49
|
+
|
|
50
|
+
// تقسيم الصفحات
|
|
51
|
+
const totalPages = Math.ceil(sorted.length / pageSize)
|
|
52
|
+
const paged = sorted.slice((page - 1) * pageSize, page * pageSize)
|
|
53
|
+
|
|
54
|
+
const handleSort = (key: string) => {
|
|
55
|
+
if (sortKey === key) {
|
|
56
|
+
setSortDir(sortDir === "asc" ? "desc" : "asc")
|
|
57
|
+
} else {
|
|
58
|
+
setSortKey(key)
|
|
59
|
+
setSortDir("asc")
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="space-y-4">
|
|
65
|
+
{/* بحث */}
|
|
66
|
+
{searchable && (
|
|
67
|
+
<div className="relative max-w-sm">
|
|
68
|
+
<input
|
|
69
|
+
value={search}
|
|
70
|
+
onChange={(e) => { setSearch(e.target.value); setPage(1) }}
|
|
71
|
+
placeholder="بحث..."
|
|
72
|
+
className="flex h-10 w-full rounded-[var(--teranoo-radius)] border border-[var(--teranoo-border)] bg-[var(--teranoo-background)] px-3 py-2 pr-10 text-sm"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{/* جدول */}
|
|
78
|
+
<div className="rounded-[var(--teranoo-radius)] border border-[var(--teranoo-border)] overflow-hidden">
|
|
79
|
+
<table className="w-full text-sm">
|
|
80
|
+
<thead className="bg-[var(--teranoo-muted)]">
|
|
81
|
+
<tr>
|
|
82
|
+
{columns.map((col) => (
|
|
83
|
+
<th
|
|
84
|
+
key={col.key}
|
|
85
|
+
onClick={() => col.sortable && handleSort(col.key)}
|
|
86
|
+
className={`px-4 py-3 text-right font-medium text-[var(--teranoo-muted-foreground)] ${
|
|
87
|
+
col.sortable ? "cursor-pointer hover:text-[var(--teranoo-foreground)]" : ""
|
|
88
|
+
}`}
|
|
89
|
+
>
|
|
90
|
+
{col.header}
|
|
91
|
+
{sortKey === col.key && (
|
|
92
|
+
<span className="mr-1">{sortDir === "asc" ? "↑" : "↓"}</span>
|
|
93
|
+
)}
|
|
94
|
+
</th>
|
|
95
|
+
))}
|
|
96
|
+
</tr>
|
|
97
|
+
</thead>
|
|
98
|
+
<tbody className="bg-[var(--teranoo-background)]">
|
|
99
|
+
{paged.length === 0 ? (
|
|
100
|
+
<tr>
|
|
101
|
+
<td colSpan={columns.length} className="px-4 py-12 text-center text-[var(--teranoo-muted-foreground)]">
|
|
102
|
+
لا توجد بيانات
|
|
103
|
+
</td>
|
|
104
|
+
</tr>
|
|
105
|
+
) : (
|
|
106
|
+
paged.map((item, i) => (
|
|
107
|
+
<tr key={i} className="border-t border-[var(--teranoo-border)] hover:bg-[var(--teranoo-muted)]">
|
|
108
|
+
{columns.map((col) => (
|
|
109
|
+
<td key={col.key} className="px-4 py-3">
|
|
110
|
+
{col.render ? col.render(item) : item[col.key]}
|
|
111
|
+
</td>
|
|
112
|
+
))}
|
|
113
|
+
</tr>
|
|
114
|
+
))
|
|
115
|
+
)}
|
|
116
|
+
</tbody>
|
|
117
|
+
</table>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{/* Pagination */}
|
|
121
|
+
{totalPages > 1 && (
|
|
122
|
+
<div className="flex items-center justify-between">
|
|
123
|
+
<p className="text-sm text-[var(--teranoo-muted-foreground)]">
|
|
124
|
+
صفحة {page} من {totalPages}
|
|
125
|
+
</p>
|
|
126
|
+
<div className="flex gap-2">
|
|
127
|
+
<button
|
|
128
|
+
onClick={() => setPage(p => Math.max(1, p - 1))}
|
|
129
|
+
disabled={page === 1}
|
|
130
|
+
className="px-3 py-1 rounded border border-[var(--teranoo-border)] text-sm disabled:opacity-50"
|
|
131
|
+
>
|
|
132
|
+
السابق
|
|
133
|
+
</button>
|
|
134
|
+
<button
|
|
135
|
+
onClick={() => setPage(p => Math.min(totalPages, p + 1))}
|
|
136
|
+
disabled={page === totalPages}
|
|
137
|
+
className="px-3 py-1 rounded border border-[var(--teranoo-border)] text-sm disabled:opacity-50"
|
|
138
|
+
>
|
|
139
|
+
التالي
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
interface EmptyStateProps {
|
|
4
|
+
icon?: React.ReactNode
|
|
5
|
+
title: string
|
|
6
|
+
description?: string
|
|
7
|
+
action?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
13
|
+
{icon && (
|
|
14
|
+
<div className="mb-4 h-16 w-16 rounded-full bg-[var(--teranoo-muted)] flex items-center justify-center">
|
|
15
|
+
{icon}
|
|
16
|
+
</div>
|
|
17
|
+
)}
|
|
18
|
+
<h3 className="text-lg font-semibold">{title}</h3>
|
|
19
|
+
{description && (
|
|
20
|
+
<p className="mt-1 text-sm text-[var(--teranoo-muted-foreground)] max-w-sm">
|
|
21
|
+
{description}
|
|
22
|
+
</p>
|
|
23
|
+
)}
|
|
24
|
+
{action && (
|
|
25
|
+
<div className="mt-4">
|
|
26
|
+
{action}
|
|
27
|
+
</div>
|
|
28
|
+
)}
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Card, CardContent } from "./card"
|
|
3
|
+
|
|
4
|
+
interface KpiCardProps {
|
|
5
|
+
title: string
|
|
6
|
+
value: string | number
|
|
7
|
+
change?: number
|
|
8
|
+
changeLabel?: string
|
|
9
|
+
icon?: React.ReactNode
|
|
10
|
+
variant?: "default" | "success" | "warning" | "danger"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function KpiCard({ title, value, change, changeLabel, icon, variant = "default" }: KpiCardProps) {
|
|
14
|
+
const changeColor = change
|
|
15
|
+
? change > 0
|
|
16
|
+
? "text-[var(--teranoo-success)]"
|
|
17
|
+
: "text-[var(--teranoo-danger)]"
|
|
18
|
+
: "text-[var(--teranoo-muted-foreground)]"
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Card>
|
|
22
|
+
<CardContent className="p-6">
|
|
23
|
+
<div className="flex items-center justify-between">
|
|
24
|
+
<div className="space-y-1">
|
|
25
|
+
<p className="text-sm text-[var(--teranoo-muted-foreground)]">{title}</p>
|
|
26
|
+
<p className="text-3xl font-bold">{value}</p>
|
|
27
|
+
{change !== undefined && (
|
|
28
|
+
<p className={`text-sm ${changeColor}`}>
|
|
29
|
+
{change > 0 ? "+" : ""}{change}% {changeLabel}
|
|
30
|
+
</p>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
{icon && (
|
|
34
|
+
<div className="h-12 w-12 rounded-full bg-[var(--teranoo-muted)] flex items-center justify-center">
|
|
35
|
+
{icon}
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
</CardContent>
|
|
40
|
+
</Card>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
interface PageHeaderProps {
|
|
4
|
+
title: string
|
|
5
|
+
description?: string
|
|
6
|
+
children?: React.ReactNode
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function PageHeader({ title, description, children }: PageHeaderProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex items-center justify-between pb-6">
|
|
12
|
+
<div className="space-y-1">
|
|
13
|
+
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
|
|
14
|
+
{description && (
|
|
15
|
+
<p className="text-sm text-[var(--teranoo-muted-foreground)]">{description}</p>
|
|
16
|
+
)}
|
|
17
|
+
</div>
|
|
18
|
+
{children && (
|
|
19
|
+
<div className="flex items-center gap-2">
|
|
20
|
+
{children}
|
|
21
|
+
</div>
|
|
22
|
+
)}
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
+
onClear?: () => void
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
|
|
8
|
+
({ className, onClear, value, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<div className="relative">
|
|
11
|
+
<svg
|
|
12
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--teranoo-muted-foreground)]"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
viewBox="0 0 24 24"
|
|
15
|
+
fill="none"
|
|
16
|
+
stroke="currentColor"
|
|
17
|
+
strokeWidth="2"
|
|
18
|
+
>
|
|
19
|
+
<circle cx="11" cy="11" r="8" />
|
|
20
|
+
<path d="m21 21-4.3-4.3" />
|
|
21
|
+
</svg>
|
|
22
|
+
<input
|
|
23
|
+
ref={ref}
|
|
24
|
+
value={value}
|
|
25
|
+
className="flex h-10 w-full rounded-[var(--teranoo-radius)] border border-[var(--teranoo-border)] bg-[var(--teranoo-background)] px-3 py-2 pr-10 text-sm ring-offset-background placeholder:text-[var(--teranoo-muted-foreground)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--teranoo-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
{value && onClear && (
|
|
29
|
+
<button
|
|
30
|
+
onClick={onClear}
|
|
31
|
+
className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--teranoo-muted-foreground)] hover:text-[var(--teranoo-foreground)]"
|
|
32
|
+
>
|
|
33
|
+
×
|
|
34
|
+
</button>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
SearchInput.displayName = "SearchInput"
|