m9sh 0.2.1 → 0.2.2

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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/hotcdn.iml +30 -0
  3. data/.mise.toml +2 -2
  4. data/app/assets/config/manifest.js +4 -0
  5. data/app/assets/images/icons/activity.svg +3 -0
  6. data/app/assets/images/icons/bell.svg +4 -0
  7. data/app/assets/images/icons/book.svg +4 -0
  8. data/app/assets/images/icons/chevron-down.svg +3 -0
  9. data/app/assets/images/icons/chevron-left.svg +3 -0
  10. data/app/assets/images/icons/chevron-right.svg +3 -0
  11. data/app/assets/images/icons/credit-card.svg +4 -0
  12. data/app/assets/images/icons/dollar-sign.svg +3 -0
  13. data/app/assets/images/icons/edit.svg +4 -0
  14. data/app/assets/images/icons/github.svg +3 -0
  15. data/app/assets/images/icons/home.svg +4 -0
  16. data/app/assets/images/icons/info.svg +5 -0
  17. data/app/assets/images/icons/layout.svg +6 -0
  18. data/app/assets/images/icons/logout.svg +5 -0
  19. data/app/assets/images/icons/menu.svg +5 -0
  20. data/app/assets/images/icons/moon.svg +3 -0
  21. data/app/assets/images/icons/paintbrush.svg +6 -0
  22. data/app/assets/images/icons/search.svg +4 -0
  23. data/app/assets/images/icons/settings.svg +4 -0
  24. data/app/assets/images/icons/sun.svg +11 -0
  25. data/app/assets/images/icons/user.svg +4 -0
  26. data/app/assets/images/icons/users.svg +5 -0
  27. data/app/assets/stylesheets/tailwind.css +1180 -0
  28. data/app/components/backdrop_component.rb +103 -0
  29. data/app/components/docs/code_block_component.rb +56 -0
  30. data/app/components/docs/component_api_component.rb +16 -0
  31. data/app/components/docs/component_examples_component.rb +16 -0
  32. data/app/components/docs/component_header_component.html.erb +8 -0
  33. data/app/components/docs/component_header_component.rb +14 -0
  34. data/app/components/docs/component_installation_component.html.erb +15 -0
  35. data/app/components/docs/component_installation_component.rb +13 -0
  36. data/app/components/docs/component_page_component.html.erb +9 -0
  37. data/app/components/docs/component_page_component.rb +19 -0
  38. data/app/components/docs/component_preview_component.rb +318 -0
  39. data/app/components/docs/component_usage_component.rb +18 -0
  40. data/app/components/docs/prop_table_component.rb +64 -0
  41. data/app/controllers/application_controller.rb +3 -0
  42. data/app/controllers/blocks_controller.rb +51 -0
  43. data/app/controllers/docs_controller.rb +162 -0
  44. data/app/controllers/showcase_controller.rb +42 -0
  45. data/app/helpers/blocks_helper.rb +343 -0
  46. data/app/helpers/docs_helper.rb +3807 -0
  47. data/app/helpers/m9sh/toast_helper.rb +46 -0
  48. data/app/helpers/m9sh_helper.rb +343 -0
  49. data/app/javascript/application.js +3 -0
  50. data/app/javascript/controllers/application.js +9 -0
  51. data/app/javascript/controllers/backdrop_controller.js +137 -0
  52. data/app/javascript/controllers/color_customizer_controller.js +569 -0
  53. data/app/javascript/controllers/color_theme_controller.js +120 -0
  54. data/app/javascript/controllers/docs/component_preview_controller.js +149 -0
  55. data/app/javascript/controllers/docs/copy_button_controller.js +20 -0
  56. data/app/javascript/controllers/index.js +6 -0
  57. data/app/javascript/controllers/theme_controller.js +23 -0
  58. data/app/views/blocks/_sidebar.html.erb +31 -0
  59. data/app/views/blocks/_toc.html.erb +29 -0
  60. data/app/views/blocks/examples/dashboard-01.html.erb +180 -0
  61. data/app/views/blocks/examples/dashboard-02.html.erb +190 -0
  62. data/app/views/blocks/examples/dashboard-03.html.erb +210 -0
  63. data/app/views/blocks/examples/settings-01.html.erb +220 -0
  64. data/app/views/blocks/examples/settings-02.html.erb +231 -0
  65. data/app/views/blocks/examples/settings-03.html.erb +340 -0
  66. data/app/views/blocks/index.html.erb +65 -0
  67. data/app/views/docs/_sidebar.html.erb +47 -0
  68. data/app/views/docs/_toc.html.erb +19 -0
  69. data/app/views/docs/about.html.erb +68 -0
  70. data/app/views/docs/components/accordion.html.erb +196 -0
  71. data/app/views/docs/components/alert.html.erb +272 -0
  72. data/app/views/docs/components/alert_dialog.html.erb +232 -0
  73. data/app/views/docs/components/avatar.html.erb +207 -0
  74. data/app/views/docs/components/badge.html.erb +145 -0
  75. data/app/views/docs/components/breadcrumb.html.erb +264 -0
  76. data/app/views/docs/components/button.html.erb +229 -0
  77. data/app/views/docs/components/card.html.erb +378 -0
  78. data/app/views/docs/components/checkbox.html.erb +212 -0
  79. data/app/views/docs/components/collapsible.html.erb +252 -0
  80. data/app/views/docs/components/dialog.html.erb +323 -0
  81. data/app/views/docs/components/dropdown_menu.html.erb +289 -0
  82. data/app/views/docs/components/hover_card.html.erb +220 -0
  83. data/app/views/docs/components/input.html.erb +254 -0
  84. data/app/views/docs/components/label.html.erb +128 -0
  85. data/app/views/docs/components/main.html.erb +352 -0
  86. data/app/views/docs/components/navbar.html.erb +394 -0
  87. data/app/views/docs/components/navigation_menu.html.erb +226 -0
  88. data/app/views/docs/components/popover.html.erb +267 -0
  89. data/app/views/docs/components/progress.html.erb +107 -0
  90. data/app/views/docs/components/radio_group.html.erb +209 -0
  91. data/app/views/docs/components/select.html.erb +260 -0
  92. data/app/views/docs/components/separator.html.erb +162 -0
  93. data/app/views/docs/components/sheet.html.erb +270 -0
  94. data/app/views/docs/components/sidebar.html.erb +597 -0
  95. data/app/views/docs/components/skeleton.html.erb +150 -0
  96. data/app/views/docs/components/slider.html.erb +218 -0
  97. data/app/views/docs/components/spinner.html.erb +132 -0
  98. data/app/views/docs/components/switch.html.erb +148 -0
  99. data/app/views/docs/components/table.html.erb +259 -0
  100. data/app/views/docs/components/tabs.html.erb +225 -0
  101. data/app/views/docs/components/textarea.html.erb +239 -0
  102. data/app/views/docs/components/theme_toggle.html.erb +135 -0
  103. data/app/views/docs/components/toast.html.erb +205 -0
  104. data/app/views/docs/components/toaster.html.erb +227 -0
  105. data/app/views/docs/components/toggle.html.erb +154 -0
  106. data/app/views/docs/components/tooltip.html.erb +216 -0
  107. data/app/views/docs/components/typography.html.erb +180 -0
  108. data/app/views/docs/index.html.erb +143 -0
  109. data/app/views/docs/installation.html.erb +155 -0
  110. data/app/views/docs/simple_test.html.erb +13 -0
  111. data/app/views/docs/test_accordion.html.erb +14 -0
  112. data/app/views/docs/usage.html.erb +272 -0
  113. data/app/views/layouts/application.html.erb +107 -0
  114. data/app/views/layouts/backdrop.html.erb +77 -0
  115. data/app/views/shared/_app_navbar.html.erb +240 -0
  116. data/app/views/shared/_navbar.html.erb +69 -0
  117. data/app/views/showcase/v2/_components_grid.html.erb +38 -0
  118. data/app/views/showcase/v2/_features.html.erb +59 -0
  119. data/app/views/showcase/v2/_forms.html.erb +195 -0
  120. data/app/views/showcase/v2/_hero.html.erb +55 -0
  121. data/app/views/showcase/v2/_metrics.html.erb +107 -0
  122. data/app/views/showcase/v2.html.erb +18 -0
  123. data/lib/m9sh/version.rb +1 -1
  124. data/m9sh.gemspec +1 -1
  125. metadata +120 -1
