Compare commits
1 Commits
9-napravit
...
css-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df93b184da |
@@ -5,3 +5,4 @@
|
|||||||
//= link controllers/application.js
|
//= link controllers/application.js
|
||||||
//= link controllers/index.js
|
//= link controllers/index.js
|
||||||
//= link_tree ../../javascript .js
|
//= link_tree ../../javascript .js
|
||||||
|
//= link calendar.tailwind.css
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,13 +1,4 @@
|
|||||||
@tailwind base;
|
@import "tailwindcss/base";
|
||||||
@tailwind components;
|
@import "tailwindcss/components";
|
||||||
@tailwind utilities;
|
@import "tailwindcss/utilities";
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
/*
|
|
||||||
|
|
||||||
@layer components {
|
|
||||||
.btn-primary {
|
|
||||||
@apply py-2 px-4 bg-blue-200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1 +1,4 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@import "tailwindcss/base";
|
||||||
|
@import "tailwindcss/components";
|
||||||
|
@import "tailwindcss/utilities";
|
||||||
@@ -1,34 +1,9 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
before_action :set_locale
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_locale
|
|
||||||
I18n.locale = params[:locale] || session[:locale] || I18n.default_locale
|
|
||||||
session[:locale] = I18n.locale
|
|
||||||
end
|
|
||||||
|
|
||||||
# Optional: Make locale persist across requests via URL helpers
|
|
||||||
def default_url_options
|
|
||||||
{ locale: I18n.locale }
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_company
|
def set_company
|
||||||
# This should be handled by your authentication system
|
company_id = session.fetch(:company_id, Company.first&.id)
|
||||||
# But for now, we'll use a placeholder
|
|
||||||
company_id = session[:company_id]
|
|
||||||
|
|
||||||
unless company_id && Company.exists?(company_id)
|
|
||||||
# If no company in session or it doesn't exist, use the first company
|
|
||||||
company_id = Company.first&.id
|
|
||||||
session[:company_id] = company_id
|
session[:company_id] = company_id
|
||||||
|
@company = Company.find(session[:company_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
@company = Company.find(company_id) if company_id
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_company
|
|
||||||
@company
|
|
||||||
end
|
|
||||||
helper_method :current_company
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ class ReservationsController < ApplicationController
|
|||||||
# GET /reservations or /reservations.json
|
# GET /reservations or /reservations.json
|
||||||
def index
|
def index
|
||||||
@reservations = Reservation.includes(:team, :customer).where(company: @company)
|
@reservations = Reservation.includes(:team, :customer).where(company: @company)
|
||||||
@reservations = ActiveModelSerializers::SerializableResource.new(
|
@reservations = ActiveModelSerializers::SerializableResource.new(@reservations).as_json
|
||||||
@reservations,
|
|
||||||
each_serializer: ReservationSerializer
|
|
||||||
).as_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /reservations/1 or /reservations/1.json
|
# GET /reservations/1 or /reservations/1.json
|
||||||
@@ -17,22 +14,9 @@ class ReservationsController < ApplicationController
|
|||||||
|
|
||||||
# GET /reservations/new
|
# GET /reservations/new
|
||||||
def new
|
def new
|
||||||
# logger.debug "--- Reservations#new --- Params received: #{params.inspect}"
|
@reservation = Reservation.new
|
||||||
|
@reservation.team = @company.teams.first
|
||||||
# Use Time.zone.parse to interpret times within the application's configured timezone
|
@customers = @company.customers
|
||||||
start_time_param = params[:start_time]
|
|
||||||
end_time_param = params[:end_time]
|
|
||||||
# logger.debug "--- Reservations#new --- Start Param: #{start_time_param}, End Param: #{end_time_param}"
|
|
||||||
|
|
||||||
start_time = start_time_param ? Time.zone.parse(start_time_param) : Time.current
|
|
||||||
end_time = end_time_param ? Time.zone.parse(end_time_param) : Time.current + 30.minutes
|
|
||||||
# logger.debug "--- Reservations#new --- Parsed Start: #{start_time}, Parsed End: #{end_time}"
|
|
||||||
|
|
||||||
@reservation = Reservation.new(start_time: start_time, end_time: end_time)
|
|
||||||
# logger.debug "--- Reservations#new --- Reservation object initialized: #{@reservation.attributes.inspect}"
|
|
||||||
|
|
||||||
@reservation.team = @company.teams.first # Assign default team
|
|
||||||
@customers = @company.customers # Preload customers for potential dropdown
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /reservations/1/edit
|
# GET /reservations/1/edit
|
||||||
@@ -44,19 +28,17 @@ class ReservationsController < ApplicationController
|
|||||||
reservation_params.except(:customer_id, :customer_birth_year)
|
reservation_params.except(:customer_id, :customer_birth_year)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find or create customer based on submitted attributes
|
# Find or create customer
|
||||||
find_or_create_customer
|
find_or_create_customer
|
||||||
# Associate the reservation with the found/created customer's primary key attributes
|
|
||||||
assign_customer_to_reservation
|
assign_customer_to_reservation
|
||||||
|
|
||||||
if @reservation.save
|
if @reservation.save
|
||||||
redirect_to reservations_url, notice: t('.reservation_created')
|
redirect_to @reservation, notice: t('.reservation_created')
|
||||||
else
|
else
|
||||||
@customers = @company.customers
|
@customers = @company.customers
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
# If customer creation/validation fails
|
|
||||||
@reservation.errors.add(:base, "Failed to save customer: #{e.message}")
|
@reservation.errors.add(:base, "Failed to save customer: #{e.message}")
|
||||||
@customers = @company.customers
|
@customers = @company.customers
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
@@ -64,55 +46,15 @@ class ReservationsController < ApplicationController
|
|||||||
|
|
||||||
# PATCH/PUT /reservations/1 or /reservations/1.json
|
# PATCH/PUT /reservations/1 or /reservations/1.json
|
||||||
def update
|
def update
|
||||||
# Separate reservation attributes from customer attributes
|
|
||||||
customer_attrs = build_customer_attributes # Use existing helper
|
|
||||||
reservation_attrs = reservation_params.except(
|
|
||||||
:customer_id, :customer_first_name, :customer_surname,
|
|
||||||
:customer_original_phone, :customer_birth_year
|
|
||||||
)
|
|
||||||
|
|
||||||
# Find the customer identified by the submitted name/phone
|
|
||||||
@customer = Customer.find_by(
|
|
||||||
first_name: customer_attrs[:first_name],
|
|
||||||
surname: customer_attrs[:surname],
|
|
||||||
original_phone: customer_attrs[:original_phone],
|
|
||||||
company_id: @company.id
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update customer phone/birthyear if found (identifiers assumed immutable here)
|
|
||||||
customer_updated = @customer ? @customer.update(customer_attrs.slice(:phone, :birthyear)) : false
|
|
||||||
|
|
||||||
# Assign the correct customer foreign keys to the reservation attributes
|
|
||||||
if @customer
|
|
||||||
reservation_attrs[:customer_first_name] = @customer.first_name
|
|
||||||
reservation_attrs[:customer_surname] = @customer.surname
|
|
||||||
reservation_attrs[:customer_original_phone] = @customer.original_phone
|
|
||||||
else
|
|
||||||
# Fall back to original keys if form customer wasn't found (e.g., identifiers changed)
|
|
||||||
# Consider adding an error or different handling if customer *must* be found.
|
|
||||||
reservation_attrs[:customer_first_name] = @reservation.customer_first_name
|
|
||||||
reservation_attrs[:customer_surname] = @reservation.customer_surname
|
|
||||||
reservation_attrs[:customer_original_phone] = @reservation.customer_original_phone
|
|
||||||
end
|
|
||||||
|
|
||||||
reservation_updated = @reservation.update(reservation_attrs)
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
# Check if reservation update was successful
|
if @reservation.update(reservation_params)
|
||||||
# (We might ignore customer_updated status for now, or add more complex checks)
|
format.html { redirect_to reservation_url(@reservation), notice: t('.reservation_updated') }
|
||||||
if reservation_updated
|
|
||||||
format.html { redirect_to reservations_url, notice: t('.reservation_updated') }
|
|
||||||
format.json { render :show, status: :ok, location: @reservation }
|
format.json { render :show, status: :ok, location: @reservation }
|
||||||
else
|
else
|
||||||
@customers = @company.customers # Reload for form
|
|
||||||
format.html { render :edit, status: :unprocessable_entity }
|
format.html { render :edit, status: :unprocessable_entity }
|
||||||
format.json { render json: @reservation.errors, status: :unprocessable_entity }
|
format.json { render json: @reservation.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue ActiveRecord::RecordInvalid => e # Catch potential customer update errors
|
|
||||||
@reservation.errors.add(:base, "Failed to save customer: #{e.message}") if @customer&.invalid?
|
|
||||||
@customers = @company.customers
|
|
||||||
render :edit, status: :unprocessable_entity
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# DELETE /reservations/1 or /reservations/1.json
|
# DELETE /reservations/1 or /reservations/1.json
|
||||||
@@ -134,17 +76,17 @@ class ReservationsController < ApplicationController
|
|||||||
|
|
||||||
# Only allow a list of trusted parameters through.
|
# Only allow a list of trusted parameters through.
|
||||||
def reservation_params
|
def reservation_params
|
||||||
# Permit composite key if form uses it, otherwise permit individual fields
|
|
||||||
# params.require(:reservation).permit(:customer_composite_key, ...)
|
|
||||||
params.require(:reservation).permit(
|
params.require(:reservation).permit(
|
||||||
|
:customer_id,
|
||||||
:team_id,
|
:team_id,
|
||||||
:start_time,
|
:start_time,
|
||||||
:end_time,
|
:end_time,
|
||||||
|
:title,
|
||||||
|
:description,
|
||||||
:customer_first_name,
|
:customer_first_name,
|
||||||
:customer_surname,
|
:customer_surname,
|
||||||
:customer_original_phone,
|
:customer_original_phone,
|
||||||
:customer_birth_year,
|
:customer_birth_year # Keep this in permitted params
|
||||||
:customer_id # Allow this if select still sends it sometimes
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -152,7 +94,6 @@ class ReservationsController < ApplicationController
|
|||||||
action_name == 'index' ? 'calendar' : 'application'
|
action_name == 'index' ? 'calendar' : 'application'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds or creates customer based on submitted fields
|
|
||||||
def find_or_create_customer
|
def find_or_create_customer
|
||||||
customer_params = build_customer_attributes
|
customer_params = build_customer_attributes
|
||||||
|
|
||||||
@@ -163,57 +104,35 @@ class ReservationsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Extracts customer attributes from reservation form parameters
|
|
||||||
def build_customer_attributes
|
def build_customer_attributes
|
||||||
{
|
{
|
||||||
first_name: params[:reservation][:customer_first_name],
|
first_name: params[:reservation][:customer_first_name],
|
||||||
surname: params[:reservation][:customer_surname],
|
surname: params[:reservation][:customer_surname],
|
||||||
original_phone: params[:reservation][:customer_original_phone],
|
original_phone: params[:reservation][:customer_original_phone],
|
||||||
phone: params[:reservation][:customer_original_phone], # Assuming phone is same as original_phone for now
|
phone: params[:reservation][:customer_original_phone],
|
||||||
birthyear: params[:reservation][:customer_birth_year],
|
birthyear: params[:reservation][:customer_birth_year],
|
||||||
company_id: @company.id
|
company_id: @company.id
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks if the submitted customer ID indicates a new customer
|
|
||||||
def new_customer?
|
def new_customer?
|
||||||
# Check based on the specific value format used by TomSelect create function
|
params[:reservation][:customer_id].present? &&
|
||||||
params[:reservation][:customer_composite_key]&.end_with?('__new') ||
|
params[:reservation][:customer_id].end_with?('__new')
|
||||||
params[:reservation][:customer_id]&.end_with?('__new') # Fallback if old key is used
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds customer by composite key or creates them
|
|
||||||
def find_or_initialize_customer(attributes)
|
def find_or_initialize_customer(attributes)
|
||||||
# Find using the composite key fields
|
|
||||||
Customer.find_or_create_by!(
|
Customer.find_or_create_by!(
|
||||||
first_name: attributes[:first_name],
|
first_name: attributes[:first_name],
|
||||||
surname: attributes[:surname],
|
surname: attributes[:surname],
|
||||||
original_phone: attributes[:original_phone]
|
original_phone: attributes[:original_phone]
|
||||||
# company_id: attributes[:company_id] # Scope to company if needed
|
|
||||||
) do |customer|
|
) do |customer|
|
||||||
# Assign other attributes only if creating
|
customer.assign_attributes(attributes)
|
||||||
customer.assign_attributes(attributes.slice(:phone, :birthyear, :company_id))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the foreign key fields on the reservation based on the found/created customer
|
|
||||||
def assign_customer_to_reservation
|
def assign_customer_to_reservation
|
||||||
return unless @customer # Guard clause
|
|
||||||
|
|
||||||
@reservation.customer_first_name = @customer.first_name
|
@reservation.customer_first_name = @customer.first_name
|
||||||
@reservation.customer_surname = @customer.surname
|
@reservation.customer_surname = @customer.surname
|
||||||
@reservation.customer_original_phone = @customer.original_phone
|
@reservation.customer_original_phone = @customer.original_phone
|
||||||
end
|
end
|
||||||
|
|
||||||
# Override the application controller method to include teams
|
|
||||||
def set_company
|
|
||||||
company_id = session[:company_id]
|
|
||||||
|
|
||||||
unless company_id && Company.exists?(company_id)
|
|
||||||
company_id = Company.first&.id
|
|
||||||
session[:company_id] = company_id
|
|
||||||
end
|
|
||||||
|
|
||||||
@company = Company.includes(:teams).find(company_id) if company_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
class ServiceWorkerController < ApplicationController
|
|
||||||
protect_from_forgery except: :service_worker
|
|
||||||
def service_worker
|
|
||||||
end
|
|
||||||
def manifest
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
module ColorHelper
|
|
||||||
# Generates a consistent, visually pleasing color based on an ID
|
|
||||||
# Uses the golden ratio to ensure good color distribution
|
|
||||||
def team_color(team_id)
|
|
||||||
# Use the golden ratio to create a well-distributed sequence of hues
|
|
||||||
golden_ratio_conjugate = 0.618033988749895
|
|
||||||
|
|
||||||
# Use the team_id as a seed for the hue
|
|
||||||
h = (team_id.to_i * golden_ratio_conjugate) % 1
|
|
||||||
|
|
||||||
# Convert to HSL color with fixed saturation and lightness for good UI colors
|
|
||||||
# Saturation: 65% - vibrant but not too intense
|
|
||||||
# Lightness: 55% - visible on both light and dark backgrounds
|
|
||||||
hsl_to_hex(h, 0.65, 0.55)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a color object with various formats for a team
|
|
||||||
# This allows for more flexibility in how the color is used
|
|
||||||
def team_color_object(team_id)
|
|
||||||
h = (team_id.to_i * 0.618033988749895) % 1
|
|
||||||
s = 0.65
|
|
||||||
l = 0.55
|
|
||||||
|
|
||||||
# Calculate RGB values
|
|
||||||
rgb = hsl_to_rgb(h, s, l)
|
|
||||||
hex = hsl_to_hex(h, s, l)
|
|
||||||
|
|
||||||
{
|
|
||||||
hex: hex, # #RRGGBB
|
|
||||||
rgb: "rgb(#{rgb[0]}, #{rgb[1]}, #{rgb[2]})", # rgb(r,g,b)
|
|
||||||
hsl: "hsl(#{(h*360).round}, #{(s*100).round}%, #{(l*100).round}%)", # hsl(h,s%,l%)
|
|
||||||
light_bg: hsl_to_hex(h, s, 0.9), # Lighter version for backgrounds
|
|
||||||
dark_bg: hsl_to_hex(h, s, 0.2), # Darker version for backgrounds
|
|
||||||
border: hsl_to_hex(h, s, 0.4) # Border color
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Convert HSL to Hex color
|
|
||||||
def hsl_to_hex(h, s, l)
|
|
||||||
r, g, b = hsl_to_rgb(h, s, l)
|
|
||||||
"##{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Convert HSL to RGB values
|
|
||||||
def hsl_to_rgb(h, s, l)
|
|
||||||
# Convert HSL to RGB using standard algorithm
|
|
||||||
if s == 0
|
|
||||||
r = g = b = (l * 255).round
|
|
||||||
else
|
|
||||||
q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
|
||||||
p = 2 * l - q
|
|
||||||
|
|
||||||
r = (hue_to_rgb(p, q, h + 1/3.0) * 255).round
|
|
||||||
g = (hue_to_rgb(p, q, h) * 255).round
|
|
||||||
b = (hue_to_rgb(p, q, h - 1/3.0) * 255).round
|
|
||||||
end
|
|
||||||
|
|
||||||
[r, g, b]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper function for HSL to RGB conversion
|
|
||||||
def hue_to_rgb(p, q, t)
|
|
||||||
t += 1 if t < 0
|
|
||||||
t -= 1 if t > 1
|
|
||||||
|
|
||||||
return p + (q - p) * 6 * t if t < 1/6.0
|
|
||||||
return q if t < 1/2.0
|
|
||||||
return p + (q - p) * (2/3.0 - t) * 6 if t < 2/3.0
|
|
||||||
return p
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||||
import "controllers"
|
import "controllers"
|
||||||
import "custom/companion"
|
|
||||||
|
|||||||
@@ -2,20 +2,8 @@ import { Controller } from "@hotwired/stimulus"
|
|||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ["select", "phoneField", "birthYearField", "firstNameField", "surnameField", "newCustomerFields"]
|
static targets = ["select", "phoneField", "birthYearField", "firstNameField", "surnameField", "newCustomerFields"]
|
||||||
static values = {
|
|
||||||
existingId: String,
|
|
||||||
existingLabel: String
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
let initialOptions = [];
|
|
||||||
let initialValue = null;
|
|
||||||
|
|
||||||
if (this.hasExistingIdValue && this.hasExistingLabelValue && this.existingIdValue.length > 0) {
|
|
||||||
initialOptions = [{ id: this.existingIdValue, label: this.existingLabelValue }];
|
|
||||||
initialValue = this.existingIdValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectInstance = new TomSelect(this.selectTarget, {
|
this.selectInstance = new TomSelect(this.selectTarget, {
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
labelField: 'label',
|
labelField: 'label',
|
||||||
@@ -32,11 +20,10 @@ export default class extends Controller {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
options: initialOptions,
|
options: [],
|
||||||
items: initialValue ? [initialValue] : [],
|
|
||||||
|
|
||||||
load: async (query, callback) => {
|
load: async (query, callback) => {
|
||||||
if (!query.length && !initialValue) return callback();
|
if (!query.length) return callback();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/customers/search?q=${encodeURIComponent(query)}`, {
|
const response = await fetch(`/customers/search?q=${encodeURIComponent(query)}`, {
|
||||||
@@ -46,11 +33,7 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
callback(data);
|
||||||
const existingOptionId = this.hasExistingIdValue ? this.existingIdValue : null;
|
|
||||||
const filteredData = data.filter(item => item.id !== existingOptionId);
|
|
||||||
|
|
||||||
callback(filteredData);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading customers:', error);
|
console.error('Error loading customers:', error);
|
||||||
callback();
|
callback();
|
||||||
@@ -70,17 +53,16 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Events
|
||||||
onLoad: (data) => {
|
onLoad: (data) => {
|
||||||
if (!this.selectInstance.getValue() && (!data || data.length === 0)) {
|
if (!data || data.length === 0) {
|
||||||
this.showNewCustomerFields();
|
this.showNewCustomerFields();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange: (value) => {
|
onChange: (value) => {
|
||||||
if (value === null || value === '') {
|
if (value === null) {
|
||||||
this.showNewCustomerFields();
|
this.showNewCustomerFields();
|
||||||
} else if (!value.endsWith('__new')) {
|
|
||||||
this.newCustomerFieldsTarget.classList.add('hidden');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -94,11 +76,6 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (initialValue) {
|
|
||||||
this.newCustomerFieldsTarget.classList.add('hidden');
|
|
||||||
this.customerSelected(initialValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customerSelected(value) {
|
customerSelected(value) {
|
||||||
@@ -120,15 +97,9 @@ export default class extends Controller {
|
|||||||
|
|
||||||
if (customerData && customerData.birthyear) {
|
if (customerData && customerData.birthyear) {
|
||||||
this.birthYearFieldTarget.value = customerData.birthyear;
|
this.birthYearFieldTarget.value = customerData.birthyear;
|
||||||
} else {
|
|
||||||
this.birthYearFieldTarget.value = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newCustomerFieldsTarget.classList.add('hidden');
|
this.newCustomerFieldsTarget.classList.add('hidden');
|
||||||
} else {
|
|
||||||
console.warn("Selected customer value format unexpected:", value);
|
|
||||||
this.clearFields();
|
|
||||||
this.showNewCustomerFields();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,63 +2,14 @@ import {Controller} from "@hotwired/stimulus"
|
|||||||
// Connects to data-controller="main-calendar"
|
// Connects to data-controller="main-calendar"
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ["dateDisplay", "navigation", "teamFilter"]
|
static targets = ["dateDisplay", "navigation"]
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
// Set height to full viewport
|
// Set height to full viewport
|
||||||
document.getElementById('main-calendar').style.height = '100vh';
|
document.getElementById('main-calendar').style.height = '100vh';
|
||||||
document.getElementById('main-calendar').style.width = '100vw';
|
document.getElementById('main-calendar').style.width = '100vw';
|
||||||
|
|
||||||
// Get current locale from html lang attribute
|
const calendar = new tui.Calendar(document.getElementById('main-calendar'), {
|
||||||
const currentLocale = document.documentElement.lang || 'bs';
|
|
||||||
|
|
||||||
// Get translations from data attribute
|
|
||||||
const translations = JSON.parse(document.getElementById('main-calendar').dataset.translations || '{}');
|
|
||||||
|
|
||||||
// Translation helper
|
|
||||||
const t = (key) => translations[key] || key;
|
|
||||||
|
|
||||||
// Pre-process reservations to set up team calendars
|
|
||||||
const reservations = JSON.parse(document.querySelector("#main-calendar").dataset.reservations);
|
|
||||||
window.reservations = reservations;
|
|
||||||
|
|
||||||
// Debug: Log the first reservation to inspect color format
|
|
||||||
if (reservations && reservations.length > 0) {
|
|
||||||
console.log("First reservation team color:", reservations[0].team.color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract unique teams and create calendar configurations
|
|
||||||
const teamCalendars = [];
|
|
||||||
const teamMap = {};
|
|
||||||
|
|
||||||
// Create calendar configs for each team
|
|
||||||
reservations.forEach(reservation => {
|
|
||||||
const teamId = reservation.team.id;
|
|
||||||
if (!teamMap[teamId]) {
|
|
||||||
const calendarId = `team-${teamId}`;
|
|
||||||
teamMap[teamId] = calendarId;
|
|
||||||
|
|
||||||
// Use color directly - it should be a hex string from the TeamSerializer
|
|
||||||
const teamColor = reservation.team.color || '#00a9ff';
|
|
||||||
|
|
||||||
teamCalendars.push({
|
|
||||||
id: calendarId,
|
|
||||||
name: reservation.team.name,
|
|
||||||
backgroundColor: teamColor,
|
|
||||||
borderColor: teamColor
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store team calendars for filtering
|
|
||||||
this.teamCalendars = teamCalendars;
|
|
||||||
this.allCalendars = [...teamCalendars];
|
|
||||||
|
|
||||||
// Store all reservations for filtering
|
|
||||||
this.allReservations = reservations;
|
|
||||||
|
|
||||||
// Initialize calendar with all team calendars
|
|
||||||
window.calendar = new tui.Calendar(document.getElementById('main-calendar'), {
|
|
||||||
defaultView: 'week',
|
defaultView: 'week',
|
||||||
usageStatistics: false,
|
usageStatistics: false,
|
||||||
week: {
|
week: {
|
||||||
@@ -69,21 +20,13 @@ export default class extends Controller {
|
|||||||
hourStart: 4,
|
hourStart: 4,
|
||||||
hourEnd: 21,
|
hourEnd: 21,
|
||||||
},
|
},
|
||||||
timezone: {
|
|
||||||
zones: [
|
|
||||||
{
|
|
||||||
timezoneName: 'UTC',
|
|
||||||
displayLabel: 'UTC' // Optional: Label for the timezone
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// This is important - set the height to 100%
|
// This is important - set the height to 100%
|
||||||
height: '100%',
|
height: '100%',
|
||||||
// Make sure it takes full width
|
// Make sure it takes full width
|
||||||
width: '100%',
|
width: '100%',
|
||||||
template: {
|
template: {
|
||||||
timegridDisplayPrimaryTime({time}) {
|
timegridDisplayPrimaryTime({time}) {
|
||||||
return `${time.getHours()} ${t('hours')}`;
|
return `${time.getHours()} sati`;
|
||||||
},
|
},
|
||||||
popupDetailLocation(eventObj) {
|
popupDetailLocation(eventObj) {
|
||||||
return ''; // Empty location as requested
|
return ''; // Empty location as requested
|
||||||
@@ -96,85 +39,46 @@ export default class extends Controller {
|
|||||||
},
|
},
|
||||||
popupDetailBody(eventObj) {
|
popupDetailBody(eventObj) {
|
||||||
return '';
|
return '';
|
||||||
},
|
|
||||||
popupEdit() {
|
|
||||||
return t('edit');
|
|
||||||
},
|
|
||||||
popupDelete() {
|
|
||||||
return t('delete');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calendars: teamCalendars.length > 0 ? teamCalendars : [
|
calendars: [
|
||||||
{
|
{
|
||||||
id: 'default',
|
id: 'cal1',
|
||||||
name: 'Default',
|
name: 'Work',
|
||||||
backgroundColor: '#00a9ff',
|
backgroundColor: '#00a9ff',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
|
// Enable the built-in popup
|
||||||
|
useDetailPopup: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create calendar events
|
window.calendar = calendar;
|
||||||
this.createCalendarEvents(reservations);
|
this.getCalendardata();
|
||||||
|
|
||||||
// Set up initial date display
|
calendar.render();
|
||||||
|
|
||||||
|
// Update the date display after rendering
|
||||||
this.updateDateDisplay();
|
this.updateDateDisplay();
|
||||||
|
|
||||||
// Handle calendar navigation
|
|
||||||
window.calendar.on('beforeUpdateDay', (date) => {
|
|
||||||
this.updateDateDisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle edit and delete actions
|
|
||||||
window.calendar.on('clickEvent', (event) => {
|
|
||||||
const reservation = event.event.reservation;
|
|
||||||
|
|
||||||
// Redirect to edit page
|
|
||||||
window.location.href = `/reservations/${reservation.id}/edit`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render the calendar
|
|
||||||
window.calendar.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create events for all reservations
|
|
||||||
createCalendarEvents(reservations) {
|
|
||||||
if (!window.calendar) return;
|
|
||||||
|
|
||||||
// Process each reservation into a calendar event
|
|
||||||
reservations.forEach(reservation => {
|
|
||||||
const teamId = reservation.team.id;
|
|
||||||
const calendarId = `team-${teamId}`;
|
|
||||||
|
|
||||||
const startTime = new Date(reservation.start_time);
|
|
||||||
const endTime = new Date(reservation.end_time);
|
|
||||||
|
|
||||||
// Create the event
|
|
||||||
const event = {
|
|
||||||
id: `reservation-${reservation.id}`,
|
|
||||||
calendarId: calendarId,
|
|
||||||
title: reservation.customer ? `${reservation.customer.first_name} ${reservation.customer.surname}` : '',
|
|
||||||
start: startTime,
|
|
||||||
end: endTime,
|
|
||||||
category: 'time',
|
|
||||||
isReadOnly: false,
|
|
||||||
reservation: reservation,
|
|
||||||
attendees: [reservation.team.name]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add event to calendar
|
|
||||||
window.calendar.createEvents([event]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCalendardata() {
|
getCalendardata() {
|
||||||
// This method is now replaced by initialization in connect()
|
var reservations = JSON.parse(document.querySelector("#main-calendar").dataset.reservations);
|
||||||
// and createCalendarEvents method
|
window.reservations = reservations;
|
||||||
|
reservations.forEach(reservation => {
|
||||||
|
window.calendar.createEvents([
|
||||||
|
{
|
||||||
|
id: reservation.id,
|
||||||
|
calendarId: 'cal1',
|
||||||
|
title: reservation.customer.first_name + ' ' + reservation.customer.surname + ' (' + reservation.customer.phone + ')',
|
||||||
|
category: 'time',
|
||||||
|
dueDateClass: reservation.dueDateClass,
|
||||||
|
location: '', // Empty location as requested
|
||||||
|
attendees: [reservation.team.name], // Team name as attendee
|
||||||
|
start: reservation.start_time,
|
||||||
|
end: reservation.end_time
|
||||||
}
|
}
|
||||||
|
])
|
||||||
// Helper function to get CSRF token from meta tag
|
});
|
||||||
getCsrfToken() {
|
|
||||||
const token = document.querySelector('meta[name="csrf-token"]')?.content;
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation methods - using the global window.calendar
|
// Navigation methods - using the global window.calendar
|
||||||
@@ -236,44 +140,4 @@ export default class extends Controller {
|
|||||||
day: 'numeric'
|
day: 'numeric'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method for team filtering
|
|
||||||
filterByTeam(event) {
|
|
||||||
const selectedTeamId = event.target.value;
|
|
||||||
|
|
||||||
// Store last value for test verification
|
|
||||||
window.lastTeamFilterValue = selectedTeamId;
|
|
||||||
|
|
||||||
console.log(`Filtering by team: ${selectedTeamId}`);
|
|
||||||
|
|
||||||
if (!window.calendar) return;
|
|
||||||
|
|
||||||
// Clear existing events
|
|
||||||
window.calendar.clear();
|
|
||||||
|
|
||||||
// Process reservations based on filter
|
|
||||||
let filteredReservations = this.allReservations;
|
|
||||||
|
|
||||||
// If not "all", filter by team
|
|
||||||
if (selectedTeamId !== 'all') {
|
|
||||||
// Extract numeric ID if the value is in "team-{id}" format
|
|
||||||
const teamId = selectedTeamId.toString().startsWith('team-')
|
|
||||||
? selectedTeamId.toString().replace('team-', '')
|
|
||||||
: selectedTeamId.toString();
|
|
||||||
|
|
||||||
console.log(`Using team ID for filtering: ${teamId}`);
|
|
||||||
|
|
||||||
filteredReservations = this.allReservations.filter(reservation =>
|
|
||||||
reservation.team.id.toString() === teamId
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Found ${filteredReservations.length} reservations for team ${teamId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and add calendar events based on filtered reservations
|
|
||||||
this.createCalendarEvents(filteredReservations);
|
|
||||||
|
|
||||||
// Update calendar display
|
|
||||||
window.calendar.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
if (navigator.serviceWorker) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register("/service-worker.js", { scope: "/" })
|
|
||||||
.then(() => navigator.serviceWorker.ready)
|
|
||||||
.then((registration) => {
|
|
||||||
if ("SyncManager" in window) {
|
|
||||||
registration.sync.register("sync-forms");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(() => console.log("[Companion]", "Service worker registered!"));
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,6 @@ class Reservation < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Moved to app/serializers/reservation_serializer.rb
|
class ReservationSerializer < ActiveModel::Serializer
|
||||||
# class ReservationSerializer < ActiveModel::Serializer
|
attributes :id, :company, :customer, :team, :start_time, :end_time
|
||||||
# attributes :id, :company, :customer, :team, :start_time, :end_time
|
end
|
||||||
# end
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
class ReservationSerializer < ActiveModel::Serializer
|
|
||||||
attributes :id, :start_time, :end_time
|
|
||||||
|
|
||||||
belongs_to :customer
|
|
||||||
belongs_to :team, serializer: TeamSerializer
|
|
||||||
end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
class TeamSerializer < ActiveModel::Serializer
|
|
||||||
include ColorHelper
|
|
||||||
|
|
||||||
attributes :id, :name, :color
|
|
||||||
|
|
||||||
def color
|
|
||||||
team_color(object.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,51 +1,47 @@
|
|||||||
<div id="<%= dom_id company %>" class="bg-white rounded-xl shadow-md overflow-hidden p-6">
|
<div id="<%= dom_id company %>">
|
||||||
<div class="flex justify-between items-start mb-4">
|
<p class="my-5">
|
||||||
<h2 class="text-2xl font-bold text-gray-800"><%= company.name %></h2>
|
<strong class="block font-medium mb-1">Name:</strong>
|
||||||
<div class="flex-shrink-0 bg-blue-100 text-blue-800 rounded-full p-3">
|
<%= company.name %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
</p>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<p class="my-5">
|
||||||
<div>
|
<strong class="block font-medium mb-1">Id number:</strong>
|
||||||
<p class="text-sm">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.id_number') %></span>
|
|
||||||
<%= company.id_number %>
|
<%= company.id_number %>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mt-2">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.vat_number') %></span>
|
<p class="my-5">
|
||||||
|
<strong class="block font-medium mb-1">Vat number:</strong>
|
||||||
<%= company.vat_number %>
|
<%= company.vat_number %>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mt-2">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.entity') %></span>
|
<p class="my-5">
|
||||||
<%= company.entity %>
|
<strong class="block font-medium mb-1">Address line one:</strong>
|
||||||
</p>
|
|
||||||
<p class="text-sm mt-2">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.country') %></span>
|
|
||||||
<%= company.country %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.address_line_one') %></span>
|
|
||||||
<%= company.address_line_one %>
|
<%= company.address_line_one %>
|
||||||
</p>
|
</p>
|
||||||
<% if company.address_line_two.present? %>
|
|
||||||
<p class="text-sm mt-2">
|
<p class="my-5">
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.address_line_two') %></span>
|
<strong class="block font-medium mb-1">Address line two:</strong>
|
||||||
<%= company.address_line_two %>
|
<%= company.address_line_two %>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
|
||||||
<p class="text-sm mt-2">
|
<p class="my-5">
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.postal_code') %></span>
|
<strong class="block font-medium mb-1">Postal code:</strong>
|
||||||
<%= company.postal_code %>
|
<%= company.postal_code %>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mt-2">
|
|
||||||
<span class="font-medium text-gray-500"><%= t('companies.company.city') %></span>
|
<p class="my-5">
|
||||||
|
<strong class="block font-medium mb-1">City:</strong>
|
||||||
<%= company.city %>
|
<%= company.city %>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
<p class="my-5">
|
||||||
|
<strong class="block font-medium mb-1">Entity:</strong>
|
||||||
|
<%= company.entity %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="my-5">
|
||||||
|
<strong class="block font-medium mb-1">Country:</strong>
|
||||||
|
<%= company.country %>
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<%= form_with(model: company, class: "contents") do |form| %>
|
<%= form_with(model: company, class: "contents") do |form| %>
|
||||||
<% if company.errors.any? %>
|
<% if company.errors.any? %>
|
||||||
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
||||||
<h2><%= t('companies.form.errors.header', count: company.errors.count) %></h2>
|
<h2><%= pluralize(company.errors.count, "error") %> prohibited this company from being saved:</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% company.errors.each do |error| %>
|
<% company.errors.each do |error| %>
|
||||||
@@ -12,55 +12,51 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :name, t('companies.form.name') %>
|
<%= form.label :name %>
|
||||||
<%= form.text_field :name, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :name, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :id_number, t('companies.form.id_number') %>
|
<%= form.label :id_number %>
|
||||||
<%= form.text_field :id_number, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :id_number, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :vat_number, t('companies.form.vat_number') %>
|
<%= form.label :vat_number %>
|
||||||
<%= form.text_field :vat_number, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :vat_number, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :address_line_one, t('companies.form.address_line_one') %>
|
<%= form.label :address_line_one %>
|
||||||
<%= form.text_field :address_line_one, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :address_line_one, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :address_line_two, t('companies.form.address_line_two') %>
|
<%= form.label :address_line_two %>
|
||||||
<%= form.text_field :address_line_two, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :address_line_two, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :postal_code, t('companies.form.postal_code') %>
|
<%= form.label :postal_code %>
|
||||||
<%= form.text_field :postal_code, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :postal_code, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :city, t('companies.form.city') %>
|
<%= form.label :city %>
|
||||||
<%= form.text_field :city, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :city, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :entity, t('companies.form.entity') %>
|
<%= form.label :entity %>
|
||||||
<%= form.text_field :entity, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :entity, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :country, t('companies.form.country') %>
|
<%= form.label :country %>
|
||||||
<%= form.text_field :country, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :country, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inline">
|
<div class="inline">
|
||||||
<% if company.new_record? %>
|
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
||||||
<%= form.submit t('companies.form.create'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% else %>
|
|
||||||
<%= form.submit t('companies.form.update'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">Editing company</h1>
|
||||||
|
|
||||||
<%= render "form", company: @company %>
|
<%= render "form", company: @company %>
|
||||||
|
|
||||||
<%= link_to t('.show'), @company, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Show this company", @company, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<%= link_to t('.back'), companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to companies", companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
<div class="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="w-full">
|
||||||
<% if notice.present? %>
|
<% if notice.present? %>
|
||||||
<div class="mb-8 mt-4">
|
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
||||||
<p class="py-3 px-4 bg-green-50 text-green-700 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% content_for :title, t('.title') %>
|
<% content_for :title, "Companies" %>
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-12">
|
<div class="flex justify-between items-center">
|
||||||
<h1 class="font-bold text-4xl text-gray-900"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">Companies</h1>
|
||||||
<%= link_to new_company_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white font-medium hover:bg-blue-700 transition-colors duration-200 flex items-center" do %>
|
<%= link_to "New company", new_company_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 mr-2">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
||||||
</svg>
|
|
||||||
<%= t('.new_company') %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="companies" class="grid gap-6 mb-8">
|
<div id="companies" class="min-w-full">
|
||||||
<% @companies.each do |company| %>
|
<% @companies.each do |company| %>
|
||||||
<%= link_to company, class: "block transition-all duration-200 hover:shadow-lg hover:-translate-y-1" do %>
|
|
||||||
<%= render company %>
|
<%= render company %>
|
||||||
<% end %>
|
<p>
|
||||||
|
<%= link_to "Show this company", company, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">New company</h1>
|
||||||
|
|
||||||
<%= render "form", company: @company %>
|
<%= render "form", company: @company %>
|
||||||
|
|
||||||
<%= link_to t('.back'), companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to companies", companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
<%= render @company %>
|
<%= render @company %>
|
||||||
|
|
||||||
<%= link_to t('.edit'), edit_company_path(@company), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Edit this company", edit_company_path(@company), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<%= link_to t('.back'), companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to companies", companies_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<div class="inline-block ml-2">
|
<div class="inline-block ml-2">
|
||||||
<%= button_to t('.destroy'), @company, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
|
<%= button_to "Destroy this company", @company, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="<%= I18n.locale %>">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title><%= content_for?(:title) ? yield(:title) : "Terminator" %></title>
|
<title>Terminator</title>
|
||||||
<link rel="manifest" crossorigin="use-credentials" href="/manifest.json" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
@@ -11,7 +10,6 @@
|
|||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||||
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
||||||
|
|
||||||
|
|
||||||
<%= javascript_importmap_tags %>
|
<%= javascript_importmap_tags %>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css" rel="stylesheet">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Terminator</title>
|
<title>Terminator</title>
|
||||||
<link rel="manifest" crossorigin="use-credentials" href="/manifest.json" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
@@ -10,7 +9,7 @@
|
|||||||
<script src="https://uicdn.toast.com/calendar/latest/toastui-calendar.min.js"></script>
|
<script src="https://uicdn.toast.com/calendar/latest/toastui-calendar.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||||
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
||||||
|
<%= stylesheet_link_tag "calendar.tailwind", "data-turbo-track": "reload" %>
|
||||||
|
|
||||||
<%= javascript_importmap_tags %>
|
<%= javascript_importmap_tags %>
|
||||||
<style>
|
<style>
|
||||||
@@ -60,4 +59,3 @@
|
|||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
want
|
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
<%= form_with(model: reservation, class: "contents",
|
<%= form_with(model: reservation, class: "contents", data: { controller: "customer-search" }) do |form| %>
|
||||||
data: {
|
|
||||||
controller: "customer-search",
|
|
||||||
customer_search_existing_id_value: (reservation.persisted? && reservation.customer ? reservation.customer.to_param : nil),
|
|
||||||
customer_search_existing_label_value: (reservation.persisted? && reservation.customer ? "#{reservation.customer.full_name} (#{reservation.customer.original_phone})" : nil)
|
|
||||||
}) do |form| %>
|
|
||||||
<% if reservation.errors.any? %>
|
<% if reservation.errors.any? %>
|
||||||
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
||||||
<h2><%= t('reservations.form.errors.header', count: reservation.errors.count) %></h2>
|
<h2><%= pluralize(reservation.errors.count, "error") %> prohibited this reservation from being saved:</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% reservation.errors.each do |error| %>
|
<% reservation.errors.each do |error| %>
|
||||||
@@ -17,19 +12,18 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :customer, t('reservations.form.customer') %>
|
<%= form.label :customer %>
|
||||||
<%= form.select :customer_composite_key,
|
<%= form.select :customer_id,
|
||||||
[], # Start with empty options
|
[], # Start with empty options
|
||||||
{ prompt: t('reservations.form.search_prompt') },
|
{ prompt: "Type to search customers..." },
|
||||||
{
|
{
|
||||||
selected: (reservation.persisted? && reservation.customer ? reservation.customer.to_param : nil),
|
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
data: { customer_search_target: "select" }
|
data: { customer_search_target: "select" }
|
||||||
} %>
|
} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :phone_number, t('reservations.form.phone_number') %>
|
<%= form.label :phone_number %>
|
||||||
<%= form.telephone_field :customer_original_phone,
|
<%= form.telephone_field :customer_original_phone,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
data: { customer_search_target: "phoneField" } %>
|
data: { customer_search_target: "phoneField" } %>
|
||||||
@@ -37,21 +31,21 @@
|
|||||||
|
|
||||||
<div data-customer-search-target="newCustomerFields" class="hidden">
|
<div data-customer-search-target="newCustomerFields" class="hidden">
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :first_name, t('reservations.form.first_name') %>
|
<%= form.label :first_name %>
|
||||||
<%= form.text_field :customer_first_name,
|
<%= form.text_field :customer_first_name,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
data: { customer_search_target: "firstNameField" } %>
|
data: { customer_search_target: "firstNameField" } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :surname, t('reservations.form.surname') %>
|
<%= form.label :surname %>
|
||||||
<%= form.text_field :customer_surname,
|
<%= form.text_field :customer_surname,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
data: { customer_search_target: "surnameField" } %>
|
data: { customer_search_target: "surnameField" } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :birth_year, t('reservations.form.birth_year') %>
|
<%= form.label :birth_year %>
|
||||||
<%= form.number_field :customer_birth_year,
|
<%= form.number_field :customer_birth_year,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
data: { customer_search_target: "birthYearField" } %>
|
data: { customer_search_target: "birthYearField" } %>
|
||||||
@@ -59,34 +53,63 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :team_id, t('reservations.form.team') %>
|
<%= form.label :team_id %>
|
||||||
<%= form.collection_select :team_id,
|
<%= form.collection_select :team_id,
|
||||||
@company.teams,
|
@company.teams,
|
||||||
:id,
|
:id,
|
||||||
:name,
|
:name,
|
||||||
{ prompt: t('reservations.form.select_team') },
|
{ prompt: "Select a team" },
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :start_time, t('reservations.form.start_time') %>
|
<%= form.label :start_time %>
|
||||||
<%= form.datetime_field :start_time,
|
<%= form.datetime_field :start_time,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
id: "start_time_field" %>
|
id: "start_time_field" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :end_time, t('reservations.form.end_time') %>
|
<%= form.label :end_time %>
|
||||||
<%= form.datetime_field :end_time,
|
<%= form.datetime_field :end_time,
|
||||||
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full",
|
||||||
id: "end_time_field" %>
|
id: "end_time_field" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inline">
|
<div class="inline">
|
||||||
<% if reservation.new_record? %>
|
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
||||||
<%= form.submit t('reservations.form.create'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% else %>
|
|
||||||
<%= form.submit t('reservations.form.update'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
// Only set default times for new records (not when editing)
|
||||||
|
<% unless reservation.persisted? %>
|
||||||
|
// Get current date and time
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Get local components
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
|
||||||
|
const day = String(now.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(now.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
// Format for datetime-local input (YYYY-MM-DDThh:mm)
|
||||||
|
const localStartTime = `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||||
|
document.getElementById('start_time_field').value = localStartTime;
|
||||||
|
|
||||||
|
// Add 30 minutes for end time
|
||||||
|
const endDate = new Date(now.getTime() + 30 * 60000);
|
||||||
|
const endHours = String(endDate.getHours()).padStart(2, '0');
|
||||||
|
const endMinutes = String(endDate.getMinutes()).padStart(2, '0');
|
||||||
|
const localEndTime = `${year}-${month}-${day}T${endHours}:${endMinutes}`;
|
||||||
|
document.getElementById('end_time_field').value = localEndTime;
|
||||||
|
|
||||||
|
// For debugging - add this to see actual values
|
||||||
|
console.log("Start time set to: " + localStartTime);
|
||||||
|
console.log("End time set to: " + localEndTime);
|
||||||
|
console.log("Current browser time: " + now.toString());
|
||||||
|
<% end %>
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -17,12 +17,12 @@
|
|||||||
|
|
||||||
<p class="my-5">
|
<p class="my-5">
|
||||||
<strong class="block font-medium mb-1">Start time:</strong>
|
<strong class="block font-medium mb-1">Start time:</strong>
|
||||||
<%= l reservation.start_time if reservation.start_time %>
|
<%= reservation.start_time %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="my-5">
|
<p class="my-5">
|
||||||
<strong class="block font-medium mb-1">End time:</strong>
|
<strong class="block font-medium mb-1">End time:</strong>
|
||||||
<%= l reservation.end_time if reservation.end_time %>
|
<%= reservation.end_time %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">Editing reservation</h1>
|
||||||
|
|
||||||
<%= render "form", reservation: @reservation %>
|
<%= render "form", reservation: @reservation %>
|
||||||
|
|
||||||
<%= link_to t('.show'), @reservation, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Show this reservation", @reservation, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<%= link_to t('.back'), reservations_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to reservations", reservations_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,67 +2,36 @@
|
|||||||
<div class="reservation-page" data-controller="main-calendar" style="display: block; width: 100%; height: 100vh; overflow: hidden;">
|
<div class="reservation-page" data-controller="main-calendar" style="display: block; width: 100%; height: 100vh; overflow: hidden;">
|
||||||
<!-- Fixed height header -->
|
<!-- Fixed height header -->
|
||||||
<header style="height: 80px; padding: 15px; background-color: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); position: relative; z-index: 100;">
|
<header style="height: 80px; padding: 15px; background-color: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); position: relative; z-index: 100;">
|
||||||
<% content_for :title, t('.title') %>
|
<% if notice.present? %>
|
||||||
|
<p class="py-2 px-3 bg-green-50 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% content_for :title, "Reservations" %>
|
||||||
|
|
||||||
<div class="flex justify-between items-center-calendar">
|
<div class="flex justify-between items-center-calendar">
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<h1 class="font-bold text-4xl px-5"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl px-5">Reservations</h1>
|
||||||
<% if notice.present? %>
|
|
||||||
<p class="py-1 px-3 ml-4 bg-green-100 text-green-700 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
|
||||||
<% end %>
|
|
||||||
<div class="flex items-center space-x-2 ml-6" data-main-calendar-target="navigation">
|
<div class="flex items-center space-x-2 ml-6" data-main-calendar-target="navigation">
|
||||||
<button data-action="main-calendar#prev" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
<button data-action="main-calendar#prev" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
||||||
<%= t('.prev') %>
|
« Prev
|
||||||
</button>
|
</button>
|
||||||
<button data-action="main-calendar#today" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
<button data-action="main-calendar#today" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
||||||
<%= t('.today') %>
|
Today
|
||||||
</button>
|
</button>
|
||||||
<button data-action="main-calendar#next" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
<button data-action="main-calendar#next" class="px-3 py-1 bg-gray-100 rounded-md hover:bg-gray-200">
|
||||||
<%= t('.next') %>
|
Next »
|
||||||
</button>
|
</button>
|
||||||
<span data-main-calendar-target="dateDisplay" class="ml-3 font-medium"></span>
|
<span data-main-calendar-target="dateDisplay" class="ml-3 font-medium"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Team Filter Dropdown -->
|
|
||||||
<div class="ml-6">
|
|
||||||
<label for="team-filter" class="mr-2 font-medium"><%= t('.filter_by_team') %>:</label>
|
|
||||||
<select id="team-filter" data-main-calendar-target="teamFilter" data-action="change->main-calendar#filterByTeam" class="rounded-md border-gray-300 shadow-sm px-3 py-1 bg-white">
|
|
||||||
<option value="all"><%= t('.all_teams') %></option>
|
|
||||||
<% @company.teams.each do |team| %>
|
|
||||||
<option value="<%= team.id %>" style="background-color: <%= team_color(team.id) %>; color: #000000; padding-left: 10px;">
|
|
||||||
<%= team.name %>
|
|
||||||
</option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<%= link_to "New reservation", new_reservation_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium ml-auto" %>
|
||||||
<%= link_to t('.new_reservation'), new_reservation_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium ml-auto" %>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Calendar container - with fixed top position and precise height calculation -->
|
<!-- Calendar container - with fixed top position and precise height calculation -->
|
||||||
<div class="calendar-container" style="height: calc(100vh - 80px); width: 100%; position: relative; top: 0; left: 0;">
|
<div class="calendar-container" style="height: calc(100vh - 80px); width: 100%; position: relative; top: 0; left: 0;">
|
||||||
<div style="height: 100%; width: 100%;">
|
<div style="height: 100%; width: 100%;">
|
||||||
<%
|
<%= tag.div nil, data: {reservations: @reservations.to_json}, id: "main-calendar", style: "height: 100%; width: 100%;" %>
|
||||||
calendar_translations = {
|
|
||||||
hours: t('reservations.calendar.hours'),
|
|
||||||
delete_confirm: t('reservations.calendar.delete_confirm'),
|
|
||||||
delete_success: t('reservations.calendar.delete_success'),
|
|
||||||
delete_error: t('reservations.calendar.delete_error'),
|
|
||||||
network_error: t('reservations.calendar.network_error'),
|
|
||||||
edit: t('reservations.calendar.edit'),
|
|
||||||
delete: t('reservations.calendar.delete'),
|
|
||||||
all_teams: t('reservations.calendar.all_teams')
|
|
||||||
}.to_json
|
|
||||||
%>
|
|
||||||
<%= tag.div nil,
|
|
||||||
data: {
|
|
||||||
reservations: @reservations.to_json,
|
|
||||||
translations: calendar_translations
|
|
||||||
},
|
|
||||||
id: "main-calendar",
|
|
||||||
style: "height: 100%; width: 100%;"
|
|
||||||
%>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">New reservation</h1>
|
||||||
|
|
||||||
<%= render "form", reservation: @reservation %>
|
<%= render "form", reservation: @reservation %>
|
||||||
|
|
||||||
<%= link_to t('.back'), reservations_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to reservations", reservations_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Zdravo Stopalo",
|
|
||||||
"name": "Zdravo Stopalo",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "<%= image_path('icon-192.png')%>",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "<%= image_path('icon-512.png')%>",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": "<%= root_path %>",
|
|
||||||
"background_color": "#FFFFFF",
|
|
||||||
"display": "standalone",
|
|
||||||
"scope": "<%= root_path %>",
|
|
||||||
"theme_color": "#8e2731"
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
function onInstall(event) {
|
|
||||||
console.log("[Serviceworker]", "Installing!", event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onActivate(event) {
|
|
||||||
console.log("[Serviceworker]", "Activating!", event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFetch(event) {
|
|
||||||
console.log("[Serviceworker]", "Fetching!", event);
|
|
||||||
}
|
|
||||||
self.addEventListener("install", onInstall);
|
|
||||||
self.addEventListener("activate", onActivate);
|
|
||||||
self.addEventListener("fetch", onFetch);
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<%= form_with(model: team, class: "contents") do |form| %>
|
<%= form_with(model: team, class: "contents") do |form| %>
|
||||||
<% if team.errors.any? %>
|
<% if team.errors.any? %>
|
||||||
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
|
||||||
<h2><%= t('teams.form.errors.header', count: team.errors.count) %></h2>
|
<h2><%= pluralize(team.errors.count, "error") %> prohibited this team from being saved:</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<% team.errors.each do |error| %>
|
<% team.errors.each do |error| %>
|
||||||
<li><%= error.full_message %></li>
|
<li><%= error.full_message %></li>
|
||||||
@@ -11,15 +11,11 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<%= form.label :name, t('teams.form.name') %>
|
<%= form.label :name %>
|
||||||
<%= form.text_field :name, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
<%= form.text_field :name, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inline">
|
<div class="inline">
|
||||||
<% if team.new_record? %>
|
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
||||||
<%= form.submit t('teams.form.create'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% else %>
|
|
||||||
<%= form.submit t('teams.form.update'), class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,12 +1,2 @@
|
|||||||
<div id="<%= dom_id team %>" class="bg-white rounded-xl shadow-md overflow-hidden mb-6 hover:shadow-lg transition-shadow duration-300">
|
<div id="<%= dom_id team %>">
|
||||||
<div class="p-6 flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h2 class="text-2xl font-bold text-gray-800"><%= team.name %></h2>
|
|
||||||
</div>
|
|
||||||
<div class="flex-shrink-0 bg-blue-100 text-blue-800 rounded-full p-3">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">Editing team</h1>
|
||||||
|
|
||||||
<%= render "form", team: @team %>
|
<%= render "form", team: @team %>
|
||||||
|
|
||||||
<%= link_to t('.show'), @team, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Show this team", @team, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<%= link_to t('.back'), teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to teams", teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
<div class="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="w-full">
|
||||||
<% if notice.present? %>
|
<% if notice.present? %>
|
||||||
<div class="mb-8 mt-4">
|
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
||||||
<p class="py-3 px-4 bg-green-50 text-green-700 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% content_for :title, t('.title') %>
|
<% content_for :title, "Teams" %>
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-12">
|
<div class="flex justify-between items-center">
|
||||||
<h1 class="font-bold text-4xl text-gray-900"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">Teams</h1>
|
||||||
<%= link_to new_team_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white font-medium hover:bg-blue-700 transition-colors duration-200 flex items-center" do %>
|
<%= link_to "New team", new_team_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 mr-2">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
||||||
</svg>
|
|
||||||
<%= t('.new_team') %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="teams" class="grid gap-6 mb-8">
|
<div id="teams" class="min-w-full">
|
||||||
<% @teams.each do |team| %>
|
<% @teams.each do |team| %>
|
||||||
<%= link_to team, class: "block transition-all duration-200 hover:shadow-lg hover:-translate-y-1" do %>
|
|
||||||
<%= render team %>
|
<%= render team %>
|
||||||
<% end %>
|
<p>
|
||||||
|
<%= link_to "Show this team", team, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="mx-auto md:w-2/3 w-full">
|
<div class="mx-auto md:w-2/3 w-full">
|
||||||
<h1 class="font-bold text-4xl"><%= t('.title') %></h1>
|
<h1 class="font-bold text-4xl">New team</h1>
|
||||||
|
|
||||||
<%= render "form", team: @team %>
|
<%= render "form", team: @team %>
|
||||||
|
|
||||||
<%= link_to t('.back'), teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to teams", teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
<%= render @team %>
|
<%= render @team %>
|
||||||
|
|
||||||
<%= link_to t('.edit'), edit_team_path(@team), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Edit this team", edit_team_path(@team), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<%= link_to t('.back'), teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
<%= link_to "Back to teams", teams_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
|
||||||
<div class="inline-block ml-2">
|
<div class="inline-block ml-2">
|
||||||
<%= button_to t('.destroy'), @team, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
|
<%= button_to "Destroy this team", @team, method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ module Terminator
|
|||||||
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
||||||
config.autoload_lib(ignore: %w(assets tasks))
|
config.autoload_lib(ignore: %w(assets tasks))
|
||||||
|
|
||||||
# Set available locales and default locale
|
|
||||||
config.i18n.available_locales = [:en, :bs]
|
|
||||||
config.i18n.default_locale = :bs
|
|
||||||
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
|
|
||||||
|
|
||||||
# Configuration for the application, engines, and railties goes here.
|
# Configuration for the application, engines, and railties goes here.
|
||||||
#
|
#
|
||||||
# These settings can be overridden in specific environments using the files
|
# These settings can be overridden in specific environments using the files
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ Rails.application.configure do
|
|||||||
# Suppress logger output for asset requests.
|
# Suppress logger output for asset requests.
|
||||||
config.assets.quiet = true
|
config.assets.quiet = true
|
||||||
|
|
||||||
|
config.assets.compile = true
|
||||||
|
|
||||||
# Raises error for missing translations.
|
# Raises error for missing translations.
|
||||||
# config.i18n.raise_on_missing_translations = true
|
# config.i18n.raise_on_missing_translations = true
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,3 @@ pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|||||||
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
||||||
pin "application"
|
pin "application"
|
||||||
pin_all_from "app/javascript/controllers", under: "controllers"
|
pin_all_from "app/javascript/controllers", under: "controllers"
|
||||||
pin_all_from "app/javascript/custom", under: "custom"
|
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ Rails.application.config.assets.version = "1.0"
|
|||||||
# application.js, application.css, and all non-JS/CSS in the app/assets
|
# application.js, application.css, and all non-JS/CSS in the app/assets
|
||||||
# folder are already added.
|
# folder are already added.
|
||||||
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|
||||||
|
|
||||||
|
Rails.application.config.assets.precompile += %w( application.tailwind.css )
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
bs:
|
|
||||||
# Add Bosnian translations here
|
|
||||||
reservations:
|
|
||||||
index:
|
|
||||||
title: "Rezervacije"
|
|
||||||
prev: "« Prethodna"
|
|
||||||
today: "Danas"
|
|
||||||
next: "Sljedeća »"
|
|
||||||
new_reservation: "Nova rezervacija" # TODO: Translate to Bosnian
|
|
||||||
filter_by_team: "Filtriraj po timu"
|
|
||||||
all_teams: "Svi timovi"
|
|
||||||
calendar:
|
|
||||||
hours: "sati"
|
|
||||||
delete_confirm: "Jeste li sigurni da želite izbrisati ovu rezervaciju?"
|
|
||||||
delete_success: "Rezervacija je izbrisana."
|
|
||||||
delete_error: "Greška prilikom brisanja rezervacije."
|
|
||||||
network_error: "Greška prilikom brisanja rezervacije zbog problema s mrežom ili skriptom."
|
|
||||||
edit: "Uredi"
|
|
||||||
delete: "Obriši"
|
|
||||||
all_teams: "Svi timovi"
|
|
||||||
# Keys for form and edit/new pages
|
|
||||||
edit:
|
|
||||||
title: "Uređivanje rezervacije"
|
|
||||||
show: "Prikaži ovu rezervaciju"
|
|
||||||
back: "Nazad na rezervacije"
|
|
||||||
new:
|
|
||||||
title: "Nova rezervacija"
|
|
||||||
back: "Nazad na rezervacije"
|
|
||||||
form:
|
|
||||||
customer: "Klijent"
|
|
||||||
search_prompt: "Unesite za pretragu klijenata..."
|
|
||||||
phone_number: "Broj telefona"
|
|
||||||
first_name: "Ime"
|
|
||||||
surname: "Prezime"
|
|
||||||
birth_year: "Godina rođenja"
|
|
||||||
team: "Tim"
|
|
||||||
select_team: "Odaberite tim"
|
|
||||||
start_time: "Vrijeme početka"
|
|
||||||
end_time: "Vrijeme završetka"
|
|
||||||
submit: "Pošalji"
|
|
||||||
update: "Ažuriraj rezervaciju"
|
|
||||||
create: "Kreiraj rezervaciju"
|
|
||||||
errors:
|
|
||||||
header: "%{count} greška/e spriječila/e je da se ova rezervacija sačuva:"
|
|
||||||
create:
|
|
||||||
reservation_created: "Rezervacija je uspješno kreirana."
|
|
||||||
update:
|
|
||||||
reservation_updated: "Rezervacija je uspješno ažurirana."
|
|
||||||
destroy:
|
|
||||||
reservation_destroyed: "Rezervacija je uspješno izbrisana."
|
|
||||||
|
|
||||||
teams:
|
|
||||||
create:
|
|
||||||
team_created: "Tim je uspješno kreiran."
|
|
||||||
update:
|
|
||||||
team_updated: "Tim je uspješno ažuriran."
|
|
||||||
destroy:
|
|
||||||
team_destroyed: "Tim je uspješno izbrisan."
|
|
||||||
index:
|
|
||||||
title: "Timovi"
|
|
||||||
new_team: "Novi tim"
|
|
||||||
show_team: "Prikaži ovaj tim"
|
|
||||||
new:
|
|
||||||
title: "Novi tim"
|
|
||||||
back: "Nazad na timove"
|
|
||||||
edit:
|
|
||||||
title: "Uređivanje tima"
|
|
||||||
show: "Prikaži ovaj tim"
|
|
||||||
back: "Nazad na timove"
|
|
||||||
show:
|
|
||||||
edit: "Uredi ovaj tim"
|
|
||||||
back: "Nazad na timove"
|
|
||||||
destroy: "Izbriši ovaj tim"
|
|
||||||
form:
|
|
||||||
name: "Naziv"
|
|
||||||
submit: "Pošalji"
|
|
||||||
update: "Ažuriraj tim"
|
|
||||||
create: "Kreiraj tim"
|
|
||||||
errors:
|
|
||||||
header: "%{count} greška/e spriječila/e je da se ovaj tim sačuva:"
|
|
||||||
|
|
||||||
companies:
|
|
||||||
create:
|
|
||||||
company_created: "Kompanija je uspješno kreirana."
|
|
||||||
update:
|
|
||||||
company_updated: "Kompanija je uspješno ažurirana."
|
|
||||||
destroy:
|
|
||||||
company_destroyed: "Kompanija je uspješno izbrisana."
|
|
||||||
index:
|
|
||||||
title: "Kompanije"
|
|
||||||
new_company: "Nova kompanija"
|
|
||||||
show_company: "Prikaži ovu kompaniju"
|
|
||||||
new:
|
|
||||||
title: "Nova kompanija"
|
|
||||||
back: "Nazad na kompanije"
|
|
||||||
edit:
|
|
||||||
title: "Uređivanje kompanije"
|
|
||||||
show: "Prikaži ovu kompaniju"
|
|
||||||
back: "Nazad na kompanije"
|
|
||||||
show:
|
|
||||||
edit: "Uredi ovu kompaniju"
|
|
||||||
back: "Nazad na kompanije"
|
|
||||||
destroy: "Izbriši ovu kompaniju"
|
|
||||||
form:
|
|
||||||
name: "Naziv"
|
|
||||||
id_number: "ID broj"
|
|
||||||
vat_number: "PDV broj"
|
|
||||||
address_line_one: "Adresa (prva linija)"
|
|
||||||
address_line_two: "Adresa (druga linija)"
|
|
||||||
postal_code: "Poštanski broj"
|
|
||||||
city: "Grad"
|
|
||||||
entity: "Entitet"
|
|
||||||
country: "Država"
|
|
||||||
submit: "Pošalji"
|
|
||||||
update: "Ažuriraj kompaniju"
|
|
||||||
create: "Kreiraj kompaniju"
|
|
||||||
errors:
|
|
||||||
header: "%{count} greška/e spriječila/e je da se ova kompanija sačuva:"
|
|
||||||
company:
|
|
||||||
name: "Naziv:"
|
|
||||||
id_number: "ID broj:"
|
|
||||||
vat_number: "PDV broj:"
|
|
||||||
address_line_one: "Adresa (prva linija):"
|
|
||||||
address_line_two: "Adresa (druga linija):"
|
|
||||||
postal_code: "Poštanski broj:"
|
|
||||||
city: "Grad:"
|
|
||||||
entity: "Entitet:"
|
|
||||||
country: "Država:"
|
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
# enabled: "ON"
|
# enabled: "ON"
|
||||||
|
|
||||||
en:
|
en:
|
||||||
# Add English translations here
|
|
||||||
hello: "Hello world"
|
hello: "Hello world"
|
||||||
customers:
|
customers:
|
||||||
create:
|
create:
|
||||||
@@ -46,46 +45,6 @@ en:
|
|||||||
reservation_updated: "Reservation was successfully updated."
|
reservation_updated: "Reservation was successfully updated."
|
||||||
destroy:
|
destroy:
|
||||||
reservation_destroyed: "Reservation was successfully destroyed."
|
reservation_destroyed: "Reservation was successfully destroyed."
|
||||||
index:
|
|
||||||
title: "Reservations"
|
|
||||||
prev: "« Prev"
|
|
||||||
today: "Today"
|
|
||||||
next: "Next »"
|
|
||||||
new_reservation: "New reservation"
|
|
||||||
filter_by_team: "Filter by team"
|
|
||||||
all_teams: "All teams"
|
|
||||||
calendar:
|
|
||||||
hours: "hours"
|
|
||||||
delete_confirm: "Are you sure you want to delete this reservation?"
|
|
||||||
delete_success: "Reservation deleted."
|
|
||||||
delete_error: "Error deleting reservation."
|
|
||||||
network_error: "Error deleting reservation due to a network or script issue."
|
|
||||||
edit: "Edit"
|
|
||||||
delete: "Delete"
|
|
||||||
all_teams: "All teams"
|
|
||||||
edit:
|
|
||||||
title: "Editing reservation"
|
|
||||||
show: "Show this reservation"
|
|
||||||
back: "Back to reservations"
|
|
||||||
new:
|
|
||||||
title: "New reservation"
|
|
||||||
back: "Back to reservations"
|
|
||||||
form:
|
|
||||||
customer: "Customer"
|
|
||||||
search_prompt: "Type to search customers..."
|
|
||||||
phone_number: "Phone number"
|
|
||||||
first_name: "First name"
|
|
||||||
surname: "Surname"
|
|
||||||
birth_year: "Birth year"
|
|
||||||
team: "Team"
|
|
||||||
select_team: "Select a team"
|
|
||||||
start_time: "Start time"
|
|
||||||
end_time: "End time"
|
|
||||||
submit: "Submit"
|
|
||||||
update: "Update Reservation"
|
|
||||||
create: "Create Reservation"
|
|
||||||
errors:
|
|
||||||
header: "%{count} prohibited this reservation from being saved:"
|
|
||||||
teams:
|
teams:
|
||||||
create:
|
create:
|
||||||
team_created: "Team was successfully created."
|
team_created: "Team was successfully created."
|
||||||
@@ -93,72 +52,3 @@ en:
|
|||||||
team_updated: "Team was successfully updated."
|
team_updated: "Team was successfully updated."
|
||||||
destroy:
|
destroy:
|
||||||
team_destroyed: "Team was successfully destroyed."
|
team_destroyed: "Team was successfully destroyed."
|
||||||
index:
|
|
||||||
title: "Teams"
|
|
||||||
new_team: "New team"
|
|
||||||
show_team: "Show this team"
|
|
||||||
new:
|
|
||||||
title: "New team"
|
|
||||||
back: "Back to teams"
|
|
||||||
edit:
|
|
||||||
title: "Editing team"
|
|
||||||
show: "Show this team"
|
|
||||||
back: "Back to teams"
|
|
||||||
show:
|
|
||||||
edit: "Edit this team"
|
|
||||||
back: "Back to teams"
|
|
||||||
destroy: "Destroy this team"
|
|
||||||
form:
|
|
||||||
name: "Name"
|
|
||||||
submit: "Submit"
|
|
||||||
update: "Update Team"
|
|
||||||
create: "Create Team"
|
|
||||||
errors:
|
|
||||||
header: "%{count} prohibited this team from being saved:"
|
|
||||||
companies:
|
|
||||||
create:
|
|
||||||
company_created: "Company was successfully created."
|
|
||||||
update:
|
|
||||||
company_updated: "Company was successfully updated."
|
|
||||||
destroy:
|
|
||||||
company_destroyed: "Company was successfully destroyed."
|
|
||||||
index:
|
|
||||||
title: "Companies"
|
|
||||||
new_company: "New company"
|
|
||||||
show_company: "Show this company"
|
|
||||||
new:
|
|
||||||
title: "New company"
|
|
||||||
back: "Back to companies"
|
|
||||||
edit:
|
|
||||||
title: "Editing company"
|
|
||||||
show: "Show this company"
|
|
||||||
back: "Back to companies"
|
|
||||||
show:
|
|
||||||
edit: "Edit this company"
|
|
||||||
back: "Back to companies"
|
|
||||||
destroy: "Destroy this company"
|
|
||||||
form:
|
|
||||||
name: "Name"
|
|
||||||
id_number: "ID number"
|
|
||||||
vat_number: "VAT number"
|
|
||||||
address_line_one: "Address line one"
|
|
||||||
address_line_two: "Address line two"
|
|
||||||
postal_code: "Postal code"
|
|
||||||
city: "City"
|
|
||||||
entity: "Entity"
|
|
||||||
country: "Country"
|
|
||||||
submit: "Submit"
|
|
||||||
update: "Update Company"
|
|
||||||
create: "Create Company"
|
|
||||||
errors:
|
|
||||||
header: "%{count} prohibited this company from being saved:"
|
|
||||||
company:
|
|
||||||
name: "Name:"
|
|
||||||
id_number: "ID number:"
|
|
||||||
vat_number: "VAT number:"
|
|
||||||
address_line_one: "Address line one:"
|
|
||||||
address_line_two: "Address line two:"
|
|
||||||
postal_code: "Postal code:"
|
|
||||||
city: "City:"
|
|
||||||
entity: "Entity:"
|
|
||||||
country: "Country:"
|
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ Rails.application.routes.draw do
|
|||||||
# Can be used by load balancers and uptime monitors to verify that the app is live.
|
# Can be used by load balancers and uptime monitors to verify that the app is live.
|
||||||
get "up" => "rails/health#show", as: :rails_health_check
|
get "up" => "rails/health#show", as: :rails_health_check
|
||||||
|
|
||||||
# config/routes.rb
|
|
||||||
get "/service-worker.js" => "service_worker#service_worker"
|
|
||||||
get "/manifest.json" => "service_worker#manifest"
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
# root "posts#index"
|
# root "posts#index"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ require_relative '../config/environment'
|
|||||||
# Prevent database truncation if the environment is production
|
# Prevent database truncation if the environment is production
|
||||||
abort("The Rails environment is running in production mode!") if Rails.env.production?
|
abort("The Rails environment is running in production mode!") if Rails.env.production?
|
||||||
require 'rspec/rails'
|
require 'rspec/rails'
|
||||||
require 'capybara/rails'
|
|
||||||
require 'capybara/rspec'
|
|
||||||
# Add additional requires below this line. Rails is not loaded until this point!
|
# Add additional requires below this line. Rails is not loaded until this point!
|
||||||
|
|
||||||
# Requires supporting ruby files with custom matchers and macros, etc, in
|
# Requires supporting ruby files with custom matchers and macros, etc, in
|
||||||
@@ -63,33 +61,4 @@ RSpec.configure do |config|
|
|||||||
config.filter_rails_from_backtrace!
|
config.filter_rails_from_backtrace!
|
||||||
# arbitrary gems may also be filtered via:
|
# arbitrary gems may also be filtered via:
|
||||||
# config.filter_gems_from_backtrace("gem name")
|
# config.filter_gems_from_backtrace("gem name")
|
||||||
|
|
||||||
# Include FactoryBot methods
|
|
||||||
config.include FactoryBot::Syntax::Methods
|
|
||||||
|
|
||||||
# Configure Capybara for JavaScript tests
|
|
||||||
Capybara.javascript_driver = :selenium_chrome_headless
|
|
||||||
|
|
||||||
# Use DatabaseCleaner for feature specs
|
|
||||||
config.use_transactional_fixtures = false
|
|
||||||
|
|
||||||
config.before(:suite) do
|
|
||||||
DatabaseCleaner.clean_with(:truncation)
|
|
||||||
end
|
|
||||||
|
|
||||||
config.before(:each) do
|
|
||||||
DatabaseCleaner.strategy = :transaction
|
|
||||||
end
|
|
||||||
|
|
||||||
config.before(:each, js: true) do
|
|
||||||
DatabaseCleaner.strategy = :truncation
|
|
||||||
end
|
|
||||||
|
|
||||||
config.before(:each) do
|
|
||||||
DatabaseCleaner.start
|
|
||||||
end
|
|
||||||
|
|
||||||
config.after(:each) do
|
|
||||||
DatabaseCleaner.clean
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user