@@ -30,6 +30,27 @@ defmodule AlgoraWeb.User.DashboardLive do
3030 end
3131 end
3232
33+ defmodule AvailabilityForm do
34+ @ moduledoc false
35+ use Ecto.Schema
36+
37+ import Ecto.Changeset
38+
39+ @ primary_key false
40+ embedded_schema do
41+ field :hourly_rate_min , :integer
42+ field :hours_per_week , :integer
43+ end
44+
45+ def changeset ( form , attrs ) do
46+ form
47+ |> cast ( attrs , [ :hourly_rate_min , :hours_per_week ] )
48+ |> validate_required ( [ :hourly_rate_min , :hours_per_week ] )
49+ |> validate_number ( :hourly_rate_min , greater_than: 0 )
50+ |> validate_number ( :hours_per_week , greater_than: 0 , less_than_or_equal_to: 40 )
51+ end
52+ end
53+
3354 @ impl true
3455 def mount ( _params , _session , socket ) do
3556 if connected? ( socket ) do
@@ -49,6 +70,14 @@ defmodule AlgoraWeb.User.DashboardLive do
4970 |> SettingsForm . changeset ( % { tech_stack: socket . assigns . current_user . tech_stack } )
5071 |> to_form ( )
5172
73+ availability_form =
74+ % AvailabilityForm { }
75+ |> AvailabilityForm . changeset ( % {
76+ hourly_rate_min: socket . assigns . current_user . hourly_rate_min ,
77+ hours_per_week: socket . assigns . current_user . hours_per_week
78+ } )
79+ |> to_form ( )
80+
5281 total_earned =
5382 case Accounts . fetch_developer_by ( handle: socket . assigns . current_user . handle ) do
5483 { :ok , user } -> user . total_earned
@@ -58,12 +87,10 @@ defmodule AlgoraWeb.User.DashboardLive do
5887 socket =
5988 socket
6089 |> assign ( :view_mode , "compact" )
61- |> assign ( :available_to_work , true )
62- |> assign ( :hourly_rate , Money . new! ( 50 , :USD ) )
63- |> assign ( :hours_per_week , 40 )
6490 |> assign ( :contracts , contracts )
6591 |> assign ( :has_active_account , has_active_account )
6692 |> assign ( :settings_form , settings_form )
93+ |> assign ( :availability_form , availability_form )
6794 |> assign ( :total_earned , total_earned )
6895 |> assign_bounties ( )
6996 |> assign_achievements ( )
@@ -152,7 +179,7 @@ defmodule AlgoraWeb.User.DashboardLive do
152179 <!-- Sidebar -->
153180 < aside class = "lg:fixed lg:top-16 lg:right-0 lg:bottom-0 lg:w-96 lg:overflow-y-auto scrollbar-thin lg:border-l lg:border-border lg:bg-background p-4 pt-6 sm:p-6 md:p-8 " >
154181 <!-- Availability Section -->
155- <%!-- < div class="flex items-center justify-between">
182+ < div class = "flex items-center justify-between " >
156183 < div class = "flex items-center gap-2 " >
157184 < label for = "available " class = "text-sm font-medium " > Available to work</ label >
158185 < . tooltip >
@@ -165,47 +192,46 @@ defmodule AlgoraWeb.User.DashboardLive do
165192 < . switch
166193 id = "available "
167194 name = "available "
168- value={@available_to_work}
169- phx-click="toggle_availability"
195+ value = { @ current_user . seeking_contracts }
196+ on_click = {
197+ % JS { }
198+ |> JS . push ( "toggle_availability" )
199+ |> JS . toggle ( to: "#availability-details" )
200+ }
170201 />
171202 </ div >
172- <div class="mt-4 grid grid-cols-2 gap-4">
173- <div>
174- <label for="hourly-rate" class="text-sm font-medium">Hourly rate (USD)</label>
175- <div class="relative mt-2">
176- <span class="font-display absolute top-1/2 left-3 -translate-y-1/2">
177- $
178- </span>
179- <.input
180- type="number"
181- min="0"
182- id="hourly-rate"
183- name="hourly-rate"
184- value={@hourly_rate}
185- phx-keydown="handle_hourly_rate"
186- phx-debounce="200"
187- phx-hook="ClearInput"
188- class="font-display w-full border-input bg-background ps-6"
189- />
190- </div>
191- </div>
192- <div>
193- <label for="hours-per-week" class="text-sm font-medium">Hours per week</label>
194- <.input
195- type="number"
196- min="0"
197- max="168"
198- id="hours-per-week"
199- name="hours-per-week"
200- value={@hours_per_week}
201- phx-keydown="handle_hours_per_week"
202- phx-debounce="200"
203- class="font-display mt-2 w-full border-input bg-background"
204- />
205- </div>
206- </div> --%>
203+ < . form
204+ for = { @ availability_form }
205+ id = "availability-form "
206+ phx-change = "validate_availability "
207+ phx-submit = "save_availability "
208+ class = {
209+ classes ( [
210+ "mt-4 grid grid-cols-1 lg:grid-cols-2 gap-4" ,
211+ @ current_user . seeking_contracts || "hidden"
212+ ] )
213+ }
214+ >
215+ < . input
216+ field = { @ availability_form [ :hourly_rate_min ] }
217+ label = "Hourly Rate (USD) "
218+ icon = "tabler-currency-dollar "
219+ />
220+ < . input
221+ field = { @ availability_form [ :hours_per_week ] }
222+ label = "Hours per Week "
223+ icon = "tabler-clock "
224+ />
225+ < . button
226+ :if = { @ availability_form . source . action == :validate }
227+ type = "submit "
228+ class = "lg:col-span-2 "
229+ >
230+ Save
231+ </ . button >
232+ </ . form >
207233 <!-- Tech Stack Section -->
208- < div >
234+ < div class = " mt-4 " >
209235 < h2 class = "mb-2 text-xl font-semibold " >
210236 Tech stack
211237 </ h2 >
@@ -359,6 +385,54 @@ defmodule AlgoraWeb.User.DashboardLive do
359385 end
360386 end
361387
388+ @ impl true
389+ def handle_event ( "toggle_availability" , _params , socket ) do
390+ current_user = socket . assigns . current_user
391+
392+ { :ok , user } = Accounts . update_settings ( current_user , % { seeking_contracts: ! current_user . seeking_contracts } )
393+
394+ { :noreply , assign ( socket , :current_user , user ) }
395+ end
396+
397+ @ impl true
398+ def handle_event ( "validate_availability" , % { "availability_form" => params } , socket ) do
399+ form =
400+ % AvailabilityForm { }
401+ |> AvailabilityForm . changeset ( params )
402+ |> Map . put ( :action , :validate )
403+ |> to_form ( )
404+
405+ { :noreply , assign ( socket , availability_form: form ) }
406+ end
407+
408+ @ impl true
409+ def handle_event ( "save_availability" , % { "availability_form" => params } , socket ) do
410+ changeset =
411+ % AvailabilityForm { }
412+ |> AvailabilityForm . changeset ( params )
413+ |> Map . put ( :action , :validate )
414+
415+ case changeset do
416+ % { valid?: true } ->
417+ case socket . assigns . current_user
418+ |> User . settings_changeset ( params )
419+ |> Repo . update ( ) do
420+ { :ok , user } ->
421+ { :noreply ,
422+ socket
423+ |> put_flash ( :info , "Availability updated!" )
424+ |> assign ( :current_user , user )
425+ |> assign ( :availability_form , changeset |> Map . put ( :action , nil ) |> to_form ( ) ) }
426+
427+ { :error , _changeset } ->
428+ { :noreply , put_flash ( socket , :error , "Failed to update availability" ) }
429+ end
430+
431+ % { valid?: false } ->
432+ { :noreply , assign ( socket , availability_form: to_form ( changeset ) ) }
433+ end
434+ end
435+
362436 @ impl true
363437 def handle_info ( :bounties_updated , socket ) do
364438 { :noreply , socket }
0 commit comments