g360-cli 1.4.0 → 1.6.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 +122 -0
- package/package.json +11 -4
- package/src/assets/brand/brand.json +43 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.svg +9 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.svg +9 -0
- package/src/assets/brand/g360/logotypes/logo-g360-dark.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo-g360-light.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo_g360_dark_b64.txt +1 -0
- package/src/assets/brand/g360/logotypes/logo_g360_light_b64.txt +1 -0
- package/src/assets/config/g360-skills.json +60 -0
- package/src/assets/signature/g360_flet/__init__.py +5 -0
- package/src/assets/signature/g360_flet/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/assets/signature/g360_flet/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/assets/signature/g360_flet/__pycache__/g360_signature.cpython-312.pyc +0 -0
- package/src/assets/signature/g360_flet/__pycache__/g360_signature.cpython-314.pyc +0 -0
- package/src/assets/signature/g360_flet/g360_signature.py +224 -0
- package/src/assets/signature/index.js +130 -0
- package/src/assets/snippets/snippets.json +293 -101
- package/src/assets/templates/lit-web/src/core/skill.json +12 -5
- package/src/assets/templates/python-cli/build-portable.bat +19 -9
- package/src/assets/templates/python-cli/src/core/skill.json +25 -6
- package/src/assets/templates/python-customtkinter/README.md +32 -31
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-customtkinter/build.bat +50 -0
- package/src/assets/templates/python-customtkinter/pyproject.toml +26 -0
- package/src/assets/templates/python-customtkinter/run.bat +11 -20
- package/src/assets/templates/python-customtkinter/src/core/g360_theme.py +101 -0
- package/src/assets/templates/python-customtkinter/src/core/skill.json +23 -10
- package/src/assets/templates/python-customtkinter/src/main.py +61 -81
- package/src/assets/templates/python-flet/README.md +52 -93
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet/build.bat +36 -0
- package/src/assets/templates/python-flet/pyproject.toml +26 -0
- package/src/assets/templates/python-flet/run.bat +11 -72
- package/src/assets/templates/python-flet/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/core/g360_theme.py +193 -0
- package/src/assets/templates/python-flet/src/core/skill.json +28 -8
- package/src/assets/templates/python-flet/src/export/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/main.py +113 -68
- package/src/assets/templates/python-flet/src/test_app.py +41 -37
- package/src/assets/templates/python-flet/src/ui/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/README.md +70 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet-migrate/build.bat +36 -0
- package/src/assets/templates/python-flet-migrate/pyproject.toml +26 -0
- package/src/assets/templates/python-flet-migrate/run.bat +20 -0
- package/src/assets/templates/python-flet-migrate/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/src/core/g360_theme.py +186 -0
- package/src/assets/templates/python-flet-migrate/src/core/skill.json +34 -0
- package/src/assets/templates/python-flet-migrate/src/main.py +71 -42
- package/src/assets/templates/solid-web/src/core/skill.json +12 -5
- package/src/assets/templates/svelte-web/src/core/skill.json +12 -5
- package/src/assets/templates/web-pwa/index.html +5 -7
- package/src/assets/templates/web-pwa/public/favicon.svg +4 -0
- package/src/assets/templates/web-pwa/public/manifest.json +11 -0
- package/src/assets/templates/web-pwa/src/App.jsx +22 -0
- package/src/assets/templates/web-pwa/src/components/FooterSignature.jsx +9 -0
- package/src/assets/templates/web-pwa/src/components/Header.jsx +12 -0
- package/src/assets/templates/web-pwa/src/components/LoadingOverlay.jsx +10 -0
- package/src/assets/templates/web-pwa/src/core/skill.json +25 -0
- package/src/assets/templates/web-pwa/src/main.jsx +9 -0
- package/src/assets/templates/web-pwa/src/styles/main.css +133 -0
- package/src/assets/templates/web-pwa/vite.config.js +12 -0
- package/src/cli.js +10 -5
- package/src/commands/bring.js +81 -6
- package/src/commands/list.js +79 -9
- package/src/commands/signature.js +252 -55
- package/src/lib/asset-validator.js +145 -0
- package/src/lib/auditor.js +48 -0
- package/src/lib/global-config.js +260 -0
- package/src/lib/i18n.js +238 -0
- package/src/lib/logger.js +100 -0
- package/src/lib/manifest.js +70 -1
- package/src/lib/validator.js +39 -0
- package/src/schemas/skills-schema.json +1 -0
- package/src/schemas/snippets-schema.json +1 -0
- package/dist/g360.exe +0 -0
- package/src/assets/templates/python-customtkinter/build-portable.bat +0 -28
- package/src/assets/templates/python-customtkinter/requirements.txt +0 -1
- package/src/assets/templates/python-flet/build-portable.bat +0 -42
- package/src/assets/templates/python-flet/create_shortcut.vbs +0 -35
- package/src/assets/templates/python-flet/requirements.txt +0 -3
- package/src/assets/templates/web-pwa/app.js +0 -1
- package/src/assets/templates/web-pwa/styles.css +0 -28
|
@@ -1,101 +1,293 @@
|
|
|
1
|
-
{
|
|
2
|
-
"snippets": [
|
|
3
|
-
{
|
|
4
|
-
"name": "cli-argparse-basic",
|
|
5
|
-
"description": "Basic argparse CLI structure",
|
|
6
|
-
"language": "python",
|
|
7
|
-
"code": "import argparse\n\nparser = argparse.ArgumentParser(description='My CLI App')\nparser.add_argument('input', help='Input file')\nparser.add_argument('-v', '--verbose', action='store_true')\nparser.add_argument('-o', '--output', default='output.txt')\nargs = parser.parse_args()"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"name": "cli-subcommands",
|
|
11
|
-
"description": "CLI with subcommands (git-style)",
|
|
12
|
-
"language": "python",
|
|
13
|
-
"code": "import argparse\n\nparser = argparse.ArgumentParser()\nsubparsers = parser.add_subparsers()\n\ninit_parser = subparsers.add_parser('init')\ninit_parser.add_argument('name')\n\nrun_parser = subparsers.add_parser('run')\nrun_parser.add_argument('--watch', action='store_true')"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"name": "cli-logging",
|
|
17
|
-
"description": "Logging setup for CLI",
|
|
18
|
-
"language": "python",
|
|
19
|
-
"code": "import logging\nimport sys\n\ndef setup_logging(verbose=False):\n level = logging.DEBUG if verbose else logging.INFO\n logging.basicConfig(\n level=level,\n format='%(asctime)s - %(levelname)s - %(message)s',\n handlers=[logging.StreamHandler(sys.stdout)]\n )\n return logging.getLogger(__name__)"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"name": "cli-config-json",
|
|
23
|
-
"description": "Load JSON config file",
|
|
24
|
-
"language": "python",
|
|
25
|
-
"code": "import json\nfrom pathlib import Path\n\ndef load_config(config_path='config.json'):\n path = Path(config_path)\n if not path.exists():\n raise FileNotFoundError(f'Config not found: {config_path}')\n with open(path) as f:\n return json.load(f)"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"name": "cli-progress-bar",
|
|
29
|
-
"description": "Progress bar with tqdm",
|
|
30
|
-
"language": "python",
|
|
31
|
-
"code": "from tqdm import tqdm\n\nfor item in tqdm(items, desc='Processing', unit='item'):\n # process item\n pass"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "cli-env-config",
|
|
35
|
-
"description": "Environment-based configuration",
|
|
36
|
-
"language": "python",
|
|
37
|
-
"code": "import os\nfrom pathlib import Path\n\ndef get_config():\n env = os.getenv('APP_ENV', 'development')\n config_dir = Path(__file__).parent / 'config'\n config_file = config_dir / f'{env}.json'\n return json.loads(config_file.read_text())"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"name": "cli-exit-codes",
|
|
41
|
-
"description": "Exit codes constants",
|
|
42
|
-
"language": "python",
|
|
43
|
-
"code": "EXIT_OK = 0\nEXIT_ERROR = 1\nEXIT_USAGE = 2\nEXIT_DATA = 3\n\n# Usage: sys.exit(EXIT_ERROR)"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"name": "g360-header",
|
|
47
|
-
"description": "Standard G360 header",
|
|
48
|
-
"code": "<header class='g360-header'>...</header>"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"name": "g360-button",
|
|
52
|
-
"description": "G360 styled button",
|
|
53
|
-
"code": "<button class='g360-btn'>Click</button>"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"name": "g360-card",
|
|
57
|
-
"description": "G360 card component",
|
|
58
|
-
"code": "<div class='g360-card'>...</div>"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"name": "g360-badge",
|
|
62
|
-
"description": "G360 status badge",
|
|
63
|
-
"code": "<span class='g360-badge g360-badge-success'>Active</span>"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"name": "
|
|
67
|
-
"description": "
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"code": "
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"name": "
|
|
97
|
-
"description": "
|
|
98
|
-
"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
1
|
+
{
|
|
2
|
+
"snippets": [
|
|
3
|
+
{
|
|
4
|
+
"name": "cli-argparse-basic",
|
|
5
|
+
"description": "Basic argparse CLI structure",
|
|
6
|
+
"language": "python",
|
|
7
|
+
"code": "import argparse\n\nparser = argparse.ArgumentParser(description='My CLI App')\nparser.add_argument('input', help='Input file')\nparser.add_argument('-v', '--verbose', action='store_true')\nparser.add_argument('-o', '--output', default='output.txt')\nargs = parser.parse_args()"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "cli-subcommands",
|
|
11
|
+
"description": "CLI with subcommands (git-style)",
|
|
12
|
+
"language": "python",
|
|
13
|
+
"code": "import argparse\n\nparser = argparse.ArgumentParser()\nsubparsers = parser.add_subparsers()\n\ninit_parser = subparsers.add_parser('init')\ninit_parser.add_argument('name')\n\nrun_parser = subparsers.add_parser('run')\nrun_parser.add_argument('--watch', action='store_true')"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "cli-logging",
|
|
17
|
+
"description": "Logging setup for CLI",
|
|
18
|
+
"language": "python",
|
|
19
|
+
"code": "import logging\nimport sys\n\ndef setup_logging(verbose=False):\n level = logging.DEBUG if verbose else logging.INFO\n logging.basicConfig(\n level=level,\n format='%(asctime)s - %(levelname)s - %(message)s',\n handlers=[logging.StreamHandler(sys.stdout)]\n )\n return logging.getLogger(__name__)"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "cli-config-json",
|
|
23
|
+
"description": "Load JSON config file",
|
|
24
|
+
"language": "python",
|
|
25
|
+
"code": "import json\nfrom pathlib import Path\n\ndef load_config(config_path='config.json'):\n path = Path(config_path)\n if not path.exists():\n raise FileNotFoundError(f'Config not found: {config_path}')\n with open(path) as f:\n return json.load(f)"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "cli-progress-bar",
|
|
29
|
+
"description": "Progress bar with tqdm",
|
|
30
|
+
"language": "python",
|
|
31
|
+
"code": "from tqdm import tqdm\n\nfor item in tqdm(items, desc='Processing', unit='item'):\n # process item\n pass"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "cli-env-config",
|
|
35
|
+
"description": "Environment-based configuration",
|
|
36
|
+
"language": "python",
|
|
37
|
+
"code": "import os\nfrom pathlib import Path\n\ndef get_config():\n env = os.getenv('APP_ENV', 'development')\n config_dir = Path(__file__).parent / 'config'\n config_file = config_dir / f'{env}.json'\n return json.loads(config_file.read_text())"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "cli-exit-codes",
|
|
41
|
+
"description": "Exit codes constants",
|
|
42
|
+
"language": "python",
|
|
43
|
+
"code": "EXIT_OK = 0\nEXIT_ERROR = 1\nEXIT_USAGE = 2\nEXIT_DATA = 3\n\n# Usage: sys.exit(EXIT_ERROR)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "g360-header",
|
|
47
|
+
"description": "Standard G360 header",
|
|
48
|
+
"code": "<header class='g360-header'>...</header>"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "g360-button",
|
|
52
|
+
"description": "G360 styled button",
|
|
53
|
+
"code": "<button class='g360-btn'>Click</button>"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "g360-card",
|
|
57
|
+
"description": "G360 card component",
|
|
58
|
+
"code": "<div class='g360-card'>...</div>"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "g360-badge",
|
|
62
|
+
"description": "G360 status badge",
|
|
63
|
+
"code": "<span class='g360-badge g360-badge-success'>Active</span>"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "ctk-window",
|
|
67
|
+
"description": "CustomTkinter window setup with G360 theme",
|
|
68
|
+
"language": "python",
|
|
69
|
+
"code": "import customtkinter as ctk\nfrom core.g360_theme import G360Theme\n\nclass G360App(ctk.CTk):\n def __init__(self):\n super().__init__()\n self.theme = G360Theme()\n self.title(self.theme.build_name())\n self.geometry('900x700')\n self.configure(fg_color=self.theme.bg)\n ctk.set_appearance_mode('dark')\n ctk.set_default_color_theme('green')"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "ctk-button",
|
|
73
|
+
"description": "G360 styled CustomTkinter button",
|
|
74
|
+
"language": "python",
|
|
75
|
+
"code": "ctk.CTkButton(\n parent,\n text='Aceptar',\n command=on_click,\n fg_color='#00d084',\n hover_color='#00b070',\n text_color='#0b1220',\n corner_radius=8,\n height=40,\n font=('Segoe UI', 14),\n)"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "ctk-input",
|
|
79
|
+
"description": "G360 styled CustomTkinter entry",
|
|
80
|
+
"language": "python",
|
|
81
|
+
"code": "ctk.CTkEntry(\n parent,\n placeholder_text='Ingrese valor...',\n width=300,\n height=40,\n fg_color='#1a2332',\n border_color='#00d084',\n text_color='#f0f4f8',\n placeholder_text_color='#94a3b8',\n corner_radius=8,\n)"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "ctk-frame",
|
|
85
|
+
"description": "G360 glass frame container",
|
|
86
|
+
"language": "python",
|
|
87
|
+
"code": "ctk.CTkFrame(\n parent,\n fg_color='#1a2332',\n corner_radius=12,\n border_width=1,\n border_color='#00d08440',\n)"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "ctk-table",
|
|
91
|
+
"description": "CustomTkinter Treeview table with G360 styling",
|
|
92
|
+
"language": "python",
|
|
93
|
+
"code": "import tkinter as tk\nfrom tkinter import ttk\n\nstyle = ttk.Style()\nstyle.theme_use('clam')\nstyle.configure('G360.Treeview',\n background='#1a2332',\n foreground='#f0f4f8',\n fieldbackground='#1a2332',\n rowheight=35,\n font=('Segoe UI', 12))\nstyle.configure('G360.Treeview.Heading',\n background='#00d084',\n foreground='#0b1220',\n font=('Segoe UI', 12, 'bold'))\n\ntree = ttk.Treeview(parent, style='G360.Treeview',\n columns=('sku', 'nombre', 'cantidad'),\n show='headings')\ntree.heading('sku', text='SKU')\ntree.heading('nombre', text='Nombre')\ntree.heading('cantidad', text='Cantidad')"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "ctk-dialog",
|
|
97
|
+
"description": "CustomTkinter message dialog",
|
|
98
|
+
"language": "python",
|
|
99
|
+
"code": "import customtkinter as ctk\n\ndef show_info(parent, title, message):\n dialog = ctk.CTkToplevel(parent)\n dialog.title(title)\n dialog.geometry('400x200')\n dialog.configure(fg_color='#0b1220')\n dialog.grab_set()\n \n ctk.CTkLabel(dialog, text=message, wraplength=350,\n text_color='#f0f4f8').pack(pady=20)\n ctk.CTkButton(dialog, text='OK',\n command=dialog.destroy,\n fg_color='#00d084').pack(pady=10)"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "ctk-sidebar",
|
|
103
|
+
"description": "CustomTkinter sidebar navigation",
|
|
104
|
+
"language": "python",
|
|
105
|
+
"code": "class Sidebar(ctk.CTkFrame):\n def __init__(self, parent, items, on_select):\n super().__init__(parent, fg_color='#1a2332', width=200)\n self.on_select = on_select\n self.buttons = []\n \n for i, (text, icon) in enumerate(items):\n btn = ctk.CTkButton(\n self, text=text, anchor='w',\n fg_color='transparent',\n hover_color='#00d08420',\n text_color='#94a3b8',\n command=lambda idx=i: self._select(idx))\n btn.pack(fill='x', padx=10, pady=2)\n self.buttons.append(btn)\n \n def _select(self, idx):\n for i, btn in enumerate(self.buttons):\n btn.configure(\n fg_color='#00d08420' if i == idx else 'transparent',\n text_color='#00d084' if i == idx else '#94a3b8')\n self.on_select(idx)"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "flet-page",
|
|
109
|
+
"description": "Flet page setup with G360 theme",
|
|
110
|
+
"code": "import flet as ft\n\ndef main(page: ft.Page):\n page.title = 'G360 App'\n page.theme_mode = ft.ThemeMode.DARK\n page.bgcolor = '#0b1220'\n page.padding = 20\n page.add(ft.Text('G360 Desktop App', size=24, color='#00d084'))"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "flet-card",
|
|
114
|
+
"description": "G360 styled Flet Card",
|
|
115
|
+
"code": "ft.Container(\n content=ft.Column([...]),\n bgcolor='#1a2332',\n border_radius=12,\n padding=15,\n shadow=ft.BoxShadow(\n spread_radius=1,\n blur_radius=20,\n color='#00d08420'\n )\n)"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "flet-button",
|
|
119
|
+
"description": "G360 styled Flet Button",
|
|
120
|
+
"code": "ft.ElevatedButton(\n content=ft.Text('G360 Button', color='#0b1220'),\n style=ft.ButtonStyle(\n bgcolor='#00d084',\n color='#0b1220',\n shape=ft.RoundedRectangleBorder(radius=8),\n padding=15\n )\n)"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "flet-nav-rail",
|
|
124
|
+
"description": "Flet navigation rail for desktop",
|
|
125
|
+
"code": "ft.NavigationRail(\n selected_index=0,\n label_type=ft.NavigationRailLabelType.ALL,\n min_width=100,\n min_extended_width=200,\n destinations=[\n ft.NavigationRailDestination(icon=ft.icons.HOME_OUTLINED, selected_icon=ft.icons.HOME, label='Home'),\n ft.NavigationRailDestination(icon=ft.icons.SETTINGS_OUTLINED, selected_icon=ft.icons.SETTINGS, label='Settings'),\n ]\n)"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "flet-datatable",
|
|
129
|
+
"description": "Flet DataTable with G360 styling",
|
|
130
|
+
"code": "ft.DataTable(\n heading_row_color='#00d084',\n data_row_color='#1a2332',\n columns=[\n ft.DataColumn(ft.Text('SKU', color='#f0f4f8')),\n ft.DataColumn(ft.Text('Descripción', color='#f0f4f8')),\n ft.DataColumn(ft.Text('Cantidad', color='#f0f4f8')),\n ],\n rows=[]\n)"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "flet-dialog",
|
|
134
|
+
"description": "Flet modal dialog",
|
|
135
|
+
"code": "def show_dialog(page, title, content):\n dialog = ft.AlertDialog(\n title=ft.Text(title, color='#00d084'),\n content=ft.Container(content=content, padding=20),\n actions=[\n ft.TextButton('Cancelar', on_click=lambda _: page.close_dialog()),\n ft.ElevatedButton('Aceptar', bgcolor='#00d084', color='#0b1220'),\n ]\n )\n page.dialog = dialog\n dialog.open = True\n page.update()"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "flet-chart-bar",
|
|
139
|
+
"description": "Flet bar chart for data visualization",
|
|
140
|
+
"code": "ft.BarChart(\n bar_groups=[\n ft.BarChartGroup(\n x=0,\n bar_rods=[\n ft.BarChartRod(\n from_y=0,\n to_y=50,\n width=40,\n color='#00d084',\n ),\n ],\n ),\n ],\n left_axis=ft.ChartAxis(labels_size=40),\n bottom_axis=ft.ChartAxis(labels=[...]),\n)"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "flet-chart-line",
|
|
144
|
+
"description": "LineChart for time series data",
|
|
145
|
+
"code": "ft.LineChart(\n data_series=[\n ft.LineChartData(\n data_points=[\n ft.LineChartDataPoint(x=1, y=10),\n ft.LineChartDataPoint(x=2, y=25),\n ft.LineChartDataPoint(x=3, y=15),\n ft.LineChartDataPoint(x=4, y=40),\n ],\n color='#00d084',\n stroke_width=3,\n ),\n ],\n left_axis=ft.ChartAxis(labels_size=40),\n bottom_axis=ft.ChartAxis(labels=[...]),\n)"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "flet-kpi-card",
|
|
149
|
+
"description": "Reusable KPI card for dashboards",
|
|
150
|
+
"code": "ft.Container(\n content=ft.Column([\n ft.Text(label, size=14, color='#94a3b8'),\n ft.Text(value, size=32, weight=ft.FontWeight.BOLD, color='#00d084'),\n ft.Row([\n ft.Icon(ft.icons.TRENDING_UP, size=16, color='#22c55e'),\n ft.Text(f'{delta}%', size=14, color='#22c55e'),\n ]) if delta else ft.Container(),\n ], spacing=5),\n bgcolor='#1a2332',\n border_radius=12,\n padding=20,\n expand=True,\n)"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "flet-drop-zone",
|
|
154
|
+
"description": "File upload zone with FilePicker",
|
|
155
|
+
"code": "def __init__(self, page):\n self.page = page\n self.file_picker = ft.FilePicker(on_result=self._on_file_selected)\n self.page.overlay.append(self.file_picker)\n\ndef _build_drop_zone(self):\n return ft.Container(\n content=ft.Column([\n ft.Icon(ft.icons.UPLOAD_FILE, size=48, color='#00d084'),\n ft.Text('Arrastra un archivo o haz clic', size=16, color='#94a3b8'),\n ft.Text('Formatos: .xlsx, .xls, .csv', size=12, color='#64748b'),\n ], horizontal_alignment=ft.CrossAxisAlignment.CENTER),\n bgcolor='#1a2332',\n border=ft.border.all(2, '#00d08440'),\n border_radius=12,\n padding=40,\n on_click=lambda _: self.file_picker.pick_files(allow_multiple=False),\n ink=True,\n )\n\ndef _on_file_selected(self, e):\n if e.files:\n file = e.files[0]\n self._process_file(file.path, file.name)"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "flet-progress-bar",
|
|
159
|
+
"description": "Styled progress bar for loading states",
|
|
160
|
+
"code": "ft.Container(\n content=ft.Column([\n ft.Text(label, size=14, color='#94a3b8'),\n ft.ProgressBar(\n value=progress, # 0.0 to 1.0\n bar_height=8,\n color='#00d084',\n bgcolor='#1a2332',\n ),\n ft.Text(f'{int(progress * 100)}%', size=12, color='#64748b'),\n ], spacing=5),\n padding=10,\n)"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "flet-search-field",
|
|
164
|
+
"description": "Search field with icon",
|
|
165
|
+
"code": "ft.TextField(\n hint_text='Buscar...',\n prefix_icon=ft.icons.SEARCH,\n border_color='#1a2332',\n focused_border_color='#00d084',\n bgcolor='#1a2332',\n color='#f0f4f8',\n hint_style=ft.TextStyle(color='#94a3b8'),\n on_change=lambda e: on_search(e.control.value),\n)"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "flet-tabs",
|
|
169
|
+
"description": "Tabs for multi-view layouts",
|
|
170
|
+
"code": "ft.Tabs(\n selected_index=0,\n animation_duration=300,\n tabs=[\n ft.Tab(text='Resumen', content=resumen_view),\n ft.Tab(text='Detalle', content=detalle_view),\n ft.Tab(text='Reportes', content=reportes_view),\n ],\n on_change=lambda e: on_tab_change(e),\n)"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "flet-tooltip",
|
|
174
|
+
"description": "Rich tooltip on hover",
|
|
175
|
+
"code": "ft.Tooltip(\n message='Descripcion detallada del elemento',\n bgcolor='#1a2332',\n text_style=ft.TextStyle(color='#f0f4f8', size=12),\n padding=10,\n border_radius=8,\n wait_duration=500,\n content=ft.Text('Elemento con tooltip', color='#00d084'),\n)"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "flet-pagination",
|
|
179
|
+
"description": "Pagination control for large tables",
|
|
180
|
+
"code": "def _build_pagination(self, page, total_pages, on_page_change):\n controls = []\n controls.append(ft.IconButton(ft.icons.CHEVRON_LEFT, on_click=lambda _: on_page_change(page - 1)))\n for p in range(max(0, page - 2), min(total_pages, page + 3)):\n controls.append(\n ft.Container(\n content=ft.Text(str(p + 1), size=14, color='#00d084' if p == page else '#94a3b8'),\n padding=10,\n on_click=lambda _, x=p: on_page_change(x),\n )\n )\n controls.append(ft.IconButton(ft.icons.CHEVRON_RIGHT, on_click=lambda _: on_page_change(page + 1)))\n return ft.Row(controls, alignment=ft.MainAxisAlignment.CENTER)"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "flet-export-menu",
|
|
184
|
+
"description": "Export dropdown with PopupMenuButton",
|
|
185
|
+
"code": "ft.PopupMenuButton(\n content=ft.Row([\n ft.Icon(ft.icons.DOWNLOAD, color='#00d084'),\n ft.Text('Exportar', color='#00d084'),\n ]),\n items=[\n ft.PopupMenuItem(text='Exportar a Excel', icon=ft.icons.TABLE_CHART, on_click=lambda _: export_excel()),\n ft.PopupMenuItem(text='Exportar a CSV', icon=ft.icons.CSV, on_click=lambda _: export_csv()),\n ft.PopupMenuItem(text='Exportar a PDF', icon=ft.icons.PICTURE_AS_PDF, on_click=lambda _: export_pdf()),\n ft.PopupMenuDivider(),\n ft.PopupMenuItem(text='Copiar al portapapeles', icon=ft.icons.CONTENT_COPY, on_click=lambda _: copy_clipboard()),\n ],\n)"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "pandas-groupby-agg",
|
|
189
|
+
"description": "Data aggregation with groupby and agg",
|
|
190
|
+
"code": "import pandas as pd\n\ndef aggregate_data(df, group_cols, agg_map):\n \"\"\"Agrega datos usando groupby + agg.\n \n Args:\n df: DataFrame\n group_cols: Lista de columnas para agrupar\n agg_map: Dict {col: agg_func}, ej: {'venta': 'sum', 'cantidad': 'mean'}\n \"\"\"\n result = df.groupby(group_cols, as_index=False).agg(agg_map)\n return result"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "pandas-nc-normalize",
|
|
194
|
+
"description": "Normalize Notas de Credito from ERP data",
|
|
195
|
+
"code": "import pandas as pd\nimport numpy as np\n\ndef normalize_nc(df, monto_col='monto', tipo_col='tipo_doc'):\n \"\"\"Normaliza Notas de Credito: montos positivos y flag NC.\"\"\"\n df = df.copy()\n df['es_nc'] = df[tipo_col].str.contains('NC|NOTA CREDITO|DEVOLUCION', case=False, na=False).astype(int)\n df[monto_col] = df.apply(\n lambda r: -abs(r[monto_col]) if r['es_nc'] else abs(r[monto_col]),\n axis=1\n )\n return df"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"name": "pandas-calculate-kpi",
|
|
199
|
+
"description": "Calculate derived KPIs from data",
|
|
200
|
+
"code": "import pandas as pd\nimport numpy as np\n\ndef calculate_kpis(df, venta_col='venta_bruta', costo_col='costo', devolucion_col='devolucion'):\n \"\"\"Calcula KPIs derivados: margen, ratio devolucion, etc.\"\"\"\n result = df.copy()\n result['margen_bruto'] = result[venta_col] - result[costo_col]\n result['margen_pct'] = np.where(\n result[venta_col] != 0,\n (result['margen_bruto'] / result[venta_col]) * 100,\n 0\n )\n if devolucion_col in result.columns:\n result['ratio_devolucion'] = np.where(\n result[venta_col] != 0,\n (abs(result[devolucion_col]) / result[venta_col]) * 100,\n 0\n )\n return result"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"name": "pandas-date-range",
|
|
204
|
+
"description": "Filter DataFrame by date range",
|
|
205
|
+
"code": "import pandas as pd\n\ndef filter_by_date_range(df, date_col, start_date, end_date):\n \"\"\"Filtra DataFrame por rango de fechas.\"\"\"\n mask = (df[date_col] >= pd.Timestamp(start_date)) & (df[date_col] <= pd.Timestamp(end_date))\n return df[mask].copy()\n\n# Uso:\n# df_filtrado = filter_by_date_range(df, 'fecha', '2025-01-01', '2025-12-31')"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"name": "pandas-gap-analysis",
|
|
209
|
+
"description": "Gap analysis between catalog and purchases",
|
|
210
|
+
"code": "import pandas as pd\n\ndef gap_analysis(catalogo, compras, key_col='sku'):\n \"\"\"Analiza gaps entre catalogo y compras.\n \n Returns:\n Dict con: en_catalogo, en_compras, en_ambos, sin_compras, no_catalogados\n \"\"\"\n cat_set = set(catalogo[key_col].unique())\n comp_set = set(compras[key_col].unique())\n return {\n 'en_catalogo': len(cat_set),\n 'en_compras': len(comp_set),\n 'en_ambos': len(cat_set & comp_set),\n 'sin_compras': len(cat_set - comp_set),\n 'no_catalogados': len(comp_set - cat_set),\n }"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "pandas-temporal-compare",
|
|
214
|
+
"description": "Temporal comparison A vs B period",
|
|
215
|
+
"code": "import pandas as pd\n\ndef temporal_compare(df, date_col, value_col, group_col, period_a, period_b):\n \"\"\"Compara metricas entre dos periodos.\n \n Args:\n period_a, period_b: Tuplas (start, end) como strings 'YYYY-MM-DD'\n \"\"\"\n a = df[(df[date_col] >= period_a[0]) & (df[date_col] <= period_a[1])]\n b = df[(df[date_col] >= period_b[0]) & (df[date_col] <= period_b[1])]\n \n a_agg = a.groupby(group_col)[value_col].sum().rename('periodo_a')\n b_agg = b.groupby(group_col)[value_col].sum().rename('periodo_b')\n \n result = pd.concat([a_agg, b_agg], axis=1).fillna(0)\n result['variacion'] = result['periodo_b'] - result['periodo_a']\n result['variacion_pct'] = (result['variacion'] / result['periodo_a'].replace(0, float('nan'))) * 100\n return result"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"name": "pandas-hhi-index",
|
|
219
|
+
"description": "Calculate Herfindahl-Hirschman Index",
|
|
220
|
+
"code": "import pandas as pd\nimport numpy as np\n\ndef calculate_hhi(df, group_col, value_col):\n \"\"\"Calcula el indice HHI de concentracion.\n \n HHI = sum((share_i)^2) donde share_i es proporcion del total.\n > 2500: Alta concentracion\n 1500-2500: Concentracion moderada\n < 1500: Baja concentracion\n \"\"\"\n total = df[value_col].sum()\n if total == 0:\n return 0.0\n shares = df.groupby(group_col)[value_col].sum() / total\n hhi = (shares ** 2).sum() * 10000\n return round(hhi, 2)"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"name": "openpyxl-workbook",
|
|
224
|
+
"description": "Create Excel workbook with G360 styling",
|
|
225
|
+
"code": "from openpyxl import Workbook\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side\n\nG360_ACCENT = '00D084'\nG360_SURFACE = '1A2332'\nG360_TEXT = 'F0F4F8'\n\ndef create_g360_workbook():\n wb = Workbook()\n ws = wb.active\n ws.title = 'Reporte G360'\n \n # Header styling\n header_fill = PatternFill(start_color=G360_ACCENT, end_color=G360_ACCENT, fill_type='solid')\n header_font = Font(bold=True, color=G360_TEXT, size=11)\n thin_border = Border(\n left=Side(style='thin', color=G360_SURFACE),\n right=Side(style='thin', color=G360_SURFACE),\n top=Side(style='thin', color=G360_SURFACE),\n bottom=Side(style='thin', color=G360_SURFACE),\n )\n \n return wb, ws, header_fill, header_font, thin_border"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "openpyxl-styled-table",
|
|
229
|
+
"description": "Write styled table to Excel sheet",
|
|
230
|
+
"code": "def write_styled_table(ws, headers, data, header_fill, header_font, thin_border):\n \"\"\"Escribe una tabla con headers estilizados G360.\"\"\"\n # Headers\n for col_idx, header in enumerate(headers, 1):\n cell = ws.cell(row=1, column=col_idx, value=header)\n cell.fill = header_fill\n cell.font = header_font\n cell.alignment = Alignment(horizontal='center')\n cell.border = thin_border\n \n # Data\n for row_idx, row_data in enumerate(data, 2):\n for col_idx, value in enumerate(row_data, 1):\n cell = ws.cell(row=row_idx, column=col_idx, value=value)\n cell.border = thin_border\n cell.alignment = Alignment(horizontal='center')\n \n # Auto-width\n for col in ws.columns:\n max_length = max(len(str(cell.value or '')) for cell in col)\n ws.column_dimensions[col[0].column_letter].width = max_length + 3"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"name": "openpyxl-kpi-sheet",
|
|
234
|
+
"description": "Create KPI summary sheet with formatting",
|
|
235
|
+
"code": "def write_kpi_sheet(wb, sheet_name, kpis):\n \"\"\"Crea un sheet de KPIs con formato G360.\n \n kpis: [(nombre, valor, formato), ...]\n \"\"\"\n ws = wb.create_sheet(title=sheet_name)\n \n accent_fill = PatternFill(start_color='00D084', end_color='00D084', fill_type='solid')\n accent_font = Font(bold=True, color='F0F4F8', size=14)\n value_font = Font(bold=True, size=20, color='00D084')\n \n ws.cell(row=1, column=1, value='KPI').fill = accent_fill\n ws.cell(row=1, column=1).font = accent_font\n ws.cell(row=1, column=2, value='Valor').fill = accent_fill\n ws.cell(row=1, column=2).font = accent_font\n \n for i, (name, value, fmt) in enumerate(kpis, 2):\n ws.cell(row=i, column=1, value=name)\n ws.cell(row=i, column=2, value=value)\n ws.cell(row=i, column=2).font = value_font\n if fmt:\n ws.cell(row=i, column=2).number_format = fmt\n \n ws.column_dimensions['A'].width = 30\n ws.column_dimensions['B'].width = 20"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"name": "openpyxl-multi-sheet",
|
|
239
|
+
"description": "Create multi-sheet Excel report",
|
|
240
|
+
"code": "def create_multi_sheet_report(output_path, sheets_data):\n \"\"\"Crea un Excel con multiples sheets.\n \n sheets_data: [(name, headers, rows), ...]\n \"\"\"\n from openpyxl import Workbook\n wb = Workbook()\n wb.remove(wb.active)\n \n header_fill = PatternFill(start_color='00D084', end_color='00D084', fill_type='solid')\n header_font = Font(bold=True, color='F0F4F8', size=11)\n \n for name, headers, rows in sheets_data:\n ws = wb.create_sheet(title=name[:31])\n for col_idx, h in enumerate(headers, 1):\n cell = ws.cell(row=1, column=col_idx, value=h)\n cell.fill = header_fill\n cell.font = header_font\n for row_idx, row in enumerate(rows, 2):\n for col_idx, val in enumerate(row, 1):\n ws.cell(row=row_idx, column=col_idx, value=val)\n \n wb.save(output_path)\n return output_path"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "flet-loading-overlay",
|
|
244
|
+
"description": "Loading overlay with spinner for blocking operations",
|
|
245
|
+
"code": "self.loading = ft.Container(\n content=ft.Column([\n ft.ProgressRing(width=48, height=48, color='#00d084'),\n ft.Container(height=16),\n ft.Text('Cargando...', size=16, color='#f0f4f8'),\n ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, alignment=ft.MainAxisAlignment.CENTER),\n bgcolor='#0b1220E0',\n expand=True,\n alignment=ft.alignment.center,\n visible=False,\n)\n\n# En el layout, dentro de un Stack:\nft.Stack([page_content, self.loading], expand=True)\n\ndef show_loading(self, message='Procesando...'):\n self.loading.content.controls[2].value = message\n self.loading.visible = True\n self.page.update()\n\ndef hide_loading(self):\n self.loading.visible = False\n self.page.update()"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "flet-logo-base64",
|
|
249
|
+
"description": "Embed logo as base64 (PyInstaller-safe, no file paths)",
|
|
250
|
+
"code": "import base64\nfrom pathlib import Path\n\nLOGO_B64 = None\n\ndef get_logo_src():\n global LOGO_B64\n if LOGO_B64:\n return LOGO_B64\n candidates = [\n Path('assets/images/logo-g360-dark.svg'),\n Path(__file__).parent / 'assets' / 'images' / 'logo-g360-dark.svg',\n ]\n for p in candidates:\n if p.exists():\n raw = p.read_bytes()\n b64 = base64.b64encode(raw).decode()\n LOGO_B64 = f'data:image/svg+xml;base64,{b64}'\n return LOGO_B64\n return None\n\n# Uso en Flet:\n# ft.Image(src_base64=get_logo_src(), height=40, fit=ft.ImageFit.CONTAIN)"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "flet-logging-setup",
|
|
254
|
+
"description": "Logging setup for desktop apps (debug remoto)",
|
|
255
|
+
"code": "import logging\nimport sys\nfrom pathlib import Path\n\ndef setup_logging(app_name='g360-app'):\n log_dir = Path.home() / '.g360' / 'logs'\n log_dir.mkdir(parents=True, exist_ok=True)\n log_file = log_dir / f'{app_name}.log'\n \n logging.basicConfig(\n level=logging.DEBUG,\n format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n handlers=[\n logging.FileHandler(log_file, encoding='utf-8'),\n logging.StreamHandler(sys.stdout),\n ]\n )\n return logging.getLogger(app_name)\n\n# Uso:\n# logger = setup_logging('mi-app')\n# logger.info('App iniciada')"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"name": "flet-footer-signature",
|
|
259
|
+
"description": "G360 signature footer with logo + powered by",
|
|
260
|
+
"code": "ft.Container(\n content=ft.Row([\n ft.Image(src_base64=get_logo_src(), height=16, fit=ft.ImageFit.CONTAIN),\n ft.Container(width=8),\n ft.Text('powered by G360', size=10, color='#94a3b8'),\n ], alignment=ft.MainAxisAlignment.CENTER),\n padding=8,\n bgcolor='#1a2332',\n)"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "flet-error-boundary",
|
|
264
|
+
"description": "Global error handler for Flet callbacks",
|
|
265
|
+
"code": "import traceback\nimport logging\n\nlogger = logging.getLogger('g360-app')\n\ndef safe_handler(fn):\n \"\"\"Decorator que atrapa excepciones en callbacks Flet.\"\"\"\n def wrapper(*args, **kwargs):\n try:\n return fn(*args, **kwargs)\n except Exception as e:\n logger.error(f'Error en {fn.__name__}: {e}\\n{traceback.format_exc()}')\n page = args[0].page if hasattr(args[0], 'page') else None\n if page:\n page.snack_bar = ft.SnackBar(\n content=ft.Text(f'Error: {str(e)[:80]}'),\n bgcolor='#ef4444',\n )\n page.snack_bar.open = True\n page.update()\n return wrapper\n\n# Uso:\n# @safe_handler\n# def on_click(e): ..."
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"name": "flet-snackbar-error",
|
|
269
|
+
"description": "Show error message via SnackBar",
|
|
270
|
+
"code": "def show_error(page, message):\n page.snack_bar = ft.SnackBar(\n content=ft.Row([\n ft.Icon(ft.icons.ERROR_OUTLINE, color='#f0f4f8', size=20),\n ft.Text(str(message)[:120], color='#f0f4f8', size=14),\n ]),\n bgcolor='#ef4444',\n duration=5000,\n )\n page.snack_bar.open = True\n page.update()\n\ndef show_success(page, message):\n page.snack_bar = ft.SnackBar(\n content=ft.Row([\n ft.Icon(ft.icons.CHECK_CIRCLE, color='#0b1220', size=20),\n ft.Text(message, color='#0b1220', size=14),\n ]),\n bgcolor='#22c55e',\n duration=3000,\n )\n page.snack_bar.open = True\n page.update()\n\ndef show_warning(page, message):\n page.snack_bar = ft.SnackBar(\n content=ft.Row([\n ft.Icon(ft.icons.WARNING, color='#0b1220', size=20),\n ft.Text(message, color='#0b1220', size=14),\n ]),\n bgcolor='#f59e0b',\n duration=4000,\n )\n page.snack_bar.open = True\n page.update()"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "openpyxl-metadata",
|
|
274
|
+
"description": "G360 standard author (ccusi) + metadata on openpyxl workbook",
|
|
275
|
+
"code": "from openpyxl import Workbook\n\nwb = Workbook()\n# G360 standard metadata - autor consistente en todo el ecosistema\nwb.properties.creator = \"ccusi\"\nwb.properties.title = \"<APP_NAME>\"\nwb.properties.description = \"Generado por G360\"\n\nws = wb.active\n# ... populate data ...\nwb.save(\"reporte.xlsx\")"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"name": "openpyxl-excel-table",
|
|
279
|
+
"description": "Create Excel Table with formulas, totals outside sort range",
|
|
280
|
+
"code": "from openpyxl.worksheet.table import Table, TableStyleInfo\n\ndef write_table_with_formulas(ws, headers, data, total_values):\n \"\"\"Escribe datos como Tabla Excel con formulas vivas.\n \n La fila TOTAL queda fuera de la tabla para que el ordenamiento\n no la mueva. Los porcentajes usan =IFERROR(D{row}/D$total, 0).\n \"\"\"\n n_data = len(data)\n total_row = n_data + 2\n ncols = len(headers)\n \n # Headers\n for ci, h in enumerate(headers, 1):\n ws.cell(row=1, column=ci, value=h)\n \n # Data\n for ri, row in enumerate(data, 2):\n for ci, val in enumerate(row, 1):\n ws.cell(row=ri, column=ci, value=val)\n \n # Total row (FUERA del rango de la tabla)\n ws.cell(row=total_row, column=1, value=\"TOTAL\")\n ws.cell(row=total_row, column=4).value = f\"=SUM(D2:D{n_data+1})\"\n ws.cell(row=total_row, column=5).value = f\"=SUM(E2:E{n_data+1})\"\n ws.cell(row=total_row, column=6).value = 1.0\n ws.cell(row=total_row, column=7).value = 1.0\n \n # Live percentage formulas (IFERROR para evitar #DIV/0!)\n for ri in range(2, n_data + 2):\n ws.cell(row=ri, column=6).value = f\"=IFERROR(D{ri}/D${total_row}, 0)\"\n ws.cell(row=ri, column=7).value = f\"=IFERROR(E{ri}/E${total_row}, 0)\"\n \n # Crear tabla Excel (solos datos, sin total)\n col_letter = get_column_letter(ncols)\n table_ref = f\"A1:{col_letter}{n_data+1}\"\n table_name = f\"tbl_data\"\n tab = Table(displayName=table_name, ref=table_ref)\n tab.tableStyleInfo = TableStyleInfo(\n name=\"TableStyleMedium9\",\n showFirstColumn=False,\n showLastColumn=False,\n showRowStripes=True,\n showColumnStripes=False,\n )\n ws.add_table(tab)"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"name": "flet-export-progress",
|
|
284
|
+
"description": "Progress bars per vendor with on_file_done callback",
|
|
285
|
+
"code": "# En consolidate_view.py:_exportar()\ndef _exportar(self):\n vendedores_info = [\n (vid, vendedor.nom_vendedor)\n for vid, vendedor in self.vendedores.items()\n if self.seleccion.get(vid, [])\n ]\n if not vendedores_info:\n return\n\n self._prog_rows.clear()\n for _, nombre in vendedores_info:\n bar = ft.ProgressBar(width=380, color=self.p.accent, value=0)\n row = ft.Row([\n ft.Text(nombre, size=12, color=self.p.text, width=160),\n bar,\n ], spacing=8, vertical_alignment=ft.CrossAxisAlignment.CENTER)\n self._prog_rows[nombre] = row\n\n self._prog_col.controls = list(self._prog_rows.values())\n self._prog_col.visible = True\n self.status.value = \"Exportando...\"\n self.page.update()\n\n def on_file_done(nombre: str):\n row = self._prog_rows.get(nombre)\n if row:\n row.controls[1].value = 1.0\n self.page.update()\n\n def task():\n try:\n paths = exportar_xlsx(\n ...,\n on_file_done=on_file_done,\n )\n self.status.value = f\"Reportes: {len(paths)} archivo(s)\"\n self.status.color = self.p.success\n self.page.update()\n except Exception as ex:\n self.status.value = f\"Error: {str(ex)}\"\n\n threading.Thread(target=task, daemon=True).start()\n\n# En exporter.py: agregar callback en el signature\ndef exportar_xlsx(...,\n on_file_done: Optional[callable] = None) -> list[str]:\n ...\n for vid, vendedor in por_vendedor.items():\n ...\n generados.append(fpath)\n if on_file_done:\n on_file_done(vendedor.nom_vendedor)\n return generados"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"name": "flet-glass-theme",
|
|
289
|
+
"description": "Modern glassmorphism theme with G360 accent",
|
|
290
|
+
"code": "# theme.py - Paleta con glassmorphism\nfrom dataclasses import dataclass\n\n@dataclass\nclass Paleta:\n bg: str\n surface: str\n surface_hover: str\n accent: str\n accent_secondary: str\n text: str\n text_secondary: str\n border: str\n success: str\n warning: str\n danger: str\n info: str\n glass_bg: str\n glass_border: str\n input_bg: str\n\nLIGHT = Paleta(\n bg=\"#f0f4f8\", surface=\"#ffffff\", surface_hover=\"#f8fafc\",\n accent=\"#00d084\", accent_secondary=\"#6366f1\",\n text=\"#0b1221\", text_secondary=\"#64748b\",\n border=\"#e2e8f0\", success=\"#10b981\", warning=\"#f59e0b\",\n danger=\"#ef4444\", info=\"#06b6d4\",\n glass_bg=\"rgba(255,255,255,0.65)\",\n glass_border=\"rgba(226,232,240,0.6)\",\n input_bg=\"#f1f5f9\",\n)\n\nDARK = Paleta(\n bg=\"#0b0f19\", surface=\"#161b2b\", surface_hover=\"#1e2438\",\n accent=\"#00d084\", accent_secondary=\"#818cf8\",\n text=\"#f1f5f9\", text_secondary=\"#94a3b8\",\n border=\"#1e293b\", success=\"#34d399\", warning=\"#fbbf24\",\n danger=\"#f87171\", info=\"#22d3ee\",\n glass_bg=\"rgba(22,27,43,0.75)\",\n glass_border=\"rgba(30,41,59,0.6)\",\n input_bg=\"#1e2438\",\n)\n\n# Uso en containers (efecto glass):\n# ft.Container(\n# bgcolor=self.p.glass_bg,\n# border=ft.BorderSide(1, self.p.glass_border),\n# blur=ft.Blur(12, 12),\n# border_radius=10,\n# padding=16,\n# )"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
2
|
+
"name": "lit-web",
|
|
3
|
+
"description": "G360 Lit Web Components Application",
|
|
4
|
+
"framework": "lit",
|
|
5
5
|
"version": "1.0.0",
|
|
6
|
-
"createdAt": "2024-01-01T00:00:00.000Z",
|
|
7
6
|
"colors": {
|
|
8
7
|
"bg": "#0b1220",
|
|
9
8
|
"surface": "#151e2e",
|
|
10
9
|
"accent": "#00d084",
|
|
11
10
|
"text": "#f0f4f8",
|
|
12
|
-
"muted": "#94a3b8"
|
|
11
|
+
"muted": "#94a3b8",
|
|
12
|
+
"success": "#22c55e",
|
|
13
|
+
"warning": "#f59e0b",
|
|
14
|
+
"error": "#ef4444"
|
|
15
|
+
},
|
|
16
|
+
"effects": {
|
|
17
|
+
"glassmorphism": true,
|
|
18
|
+
"blur": "12px",
|
|
19
|
+
"rounded": "12px"
|
|
13
20
|
},
|
|
14
21
|
"signature": {
|
|
15
22
|
"mode": "powered",
|
|
@@ -5,29 +5,39 @@ title G360 - Portable CLI Launcher
|
|
|
5
5
|
echo.
|
|
6
6
|
echo ==============================================
|
|
7
7
|
echo G360 CLI - Portable Version
|
|
8
|
-
echo (Requires Python installed)
|
|
8
|
+
echo (Requires Python + uv installed)
|
|
9
9
|
echo ==============================================
|
|
10
10
|
echo.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
where uv >nul 2>&1
|
|
13
13
|
if errorlevel 1 (
|
|
14
|
-
echo
|
|
15
|
-
echo
|
|
16
|
-
echo
|
|
14
|
+
echo [SETUP] uv no encontrado
|
|
15
|
+
echo.
|
|
16
|
+
echo Por favor instala uv desde:
|
|
17
|
+
echo https://github.com/astral-sh/uv
|
|
18
|
+
echo.
|
|
17
19
|
pause
|
|
18
20
|
exit /b 1
|
|
19
21
|
)
|
|
20
22
|
|
|
21
|
-
echo
|
|
23
|
+
echo [CHECK] uv: OK
|
|
24
|
+
|
|
25
|
+
if not exist ".venv" (
|
|
26
|
+
echo [SETUP] Creando entorno virtual...
|
|
27
|
+
uv venv
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
echo [SETUP] Sincronizando dependencias...
|
|
31
|
+
uv sync
|
|
32
|
+
|
|
22
33
|
if not exist "src\main.py" (
|
|
23
34
|
echo ERROR: src\main.py no encontrado
|
|
24
35
|
pause
|
|
25
36
|
exit /b 1
|
|
26
37
|
)
|
|
27
38
|
|
|
28
|
-
echo Ejecutando G360 CLI...
|
|
29
39
|
echo.
|
|
30
|
-
|
|
40
|
+
echo [RUN] Ejecutando G360 CLI...
|
|
41
|
+
uv run python src\main.py %*
|
|
31
42
|
|
|
32
|
-
echo.
|
|
33
43
|
pause
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "python-cli",
|
|
3
|
+
"description": "G360 Python CLI Application",
|
|
4
|
+
"framework": "python-cli",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"colors": {
|
|
7
|
+
"bg": "#0b1220",
|
|
8
|
+
"surface": "#1a2332",
|
|
9
|
+
"accent": "#00d084",
|
|
10
|
+
"text": "#f0f4f8",
|
|
11
|
+
"muted": "#94a3b8",
|
|
12
|
+
"success": "#22c55e",
|
|
13
|
+
"warning": "#f59e0b",
|
|
14
|
+
"error": "#ef4444"
|
|
15
|
+
},
|
|
16
|
+
"effects": {
|
|
17
|
+
"glassmorphism": false,
|
|
18
|
+
"blur": "0px",
|
|
19
|
+
"rounded": "8px"
|
|
20
|
+
},
|
|
21
|
+
"signature": {
|
|
22
|
+
"mode": "powered",
|
|
23
|
+
"text": "powered by G360"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
# G360 CustomTkinter
|
|
1
|
+
# G360 Python CustomTkinter App
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Estructura
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
mi-proyecto/
|
|
7
|
+
├── src/
|
|
8
|
+
│ ├── main.py # Entry point
|
|
9
|
+
│ └── core/
|
|
10
|
+
│ ├── skill.json # Configuracion G360
|
|
11
|
+
│ └── g360_theme.py # Theme engine
|
|
12
|
+
├── assets/images/
|
|
13
|
+
├── skill.json
|
|
14
|
+
├── pyproject.toml
|
|
15
|
+
├── run.bat # Ejecutar app
|
|
16
|
+
├── build.bat # Build EXE (PyInstaller)
|
|
17
|
+
└── README.md
|
|
18
|
+
```
|
|
4
19
|
|
|
5
20
|
## Requisitos
|
|
6
21
|
|
|
7
|
-
- Python 3.
|
|
8
|
-
-
|
|
22
|
+
- **Python**: 3.11+ (instalado automaticamente por uv)
|
|
23
|
+
- **uv**: https://docs.astral.sh/uv/
|
|
9
24
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
pip install -r requirements.txt
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Ejecutar
|
|
25
|
+
## Ejecucion
|
|
17
26
|
|
|
18
27
|
```bash
|
|
19
28
|
run.bat
|
|
20
29
|
```
|
|
21
30
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
Ejecuta `build-portable.bat` - requiere Python instalado en el sistema.
|
|
25
|
-
|
|
26
|
-
## Estructura
|
|
31
|
+
## Build Windows
|
|
27
32
|
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
├── src/
|
|
31
|
-
│ ├── main.py # App principal
|
|
32
|
-
│ └── core/
|
|
33
|
-
│ └── skill.json # Configuracion G360
|
|
34
|
-
├── requirements.txt
|
|
35
|
-
├── run.bat # Ejecutar desarrollo
|
|
36
|
-
└── build-portable.bat # Version portable
|
|
33
|
+
```bash
|
|
34
|
+
build.bat
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
Genera `dist/G360-App.exe` usando PyInstaller.
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
- UI moderna con CustomTkinter
|
|
43
|
-
- Listo para expansion
|
|
39
|
+
## Theme
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
Colores definidos en `src/core/skill.json`, cargados via `G360Theme`.
|
|
46
42
|
|
|
47
|
-
|
|
43
|
+
| Token | Color |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `bg` | `#0b1220` |
|
|
46
|
+
| `surface` | `#1a2332` |
|
|
47
|
+
| `accent` | `#00d084` |
|
|
48
|
+
| `text` | `#f0f4f8` |
|