@@ -0,0 +1,51 @@
1
+ class BlocksController < ApplicationController
2
+ layout "backdrop"
3
+ before_action :setup_layout
4
+
5
+ BLOCKS = %w[
6
+ dashboard-01
7
+ dashboard-02
8
+ dashboard-03
9
+ settings-01
10
+ settings-02
11
+ settings-03
12
+ ].freeze
13
+
14
+ BLOCK_DESCRIPTIONS = {
15
+ 'dashboard-01' => 'Comprehensive dashboard with stats cards, charts, and recent activity.',
16
+ 'dashboard-02' => 'Analytics-focused dashboard with metrics, progress indicators, and activity feed.',
17
+ 'dashboard-03' => 'E-commerce dashboard with orders, inventory, and customer insights.',
18
+ 'settings-01' => 'Profile and account settings with form inputs, avatar upload, and preferences.',
19
+ 'settings-02' => 'Appearance and display settings with theme selection and layout options.',
20
+ 'settings-03' => 'Notifications and integrations with connected accounts and API access.'
21
+ }.freeze
22
+
23
+ BLOCK_CATEGORIES = {
24
+ 'Dashboard' => %w[dashboard-01 dashboard-02 dashboard-03],
25
+ 'Settings' => %w[settings-01 settings-02 settings-03]
26
+ }.freeze
27
+
28
+ def index
29
+ # Blocks landing page showing all available blocks
30
+ @current_page = 'index'
31
+ end
32
+
33
+ def show
34
+ @block = params[:name]
35
+
36
+ unless BLOCKS.include?(@block)
37
+ redirect_to blocks_path, alert: "Block not found"
38
+ return
39
+ end
40
+
41
+ render "blocks/examples/#{@block}"
42
+ rescue ActionView::MissingTemplate
43
+ redirect_to blocks_path, alert: "Block #{@block} is not yet available"
44
+ end
45
+
46
+ private
47
+
48
+ def setup_layout
49
+ @show_blocks_sidebars = true
50
+ end
51
+ end
@@ -0,0 +1,162 @@
1
+ class DocsController < ApplicationController
2
+ layout "backdrop"
3
+ before_action :setup_layout
4
+
5
+ GETTING_STARTED_PAGES = %w[
6
+ installation usage about
7
+ ].freeze
8
+
9
+ COMPONENTS = %w[
10
+ accordion alert alert_dialog avatar badge breadcrumb button card
11
+ checkbox collapsible dialog dropdown_menu hover_card input label
12
+ main navbar navigation_menu popover progress radio_group select separator sheet sidebar
13
+ skeleton slider spinner switch table tabs textarea theme_toggle toast
14
+ toaster tooltip toggle typography
15
+ ].freeze
16
+
17
+ COMPONENT_DESCRIPTIONS = {
18
+ 'accordion' => 'A vertically stacked set of interactive headings that each reveal a section of content.',
19
+ 'alert' => 'Displays a callout for user attention.',
20
+ 'alert_dialog' => 'A modal dialog that interrupts the user with important content and expects a response.',
21
+ 'avatar' => 'An image element with a fallback for representing the user.',
22
+ 'badge' => 'Displays a badge or a component that looks like a badge.',
23
+ 'breadcrumb' => 'Displays the path to the current resource using a hierarchy of links.',
24
+ 'button' => 'Displays a button or a component that looks like a button.',
25
+ 'card' => 'Displays a card with header, content, and footer.',
26
+ 'checkbox' => 'A control that allows the user to toggle between checked and not checked.',
27
+ 'collapsible' => 'An interactive component which expands/collapses a panel.',
28
+ 'dialog' => 'A window overlaid on either the primary window or another dialog window.',
29
+ 'dropdown_menu' => 'Displays a menu to the user — such as a set of actions or functions — triggered by a button.',
30
+ 'hover_card' => 'For sighted users to preview content available behind a link.',
31
+ 'input' => 'Displays a form input field or a component that looks like an input field.',
32
+ 'label' => 'Renders an accessible label associated with controls.',
33
+ 'main' => 'A container component that creates a rounded, elevated content area with shadow effects.',
34
+ 'navbar' => 'A responsive navigation header component with logo, navigation links, and action buttons.',
35
+ 'navigation_menu' => 'A collection of links for navigating websites.',
36
+ 'popover' => 'Displays rich content in a portal, triggered by a button.',
37
+ 'progress' => 'Displays an indicator showing the completion progress of a task.',
38
+ 'radio_group' => 'A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.',
39
+ 'select' => 'Displays a list of options for the user to pick from—triggered by a button.',
40
+ 'separator' => 'Visually or semantically separates content.',
41
+ 'sheet' => 'Extends the Dialog component to display content that complements the main content of the screen.',
42
+ 'sidebar' => 'A composable, themeable sidebar component with collapsible navigation and responsive behavior.',
43
+ 'skeleton' => 'Use to show a placeholder while content is loading.',
44
+ 'slider' => 'An input where the user selects a value from within a given range.',
45
+ 'spinner' => 'An indicator that can be used to show a loading state.',
46
+ 'switch' => 'A control that allows the user to toggle between checked and not checked.',
47
+ 'table' => 'A responsive table component.',
48
+ 'tabs' => 'A set of layered sections of content—known as tab panels—that are displayed one at a time.',
49
+ 'textarea' => 'Displays a form textarea or a component that looks like a textarea.',
50
+ 'theme_toggle' => 'A toggle button to switch between light and dark themes.',
51
+ 'toast' => 'A succinct message that is displayed temporarily.',
52
+ 'toaster' => 'The provider component for the toast notification system.',
53
+ 'tooltip' => 'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',
54
+ 'toggle' => 'A two-state button that can be either on or off.',
55
+ 'typography' => 'Styles for headings, paragraphs, lists, and other text elements.'
56
+ }.freeze
57
+
58
+ def index
59
+ # Introduction page
60
+ @current_page = 'introduction'
61
+ end
62
+
63
+ def installation
64
+ @current_page = 'installation'
65
+ end
66
+
67
+ def usage
68
+ @current_page = 'usage'
69
+
70
+ @short_syntax_code = %q{<%# Button %>
71
+ <%= ui_button(variant: :outline, class: "w-full") do %>
72
+ Click me
73
+ <% end %>
74
+
75
+ <%# Badge %>
76
+ <%= ui_badge(variant: :secondary) do %>
77
+ New
78
+ <% end %>
79
+
80
+ <%# Input %>
81
+ <%= ui_input(type: "email", placeholder: "Enter your email") %>
82
+
83
+ <%# Card with slots %>
84
+ <%= ui_card(class: "w-full") do |card| %>
85
+ <% card.with_header do %>
86
+ <h3 class="font-semibold">Card Title</h3>
87
+ <% end %>
88
+ <% card.with_body do %>
89
+ Card content goes here.
90
+ <% end %>
91
+ <% end %>}
92
+
93
+ @backwards_syntax_code = %q{<%# These are aliases to the ui_* methods %>
94
+ <%= render_button(variant: :outline) do %>
95
+ Click me
96
+ <% end %>
97
+
98
+ <%= render_badge(variant: :secondary) do %>
99
+ New
100
+ <% end %>
101
+
102
+ <%= render_input(type: "email", placeholder: "Enter your email") %>}
103
+
104
+ @original_syntax_code = %q{<%# Full ViewComponent syntax %>
105
+ <%= render M9sh::ButtonComponent.new(variant: :outline, class: "w-full") do %>
106
+ Click me
107
+ <% end %>
108
+
109
+ <%= render M9sh::BadgeComponent.new(variant: :secondary) do %>
110
+ New
111
+ <% end %>
112
+
113
+ <%= render M9sh::InputComponent.new(type: "email", placeholder: "Enter your email") %>}
114
+
115
+ @comparison_backwards_code = %q{<%= render_button(variant: :outline, class: "w-full") do %>
116
+ Submit
117
+ <% end %>}
118
+
119
+ @comparison_original_code = %q{<%= render M9sh::ButtonComponent.new(variant: :outline, class: "w-full") do %>
120
+ Submit
121
+ <% end %>}
122
+
123
+ @inline_short_example = %q{<%= ui_button(variant: :outline, class: "w-full") { "Submit" } %>}
124
+ @inline_block_example = %q{<%= ui_button(variant: :outline) { "Click me" } %>}
125
+ @custom_class_example = %q{<%= ui_button(variant: :outline, class: "w-full mt-4") { "Full Width" } %>}
126
+ end
127
+
128
+ def about
129
+ @current_page = 'about'
130
+ end
131
+
132
+ def test_accordion
133
+ # Test page for accordion
134
+ end
135
+
136
+ def debug_accordion
137
+ # Debug page for accordion
138
+ end
139
+
140
+ def simple_test
141
+ # Simple test page
142
+ end
143
+
144
+ def component
145
+ @component = params[:name]
146
+
147
+ unless COMPONENTS.include?(@component)
148
+ redirect_to docs_path, alert: "Component not found"
149
+ return
150
+ end
151
+
152
+ render "docs/components/#{@component}"
153
+ rescue ActionView::MissingTemplate
154
+ redirect_to docs_path, alert: "Documentation for #{@component} is not yet available"
155
+ end
156
+
157
+ private
158
+
159
+ def setup_layout
160
+ @show_docs_sidebars = true
161
+ end
162
+ end
@@ -0,0 +1,42 @@
1
+ class ShowcaseController < ApplicationController
2
+ def index
3
+ # Main showcase page with all components
4
+ end
5
+
6
+ def v2
7
+ # New showcase v2 - uses application layout with m9sh components
8
+ @components = DocsController::COMPONENTS
9
+ @descriptions = DocsController::COMPONENT_DESCRIPTIONS
10
+ end
11
+
12
+ def components
13
+ # Components gallery page
14
+ @components = DocsController::COMPONENTS
15
+ @descriptions = DocsController::COMPONENT_DESCRIPTIONS
16
+ render layout: false
17
+ end
18
+
19
+ def buttons
20
+ # Button component examples
21
+ end
22
+
23
+ def cards
24
+ # Card component examples
25
+ end
26
+
27
+ def forms
28
+ # Form component examples
29
+ end
30
+
31
+ def accordion
32
+ # Accordion component examples
33
+ end
34
+
35
+ def dialog
36
+ # Dialog component examples
37
+ end
38
+
39
+ def tabs
40
+ # Tabs component examples
41
+ end
42
+ end
@@ -0,0 +1,343 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlocksHelper
4
+ # Extract code from the current view file between markers
5
+ def extract_block_code(start_marker, end_marker)
6
+ view_file = caller_locations.find { |loc| loc.path.include?('app/views/blocks/examples') }&.path
7
+ return "" unless view_file && File.exist?(view_file)
8
+
9
+ content = File.read(view_file)
10
+ start_index = content.index(start_marker)
11
+ end_index = content.index(end_marker, start_index) if start_index
12
+
13
+ return "" unless start_index && end_index
14
+
15
+ # Extract the code between markers and clean it up
16
+ code = content[start_index + start_marker.length...end_index]
17
+
18
+ # Remove leading whitespace while preserving relative indentation
19
+ lines = code.lines
20
+ return "" if lines.empty?
21
+
22
+ # Find minimum indentation (excluding empty lines)
23
+ min_indent = lines.reject { |line| line.strip.empty? }
24
+ .map { |line| line[/^\s*/].length }
25
+ .min || 0
26
+
27
+ # Remove the minimum indentation from all lines
28
+ lines.map { |line| line.strip.empty? ? line : line[min_indent..] || line }.join.strip
29
+ end
30
+ def dashboard_01_code
31
+ <<~'CODE'
32
+ <div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
33
+ <!-- Stats Cards -->
34
+ <%= render M9sh::CardComponent.new do |card| %>
35
+ <% card.with_header(class: "flex flex-row items-center justify-between pb-2") do %>
36
+ <h3 class="text-sm font-medium text-muted-foreground">Total Revenue</h3>
37
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
38
+ <path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
39
+ </svg>
40
+ <% end %>
41
+ <% card.with_body do %>
42
+ <div class="text-2xl font-bold">$45,231.89</div>
43
+ <p class="text-xs text-muted-foreground">+20.1% from last month</p>
44
+ <% end %>
45
+ <% end %>
46
+
47
+ <%= render M9sh::CardComponent.new do |card| %>
48
+ <% card.with_header(class: "flex flex-row items-center justify-between pb-2") do %>
49
+ <h3 class="text-sm font-medium text-muted-foreground">Subscriptions</h3>
50
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
51
+ <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
52
+ <circle cx="9" cy="7" r="4" />
53
+ <path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
54
+ </svg>
55
+ <% end %>
56
+ <% card.with_body do %>
57
+ <div class="text-2xl font-bold">+2350</div>
58
+ <p class="text-xs text-muted-foreground">+180.1% from last month</p>
59
+ <% end %>
60
+ <% end %>
61
+
62
+ <%= render M9sh::CardComponent.new do |card| %>
63
+ <% card.with_header(class: "flex flex-row items-center justify-between pb-2") do %>
64
+ <h3 class="text-sm font-medium text-muted-foreground">Sales</h3>
65
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
66
+ <rect width="20" height="14" x="2" y="5" rx="2" />
67
+ <path d="M2 10h20" />
68
+ </svg>
69
+ <% end %>
70
+ <% card.with_body do %>
71
+ <div class="text-2xl font-bold">+12,234</div>
72
+ <p class="text-xs text-muted-foreground">+19% from last month</p>
73
+ <% end %>
74
+ <% end %>
75
+
76
+ <%= render M9sh::CardComponent.new do |card| %>
77
+ <% card.with_header(class: "flex flex-row items-center justify-between pb-2") do %>
78
+ <h3 class="text-sm font-medium text-muted-foreground">Active Now</h3>
79
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
80
+ <path d="M22 12h-4l-3 9L9 3l-3 9H2" />
81
+ </svg>
82
+ <% end %>
83
+ <% card.with_body do %>
84
+ <div class="text-2xl font-bold">+573</div>
85
+ <p class="text-xs text-muted-foreground">+201 since last hour</p>
86
+ <% end %>
87
+ <% end %>
88
+ </div>
89
+ CODE
90
+ end
91
+
92
+ def dashboard_02_code
93
+ <<~'CODE'
94
+ <!-- Top Metrics Row -->
95
+ <div class="grid gap-4 md:grid-cols-3">
96
+ <%= render M9sh::CardComponent.new do |card| %>
97
+ <% card.with_header do |header| %>
98
+ <% header.with_title { "Total Users" } %>
99
+ <% header.with_description { "Active users this month" } %>
100
+ <% end %>
101
+ <% card.with_body do %>
102
+ <div class="text-3xl font-bold">12,543</div>
103
+ <p class="text-xs text-muted-foreground mt-2">
104
+ <%= render M9sh::BadgeComponent.new(variant: :default) { "+12.5%" } %>
105
+ from last month
106
+ </p>
107
+ <% end %>
108
+ <% end %>
109
+ <!-- Add more metric cards... -->
110
+ </div>
111
+
112
+ <!-- Activity Feed and Goals -->
113
+ <div class="grid gap-4 md:grid-cols-2">
114
+ <!-- Activity Feed -->
115
+ <%= render M9sh::CardComponent.new do |card| %>
116
+ <% card.with_header do |header| %>
117
+ <% header.with_title { "Recent Activity" } %>
118
+ <% end %>
119
+ <% card.with_body do %>
120
+ <!-- Activity items with avatars and badges -->
121
+ <% end %>
122
+ <% end %>
123
+
124
+ <!-- Goals Progress -->
125
+ <%= render M9sh::CardComponent.new do |card| %>
126
+ <% card.with_header do |header| %>
127
+ <% header.with_title { "Monthly Goals" } %>
128
+ <% end %>
129
+ <% card.with_body do %>
130
+ <%= render M9sh::ProgressComponent.new(value: 84.2) %>
131
+ <!-- More progress indicators -->
132
+ <% end %>
133
+ <% end %>
134
+ </div>
135
+ CODE
136
+ end
137
+
138
+ def dashboard_03_code
139
+ <<~'CODE'
140
+ <!-- Quick Stats -->
141
+ <div class="grid gap-4 md:grid-cols-4">
142
+ <%= render M9sh::CardComponent.new do |card| %>
143
+ <% card.with_body do %>
144
+ <div class="flex items-center justify-between">
145
+ <div>
146
+ <p class="text-sm font-medium text-muted-foreground">Total Sales</p>
147
+ <p class="text-2xl font-bold mt-1">$54,239</p>
148
+ </div>
149
+ <!-- Icon SVG -->
150
+ </div>
151
+ <% end %>
152
+ <% end %>
153
+ <!-- Add more stat cards... -->
154
+ </div>
155
+
156
+ <!-- Recent Orders and Top Products -->
157
+ <div class="grid gap-4 md:grid-cols-2">
158
+ <!-- Orders Table -->
159
+ <%= render M9sh::CardComponent.new do |card| %>
160
+ <% card.with_header do |header| %>
161
+ <% header.with_title { "Recent Orders" } %>
162
+ <% end %>
163
+ <% card.with_body do %>
164
+ <%= render M9sh::TableComponent.new do |table| %>
165
+ <!-- Table content -->
166
+ <% end %>
167
+ <% end %>
168
+ <% end %>
169
+
170
+ <!-- Top Products -->
171
+ <%= render M9sh::CardComponent.new do |card| %>
172
+ <% card.with_header do |header| %>
173
+ <% header.with_title { "Top Products" } %>
174
+ <% end %>
175
+ <% card.with_body do %>
176
+ <!-- Product list with badges -->
177
+ <% end %>
178
+ <% end %>
179
+ </div>
180
+ CODE
181
+ end
182
+
183
+ def settings_01_code
184
+ <<~'CODE'
185
+ <!-- Profile Section -->
186
+ <%= render M9sh::CardComponent.new do |card| %>
187
+ <% card.with_header do |header| %>
188
+ <% header.with_title { "Profile" } %>
189
+ <% header.with_description { "Update your personal information and profile picture." } %>
190
+ <% end %>
191
+ <% card.with_body do %>
192
+ <div class="space-y-6">
193
+ <!-- Avatar Upload -->
194
+ <div class="flex items-center gap-4">
195
+ <%= render M9sh::AvatarComponent.new(fallback: "JD", class: "h-20 w-20") %>
196
+ <%= render M9sh::ButtonComponent.new(variant: :outline, size: :sm) { "Change Avatar" } %>
197
+ </div>
198
+
199
+ <!-- Form Fields -->
200
+ <div class="grid gap-4 md:grid-cols-2">
201
+ <div class="space-y-2">
202
+ <%= render M9sh::LabelComponent.new(for: "first-name") { "First Name" } %>
203
+ <%= render M9sh::InputComponent.new(id: "first-name", placeholder: "John") %>
204
+ </div>
205
+ <div class="space-y-2">
206
+ <%= render M9sh::LabelComponent.new(for: "last-name") { "Last Name" } %>
207
+ <%= render M9sh::InputComponent.new(id: "last-name", placeholder: "Doe") %>
208
+ </div>
209
+ </div>
210
+
211
+ <div class="space-y-2">
212
+ <%= render M9sh::LabelComponent.new(for: "email") { "Email" } %>
213
+ <%= render M9sh::InputComponent.new(id: "email", type: :email) %>
214
+ </div>
215
+ </div>
216
+ <% end %>
217
+ <% card.with_footer do %>
218
+ <%= render M9sh::ButtonComponent.new { "Save Changes" } %>
219
+ <% end %>
220
+ <% end %>
221
+
222
+ <!-- Preferences with Switches -->
223
+ <%= render M9sh::CardComponent.new do |card| %>
224
+ <% card.with_header do |header| %>
225
+ <% header.with_title { "Preferences" } %>
226
+ <% end %>
227
+ <% card.with_body do %>
228
+ <div class="flex items-center justify-between">
229
+ <div class="space-y-0.5">
230
+ <%= render M9sh::LabelComponent.new(for: "marketing") { "Marketing Emails" } %>
231
+ <p class="text-sm text-muted-foreground">Receive emails about new products.</p>
232
+ </div>
233
+ <%= render M9sh::SwitchComponent.new(id: "marketing") %>
234
+ </div>
235
+ <% end %>
236
+ <% end %>
237
+ CODE
238
+ end
239
+
240
+ def settings_02_code
241
+ <<~'CODE'
242
+ <!-- Theme Selection -->
243
+ <%= render M9sh::CardComponent.new do |card| %>
244
+ <% card.with_header do |header| %>
245
+ <% header.with_title { "Theme" } %>
246
+ <% header.with_description { "Select your preferred color theme." } %>
247
+ <% end %>
248
+ <% card.with_body do %>
249
+ <%= render M9sh::RadioGroupComponent.new(name: "theme", default_value: "light") do |group| %>
250
+ <div class="grid gap-4 md:grid-cols-3">
251
+ <label class="border rounded-lg p-4 cursor-pointer hover:bg-accent">
252
+ <%= group.radio_item(value: "light", id: "theme-light") %>
253
+ <p class="font-medium">Light</p>
254
+ <p class="text-sm text-muted-foreground">Clean and bright</p>
255
+ </label>
256
+ <label class="border rounded-lg p-4 cursor-pointer hover:bg-accent">
257
+ <%= group.radio_item(value: "dark", id: "theme-dark") %>
258
+ <p class="font-medium">Dark</p>
259
+ <p class="text-sm text-muted-foreground">Easy on the eyes</p>
260
+ </label>
261
+ <label class="border rounded-lg p-4 cursor-pointer hover:bg-accent">
262
+ <%= group.radio_item(value: "system", id: "theme-system") %>
263
+ <p class="font-medium">System</p>
264
+ <p class="text-sm text-muted-foreground">Use system preference</p>
265
+ </label>
266
+ </div>
267
+ <% end %>
268
+ <% end %>
269
+ <% end %>
270
+
271
+ <!-- Layout Options -->
272
+ <%= render M9sh::CardComponent.new do |card| %>
273
+ <% card.with_header do |header| %>
274
+ <% header.with_title { "Layout" } %>
275
+ <% end %>
276
+ <% card.with_body do %>
277
+ <div class="space-y-4">
278
+ <div class="flex items-start space-x-3">
279
+ <%= render M9sh::CheckboxComponent.new(id: "compact") %>
280
+ <div class="space-y-1">
281
+ <%= render M9sh::LabelComponent.new(for: "compact") { "Compact Mode" } %>
282
+ <p class="text-sm text-muted-foreground">Reduce spacing between elements.</p>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ <% end %>
287
+ <% card.with_footer do %>
288
+ <%= render M9sh::ButtonComponent.new { "Save Preferences" } %>
289
+ <% end %>
290
+ <% end %>
291
+ CODE
292
+ end
293
+
294
+ def settings_03_code
295
+ <<~'CODE'
296
+ <%= render M9sh::TabsComponent.new(default_value: "notifications") do |tabs| %>
297
+ <% tabs.with_tabs_list do %>
298
+ <%= tabs.tabs_trigger(value: "notifications") { "Notifications" } %>
299
+ <%= tabs.tabs_trigger(value: "integrations") { "Integrations" } %>
300
+ <% end %>
301
+
302
+ <!-- Notifications Tab -->
303
+ <% tabs.with_tabs_content(value: "notifications") do %>
304
+ <%= render M9sh::CardComponent.new do |card| %>
305
+ <% card.with_header do |header| %>
306
+ <% header.with_title { "Email Notifications" } %>
307
+ <% end %>
308
+ <% card.with_body do %>
309
+ <div class="flex items-center justify-between">
310
+ <div class="space-y-0.5">
311
+ <%= render M9sh::LabelComponent.new(for: "comments") { "Comments" } %>
312
+ <p class="text-sm text-muted-foreground">Notifications about comments.</p>
313
+ </div>
314
+ <%= render M9sh::SwitchComponent.new(id: "comments", checked: true) %>
315
+ </div>
316
+ <% end %>
317
+ <% end %>
318
+ <% end %>
319
+
320
+ <!-- Integrations Tab -->
321
+ <% tabs.with_tabs_content(value: "integrations") do %>
322
+ <%= render M9sh::CardComponent.new do |card| %>
323
+ <% card.with_header do |header| %>
324
+ <% header.with_title { "Connected Accounts" } %>
325
+ <% end %>
326
+ <% card.with_body do %>
327
+ <div class="flex items-center justify-between">
328
+ <div class="flex items-center gap-3">
329
+ <div class="h-10 w-10 rounded-full bg-muted"></div>
330
+ <div>
331
+ <p class="font-medium">GitHub</p>
332
+ <%= render M9sh::BadgeComponent.new { "Connected" } %>
333
+ </div>
334
+ </div>
335
+ <%= render M9sh::ButtonComponent.new(variant: :outline, size: :sm) { "Disconnect" } %>
336
+ </div>
337
+ <% end %>
338
+ <% end %>
339
+ <% end %>
340
+ <% end %>
341
+ CODE
342
+ end
343
+ end