pike13-cli 0.1.4

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +196 -0
  3. data/CONTRIBUTING.md +96 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +896 -0
  6. data/bin/pike13 +6 -0
  7. data/completions/_pike13 +229 -0
  8. data/completions/pike13.bash +112 -0
  9. data/lib/pike13/cli/commands/account/business.rb +22 -0
  10. data/lib/pike13/cli/commands/account/confirmation.rb +21 -0
  11. data/lib/pike13/cli/commands/account/password.rb +21 -0
  12. data/lib/pike13/cli/commands/account/person.rb +22 -0
  13. data/lib/pike13/cli/commands/account.rb +33 -0
  14. data/lib/pike13/cli/commands/base.rb +350 -0
  15. data/lib/pike13/cli/commands/concerns/formattable_command.rb +48 -0
  16. data/lib/pike13/cli/commands/concerns/reporting_query.rb +59 -0
  17. data/lib/pike13/cli/commands/desk/appointment.rb +58 -0
  18. data/lib/pike13/cli/commands/desk/booking.rb +56 -0
  19. data/lib/pike13/cli/commands/desk/business.rb +20 -0
  20. data/lib/pike13/cli/commands/desk/custom_field.rb +27 -0
  21. data/lib/pike13/cli/commands/desk/event.rb +32 -0
  22. data/lib/pike13/cli/commands/desk/event_occurrence.rb +64 -0
  23. data/lib/pike13/cli/commands/desk/event_occurrence_note.rb +82 -0
  24. data/lib/pike13/cli/commands/desk/event_occurrence_visit.rb +26 -0
  25. data/lib/pike13/cli/commands/desk/event_occurrence_waitlist_entry.rb +26 -0
  26. data/lib/pike13/cli/commands/desk/form_of_payment.rb +75 -0
  27. data/lib/pike13/cli/commands/desk/invoice.rb +29 -0
  28. data/lib/pike13/cli/commands/desk/location.rb +29 -0
  29. data/lib/pike13/cli/commands/desk/make_up.rb +51 -0
  30. data/lib/pike13/cli/commands/desk/note.rb +71 -0
  31. data/lib/pike13/cli/commands/desk/pack.rb +20 -0
  32. data/lib/pike13/cli/commands/desk/pack_product.rb +101 -0
  33. data/lib/pike13/cli/commands/desk/payment.rb +44 -0
  34. data/lib/pike13/cli/commands/desk/person.rb +98 -0
  35. data/lib/pike13/cli/commands/desk/person_plan.rb +27 -0
  36. data/lib/pike13/cli/commands/desk/person_visit.rb +27 -0
  37. data/lib/pike13/cli/commands/desk/person_waitlist_entry.rb +27 -0
  38. data/lib/pike13/cli/commands/desk/person_waiver.rb +27 -0
  39. data/lib/pike13/cli/commands/desk/plan.rb +29 -0
  40. data/lib/pike13/cli/commands/desk/plan_product.rb +35 -0
  41. data/lib/pike13/cli/commands/desk/punch.rb +20 -0
  42. data/lib/pike13/cli/commands/desk/refund.rb +31 -0
  43. data/lib/pike13/cli/commands/desk/revenue_category.rb +36 -0
  44. data/lib/pike13/cli/commands/desk/sales_tax.rb +36 -0
  45. data/lib/pike13/cli/commands/desk/service.rb +29 -0
  46. data/lib/pike13/cli/commands/desk/staff_member.rb +35 -0
  47. data/lib/pike13/cli/commands/desk/visit.rb +36 -0
  48. data/lib/pike13/cli/commands/desk/waitlist_entry.rb +69 -0
  49. data/lib/pike13/cli/commands/desk.rb +172 -0
  50. data/lib/pike13/cli/commands/front/appointment.rb +31 -0
  51. data/lib/pike13/cli/commands/front/booking.rb +56 -0
  52. data/lib/pike13/cli/commands/front/branding.rb +17 -0
  53. data/lib/pike13/cli/commands/front/business.rb +17 -0
  54. data/lib/pike13/cli/commands/front/event.rb +22 -0
  55. data/lib/pike13/cli/commands/front/event_occurrence.rb +35 -0
  56. data/lib/pike13/cli/commands/front/event_occurrence_note.rb +35 -0
  57. data/lib/pike13/cli/commands/front/event_occurrence_waitlist_eligibility.rb +27 -0
  58. data/lib/pike13/cli/commands/front/form_of_payment.rb +83 -0
  59. data/lib/pike13/cli/commands/front/location.rb +22 -0
  60. data/lib/pike13/cli/commands/front/person.rb +17 -0
  61. data/lib/pike13/cli/commands/front/person_plan.rb +26 -0
  62. data/lib/pike13/cli/commands/front/person_visit.rb +26 -0
  63. data/lib/pike13/cli/commands/front/person_waitlist_entry.rb +26 -0
  64. data/lib/pike13/cli/commands/front/person_waiver.rb +26 -0
  65. data/lib/pike13/cli/commands/front/plan_product.rb +31 -0
  66. data/lib/pike13/cli/commands/front/plan_terms.rb +42 -0
  67. data/lib/pike13/cli/commands/front/service.rb +22 -0
  68. data/lib/pike13/cli/commands/front.rb +113 -0
  69. data/lib/pike13/cli/commands/report/clients.rb +27 -0
  70. data/lib/pike13/cli/commands/report/enrollments.rb +27 -0
  71. data/lib/pike13/cli/commands/report/event_occurrence_staff_members.rb +31 -0
  72. data/lib/pike13/cli/commands/report/event_occurrences.rb +31 -0
  73. data/lib/pike13/cli/commands/report/invoice_item_transactions.rb +31 -0
  74. data/lib/pike13/cli/commands/report/invoice_items.rb +31 -0
  75. data/lib/pike13/cli/commands/report/invoices.rb +27 -0
  76. data/lib/pike13/cli/commands/report/monthly_business_metrics.rb +31 -0
  77. data/lib/pike13/cli/commands/report/pays.rb +27 -0
  78. data/lib/pike13/cli/commands/report/person_plans.rb +31 -0
  79. data/lib/pike13/cli/commands/report/staff_members.rb +31 -0
  80. data/lib/pike13/cli/commands/report/transactions.rb +27 -0
  81. data/lib/pike13/cli/commands/report.rb +77 -0
  82. data/lib/pike13/cli/config.rb +26 -0
  83. data/lib/pike13/cli/formatter.rb +142 -0
  84. data/lib/pike13/cli/progress.rb +85 -0
  85. data/lib/pike13/cli/thor_nested_subcommand.rb +37 -0
  86. data/lib/pike13/cli/version.rb +7 -0
  87. data/lib/pike13/cli.rb +70 -0
  88. metadata +187 -0
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../thor_nested_subcommand"
4
+
5
+ module Pike13
6
+ module CLI
7
+ module Commands
8
+ class Base < Thor
9
+ include ThorNestedSubcommand
10
+
11
+ # Inherit verbose and quiet options from parent Runner
12
+ class_option :verbose, type: :boolean, aliases: "-v", desc: "Verbose output"
13
+ class_option :quiet, type: :boolean, aliases: "-q", desc: "Quiet mode (errors only)"
14
+
15
+ # Auto-generate base_usage from class name
16
+ def self.base_usage
17
+ # Convert class name like Pike13::CLI::Commands::Report::Clients
18
+ # to "report clients"
19
+ namespace_parts = name.split("::")[3..] # Skip Pike13::CLI::Commands
20
+ if namespace_parts && namespace_parts.length > 1
21
+ # For nested classes like Report::Clients
22
+ namespace_parts.map(&:downcase).join(" ")
23
+ elsif namespace_parts&.length == 1
24
+ # For top-level namespace classes
25
+ namespace_parts.first.downcase
26
+ else
27
+ ""
28
+ end
29
+ end
30
+
31
+ # Helper to add format options to commands
32
+ def self.format_options
33
+ option :format, type: :string, default: "json", desc: "Output format (json, table, csv)"
34
+ option :compact, type: :boolean, default: false, desc: "Compact JSON output"
35
+ option :color, type: :boolean, default: false, desc: "Colorize table output"
36
+ option :progress, type: :boolean, default: false, desc: "Show progress indicator"
37
+ end
38
+
39
+ private
40
+
41
+ def output(data)
42
+ return if options[:quiet]
43
+
44
+ Formatter.output(
45
+ data,
46
+ format: options[:format],
47
+ compact: options[:compact],
48
+ color: options[:color]
49
+ )
50
+ end
51
+
52
+ def handle_error
53
+ setup_verbose_mode if options && options[:verbose]
54
+ yield
55
+ rescue Pike13::AuthenticationError => e
56
+ handle_authentication_error(e)
57
+ rescue Pike13::NotFoundError => e
58
+ handle_not_found_error(e)
59
+ rescue Pike13::ValidationError => e
60
+ handle_validation_error(e)
61
+ rescue Pike13::RateLimitError => e
62
+ handle_rate_limit_error(e)
63
+ rescue Pike13::ServerError => e
64
+ handle_server_error(e)
65
+ rescue Pike13::BadRequestError => e
66
+ handle_bad_request_error(e)
67
+ rescue Pike13::ConnectionError => e
68
+ handle_connection_error(e)
69
+ rescue Pike13::APIError => e
70
+ handle_generic_api_error(e)
71
+ rescue StandardError => e
72
+ handle_generic_error(e)
73
+ end
74
+
75
+ def with_progress(message = "Loading", &block)
76
+ require_relative "../progress"
77
+ Progress.run(message, enabled: options[:progress], &block)
78
+ end
79
+
80
+ def warn_message(msg)
81
+ if $stdout.tty?
82
+ require "colorize"
83
+ puts msg.red.bold
84
+ else
85
+ puts msg
86
+ end
87
+ end
88
+
89
+ def success_message(msg)
90
+ return if options[:quiet]
91
+
92
+ if $stdout.tty?
93
+ require "colorize"
94
+ puts msg.green
95
+ else
96
+ puts msg
97
+ end
98
+ end
99
+
100
+ def debug_message(msg)
101
+ return unless options[:verbose]
102
+
103
+ if $stderr.tty?
104
+ require "colorize"
105
+ warn "[DEBUG] #{msg}".light_black
106
+ else
107
+ warn "[DEBUG] #{msg}"
108
+ end
109
+ end
110
+
111
+ def setup_verbose_mode
112
+ return unless options[:verbose]
113
+
114
+ # Enable HTTParty debugging
115
+ begin
116
+ Pike13::HTTPClient.debug_output($stderr) if Pike13::HTTPClient.respond_to?(:debug_output)
117
+ rescue StandardError
118
+ # Silently ignore if debug_output is not available
119
+ end
120
+
121
+ debug_message "Verbose mode enabled - detailed logging active"
122
+ debug_message "Pike13 Base URL: #{ENV.fetch('PIKE13_BASE_URL', nil)}"
123
+ debug_message "Access Token: #{ENV['PIKE13_ACCESS_TOKEN'] ? '[SET]' : '[NOT SET]'}"
124
+ end
125
+
126
+ # Validate that a required option is present
127
+ def validate_required(option_name)
128
+ unless options[option_name]
129
+ warn_message "Error: --#{option_name.to_s.gsub('_', '-')} is required"
130
+ exit 1
131
+ end
132
+ end
133
+
134
+ # Validate date format (YYYY-MM-DD)
135
+ def validate_date_format(date_string, field_name = "date")
136
+ return if date_string.nil?
137
+
138
+ return if date_string =~ /^\d{4}-\d{2}-\d{2}$/
139
+
140
+ warn_message "Error: #{field_name} must be in YYYY-MM-DD format (got: #{date_string})"
141
+ exit 1
142
+ end
143
+
144
+ # Validate that an ID is numeric
145
+ def validate_numeric_id(id, resource_name = "resource")
146
+ return if id.to_s =~ /^\d+$/ || id.to_s =~ /^[a-f0-9-]{36}$/ # Allow UUIDs too
147
+
148
+ warn_message "Error: #{resource_name} ID must be numeric or a valid UUID (got: #{id})"
149
+ exit 1
150
+ end
151
+
152
+ # Error handlers with helpful messages
153
+ def handle_authentication_error(error)
154
+ warn_message "Authentication Error: Invalid or expired access token"
155
+ warn_message ""
156
+ warn_message "Suggestions:"
157
+ warn_message " 1. Verify your PIKE13_ACCESS_TOKEN environment variable is set correctly"
158
+ warn_message " 2. Check if your access token has expired"
159
+ warn_message " 3. Generate a new access token from your Pike13 account settings"
160
+ warn_message ""
161
+ warn_message "Current token: #{ENV['PIKE13_ACCESS_TOKEN'] ? "#{ENV['PIKE13_ACCESS_TOKEN'][0..10]}..." : '[NOT SET]'}"
162
+ show_verbose_error_details(error) if options && options[:verbose]
163
+ exit 1
164
+ end
165
+
166
+ def handle_not_found_error(error)
167
+ warn_message "Not Found: The requested resource does not exist"
168
+ warn_message ""
169
+ warn_message "Suggestions:"
170
+ warn_message " 1. Verify the resource ID is correct"
171
+ warn_message " 2. Check if the resource has been deleted"
172
+ warn_message " 3. Ensure you have permission to access this resource"
173
+ show_verbose_error_details(error) if options && options[:verbose]
174
+ exit 1
175
+ end
176
+
177
+ def handle_validation_error(error)
178
+ warn_message "Validation Error: The request contains invalid data"
179
+ warn_message ""
180
+
181
+ # Show main error message if available
182
+ if error.message && !error.message.empty?
183
+ formatted_message = format_error_message(error.message)
184
+ warn_message "Error details: #{formatted_message}"
185
+ warn_message ""
186
+ end
187
+
188
+ # Show structured response body errors if available
189
+ if error.response_body
190
+ details = format_validation_errors(error.response_body)
191
+ if details && !details.empty?
192
+ warn_message "API response: #{details}"
193
+ warn_message ""
194
+ end
195
+ end
196
+
197
+ # Show specific suggestions for common validation errors
198
+ suggestions = get_validation_error_suggestions(error.message)
199
+ warn_message "Suggestions:"
200
+ suggestions.each { |suggestion| warn_message " #{suggestion}" }
201
+
202
+ show_verbose_error_details(error) if options && options[:verbose]
203
+ exit 1
204
+ end
205
+
206
+ def handle_rate_limit_error(error)
207
+ warn_message "Rate Limit Exceeded: Too many requests"
208
+ warn_message ""
209
+ if error.respond_to?(:rate_limit_reset) && error.rate_limit_reset
210
+ warn_message "Rate limit will reset at: #{error.rate_limit_reset}"
211
+ warn_message ""
212
+ end
213
+ warn_message "Suggestions:"
214
+ warn_message " 1. Wait a few minutes before retrying"
215
+ warn_message " 2. Reduce the frequency of your requests"
216
+ warn_message " 3. Consider batching operations when possible"
217
+ show_verbose_error_details(error) if options && options[:verbose]
218
+ exit 1
219
+ end
220
+
221
+ def handle_server_error(error)
222
+ warn_message "Server Error: Pike13 API is experiencing issues"
223
+ warn_message ""
224
+ warn_message "HTTP Status: #{error.http_status}" if error.http_status
225
+ warn_message ""
226
+ warn_message "Suggestions:"
227
+ warn_message " 1. Wait a few minutes and try again"
228
+ warn_message " 2. Check Pike13 status page for service updates"
229
+ warn_message " 3. Contact Pike13 support if the issue persists"
230
+ show_verbose_error_details(error) if options && options[:verbose]
231
+ exit 1
232
+ end
233
+
234
+ def handle_bad_request_error(error)
235
+ warn_message "Bad Request: The request is malformed or invalid"
236
+ warn_message ""
237
+ if error.response_body
238
+ warn_message "Details: #{error.response_body}"
239
+ warn_message ""
240
+ end
241
+ warn_message "Suggestions:"
242
+ warn_message " 1. Check command syntax and options"
243
+ warn_message " 2. Verify all required parameters are provided"
244
+ warn_message " 3. Run with --verbose flag for more details"
245
+ show_verbose_error_details(error) if options && options[:verbose]
246
+ exit 1
247
+ end
248
+
249
+ def handle_connection_error(error)
250
+ warn_message "Connection Error: Unable to connect to Pike13 API"
251
+ warn_message ""
252
+ warn_message "Error: #{error.message}"
253
+ warn_message ""
254
+ warn_message "Suggestions:"
255
+ warn_message " 1. Check your internet connection"
256
+ warn_message " 2. Verify PIKE13_BASE_URL is set correctly: #{ENV.fetch('PIKE13_BASE_URL', nil)}"
257
+ warn_message " 3. Check if a firewall is blocking the connection"
258
+ warn_message " 4. Verify the Pike13 service is available"
259
+ show_verbose_error_details(error) if options && options[:verbose]
260
+ exit 1
261
+ end
262
+
263
+ def handle_generic_api_error(error)
264
+ warn_message "API Error: #{error.message}"
265
+ warn_message ""
266
+ warn_message "HTTP Status: #{error.http_status}" if error.http_status
267
+ warn_message ""
268
+ warn_message "Run with --verbose flag for more details"
269
+ show_verbose_error_details(error) if options && options[:verbose]
270
+ exit 1
271
+ end
272
+
273
+ def handle_generic_error(error)
274
+ warn_message "Error: #{error.message}"
275
+ warn_message ""
276
+ warn_message "Run with --verbose flag for more details"
277
+ show_verbose_error_details(error) if options && options[:verbose]
278
+ exit 1
279
+ end
280
+
281
+ def show_verbose_error_details(error)
282
+ warn_message ""
283
+ warn_message "=== Verbose Error Details ==="
284
+ warn_message "Error Class: #{error.class}"
285
+ warn_message "HTTP Status: #{error.http_status}" if error.respond_to?(:http_status)
286
+ if error.respond_to?(:response_body) && error.response_body
287
+ warn_message "Response Body: #{error.response_body}"
288
+ end
289
+ if error.backtrace
290
+ warn_message "Backtrace:"
291
+ error.backtrace.first(10).each { |line| warn_message " #{line}" }
292
+ end
293
+ end
294
+
295
+ def format_validation_errors(response_body)
296
+ case response_body
297
+ when Hash
298
+ if response_body["errors"]
299
+ response_body["errors"].is_a?(Array) ? response_body["errors"].join(", ") : response_body["errors"]
300
+ else
301
+ response_body.to_s
302
+ end
303
+ when String
304
+ response_body
305
+ else
306
+ response_body.to_s
307
+ end
308
+ end
309
+
310
+ def get_validation_error_suggestions(error_message)
311
+ suggestions = [
312
+ "Check that all required fields are provided",
313
+ "Verify field formats (dates, emails, etc.)",
314
+ "Review the Pike13 API documentation for field requirements"
315
+ ]
316
+
317
+ # Add specific suggestions based on error message content
318
+ if error_message
319
+ if error_message.include?("active plans")
320
+ suggestions.unshift("Cancel or end all active plans before deleting this person")
321
+ suggestions.unshift("Use 'pike13 desk person_plans list --person-id=ID' to check for active plans")
322
+ elsif error_message.include?("bookings")
323
+ suggestions.unshift("Cancel or complete all active bookings before deletion")
324
+ elsif error_message.include?("payments")
325
+ suggestions.unshift("Ensure there are no pending payment transactions")
326
+ elsif error_message.include?("dependencies")
327
+ suggestions.unshift("Remove or resolve all dependencies before deletion")
328
+ end
329
+ end
330
+
331
+ suggestions
332
+ end
333
+
334
+ def format_error_message(message)
335
+ return nil if message.nil?
336
+
337
+ # Handle common error message formats
338
+ if message.include?("=>") && message.include?('"base"')
339
+ # Parse hash-like error messages: {"base" => ["Error message"]}
340
+ match = message.match(/\{"base"\s*=>\s*\[(.*?)\]\}/)
341
+ return match[1].gsub('"', "").strip if match
342
+ end
343
+
344
+ # Clean up common formatting issues
345
+ message.gsub(/^"|"$/, "").strip
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ module Concerns
7
+ # Module to add formatting capabilities to Thor commands
8
+ # Include this in command classes and call `add_format_options` on methods
9
+ module FormattableCommand
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ # Adds format and compact options to a command
16
+ # Usage: add_format_options before defining the command method
17
+ def add_format_options
18
+ option :format, type: :string, default: "json", desc: "Output format (json, table, csv)"
19
+ option :compact, type: :boolean, default: false, desc: "Compact JSON output"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Output data using the formatter with current options
26
+ def output(data)
27
+ Pike13::CLI::Formatter.output(
28
+ data,
29
+ format: options[:format],
30
+ compact: options[:compact]
31
+ )
32
+ end
33
+
34
+ # Error handling wrapper
35
+ def handle_error
36
+ yield
37
+ rescue Pike13::APIError => e
38
+ puts "API Error: #{e.message}"
39
+ exit 1
40
+ rescue StandardError => e
41
+ puts "Error: #{e.message}"
42
+ exit 1
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ module Concerns
7
+ module ReportingQuery
8
+ def self.included(base)
9
+ base.class_eval do
10
+ format_options
11
+ option :fields, type: :array, desc: "Fields to include (defaults to basic fields)"
12
+ option :filter, type: :hash, desc: "Filter criteria"
13
+ option :group, type: :string, desc: "Group by field"
14
+ option :sort, type: :array, desc: "Sort fields"
15
+ option :page, type: :hash, desc: "Pagination options"
16
+ option :total_count, type: :boolean, desc: "Include total count in response"
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # rubocop:disable Metrics/AbcSize
23
+ def build_query_params
24
+ params = {}
25
+
26
+ # Handle fields - support comma-separated strings and arrays
27
+ if options[:fields]
28
+ params[:fields] = parse_array_option(options[:fields])
29
+ end
30
+
31
+ params[:filter] = options[:filter] if options[:filter]
32
+ params[:group] = options[:group] if options[:group]
33
+
34
+ # Handle sort - support comma-separated strings and arrays
35
+ if options[:sort]
36
+ params[:sort] = parse_array_option(options[:sort])
37
+ end
38
+
39
+ params[:page] = options[:page] if options[:page]
40
+ params[:total_count] = options[:total_count] if options[:total_count]
41
+ params
42
+ end
43
+
44
+ # Parse option that can be either a comma-separated string or an array
45
+ def parse_array_option(option)
46
+ if option.is_a?(Array) && option.size == 1 && option.first.include?(",")
47
+ option.first.split(",").map(&:strip)
48
+ elsif option.is_a?(String) && option.include?(",")
49
+ option.split(",").map(&:strip)
50
+ else
51
+ option
52
+ end
53
+ end
54
+ # rubocop:enable Metrics/AbcSize
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ class Desk < Base
7
+ class Appointment < Base
8
+ desc "available SERVICE_ID", "Find available appointment slots for a service"
9
+ format_options
10
+ option :date, type: :string, required: true, desc: "Date to check (YYYY-MM-DD)"
11
+ option :location_ids, type: :array, desc: "Location IDs to filter by"
12
+ option :staff_member_ids, type: :array, desc: "Staff member IDs to filter by"
13
+ def available(service_id)
14
+ # Validate date format
15
+ validate_date_format(options[:date], "date")
16
+
17
+ handle_error do
18
+ params = { date: options[:date] }
19
+ params[:location_ids] = options[:location_ids] if options[:location_ids]
20
+ params[:staff_member_ids] = options[:staff_member_ids] if options[:staff_member_ids]
21
+
22
+ result = with_progress("Fetching available slots") do
23
+ Pike13::Desk::Appointment.find_available_slots(service_id: service_id, **params)
24
+ end
25
+ output(result)
26
+ end
27
+ end
28
+
29
+ desc "summary SERVICE_ID", "Get appointment availability summary for a service"
30
+ format_options
31
+ option :from, type: :string, required: true, desc: "Start date (YYYY-MM-DD)"
32
+ option :to, type: :string, required: true, desc: "End date (YYYY-MM-DD)"
33
+ option :location_ids, type: :array, desc: "Location IDs to filter by"
34
+ option :staff_member_ids, type: :array, desc: "Staff member IDs to filter by"
35
+ def summary(service_id)
36
+ # Validate date formats
37
+ validate_date_format(options[:from], "from")
38
+ validate_date_format(options[:to], "to")
39
+
40
+ handle_error do
41
+ params = {
42
+ from: options[:from],
43
+ to: options[:to]
44
+ }
45
+ params[:location_ids] = options[:location_ids] if options[:location_ids]
46
+ params[:staff_member_ids] = options[:staff_member_ids] if options[:staff_member_ids]
47
+
48
+ result = with_progress("Fetching availability summary") do
49
+ Pike13::Desk::Appointment.available_slots_summary(service_id: service_id, **params)
50
+ end
51
+ output(result)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ class Desk < Base
7
+ class Booking < Base
8
+ desc "get ID", "Get a booking by ID"
9
+ format_options
10
+ def get(id)
11
+ handle_error do
12
+ result = Pike13::Desk::Booking.find(id)
13
+ output(result)
14
+ end
15
+ end
16
+
17
+ desc "create", "Create a booking"
18
+ format_options
19
+ option :event_occurrence_id, type: :numeric, required: true
20
+ option :person_id, type: :numeric, required: true
21
+ def create
22
+ handle_error do
23
+ require "securerandom"
24
+ result = Pike13::Desk::Booking.create(
25
+ event_occurrence_id: options[:event_occurrence_id],
26
+ person_id: options[:person_id],
27
+ idempotency_token: SecureRandom.uuid
28
+ )
29
+ output(result)
30
+ end
31
+ end
32
+
33
+ desc "update ID", "Update a booking"
34
+ format_options
35
+ option :state, type: :string, desc: "Booking state"
36
+ def update(id)
37
+ handle_error do
38
+ attributes = {}
39
+ attributes[:state] = options[:state] if options[:state]
40
+ result = Pike13::Desk::Booking.update(id, attributes)
41
+ output(result)
42
+ end
43
+ end
44
+
45
+ desc "delete ID", "Delete a booking"
46
+ def delete(id)
47
+ handle_error do
48
+ Pike13::Desk::Booking.destroy(id)
49
+ success_message "Booking #{id} deleted successfully"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ class Desk < Base
7
+ class Business < Base
8
+ desc "find", "Get business details"
9
+ format_options
10
+ def find
11
+ handle_error do
12
+ result = Pike13::Desk::Business.find
13
+ output(result)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ class Desk < Base
7
+ class CustomField < Base
8
+ # Override base_usage to match the actual subcommand registration
9
+ def self.base_usage
10
+ "desk custom_fields"
11
+ end
12
+ desc "list", "List all custom fields"
13
+ map "ls" => :list
14
+ format_options
15
+ def list
16
+ handle_error do
17
+ result = with_progress("Fetching custom fields") do
18
+ Pike13::Desk::CustomField.all
19
+ end
20
+ output(result)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pike13
4
+ module CLI
5
+ module Commands
6
+ class Desk < Base
7
+ class Event < Base
8
+ desc "list", "List all events"
9
+ map "ls" => :list
10
+ format_options
11
+ def list
12
+ handle_error do
13
+ result = with_progress("Fetching events") do
14
+ Pike13::Desk::Event.all
15
+ end
16
+ output(result)
17
+ end
18
+ end
19
+
20
+ desc "get ID", "Get an event by ID"
21
+ format_options
22
+ def get(id)
23
+ handle_error do
24
+ result = Pike13::Desk::Event.find(id)
25
+ output(result)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end