@@ -6,7 +6,9 @@ defmodule AlgoraWeb.HomeLive do
6
6
import AlgoraWeb.Components.ModalVideo
7
7
8
8
alias Algora.Accounts
9
+ alias Algora.Bounties
9
10
alias Algora.Jobs
11
+ alias Algora.Payments
10
12
alias AlgoraWeb.Components.Footer
11
13
alias AlgoraWeb.Components.Header
12
14
alias AlgoraWeb.Data.HomeCache
@@ -52,7 +54,8 @@ defmodule AlgoraWeb.HomeLive do
52
54
|> assign ( :company_people_examples , company_people_examples )
53
55
|> assign ( :show_challenge_drawer , false )
54
56
|> assign ( :challenge_form , to_form ( ChallengeForm . changeset ( % ChallengeForm { } , % { } ) ) )
55
- |> assign_user_applications ( ) }
57
+ |> assign_user_applications ( )
58
+ |> assign_events ( ) }
56
59
end
57
60
end
58
61
@@ -276,70 +279,13 @@ defmodule AlgoraWeb.HomeLive do
276
279
</ section >
277
280
278
281
< section class = "relative isolate pb-16 sm:pb-40 " >
279
- < div class = "mx-auto max-w-7xl px-6 lg:px-8 " >
280
- < h2 class = "font-display text-2xl sm:text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-semibold tracking-tight text-foreground text-center mb-2 " >
281
- Open positions
282
- </ h2 >
283
- < p class = "text-center text-muted-foreground mb-8 " >
284
- Discover jobs at top startups
285
- </ p >
286
-
287
- <%= if Enum . empty? ( @ jobs_by_user ) do %>
288
- < div class = "text-center py-12 " >
289
- < . icon name = "tabler-briefcase " class = "h-12 w-12 text-muted-foreground mx-auto mb-4 " />
290
- < p class = "text-muted-foreground " > No open positions at the moment</ p >
291
- </ div >
292
- <% else %>
293
- < div class = "flex flex-col sm:grid gap-6 max-w-4xl mx-auto " >
294
- <%= for { user , jobs } <- Enum . take ( @ jobs_by_user , 3 ) do %>
295
- <%= for job <- Enum . take ( jobs , 1 ) do %>
296
- < div class = "flex flex-col sm:flex-row gap-4 justify-between p-6 bg-card rounded-xl border hover:shadow-lg transition-shadow " >
297
- < div >
298
- < div class = "flex items-start gap-4 " >
299
- < . avatar class = "size-12 " >
300
- < . avatar_image src = { user . avatar_url } />
301
- < . avatar_fallback >
302
- { Algora.Util . initials ( user . name ) }
303
- </ . avatar_fallback >
304
- </ . avatar >
305
- < div >
306
- < div class = "font-semibold text-foreground " > { job . title } </ div >
307
- < div class = "text-sm text-muted-foreground " > { user . name } </ div >
308
- </ div >
309
- </ div >
310
- < div class = "flex gap-2 mt-2 sm:pl-12 " >
311
- <%= for tech <- Enum . take ( job . tech_stack || [ ] , 3 ) do %>
312
- < . tech_badge tech = { tech } size = "sm " />
313
- <% end %>
314
- </ div >
315
- </ div >
316
- < div >
317
- <%= if MapSet . member? ( @ user_applications , job . id ) do %>
318
- < . button size = "sm " disabled class = "opacity-50 w-full sm:w-auto " >
319
- Applied
320
- </ . button >
321
- <% else %>
322
- < . button
323
- size = "sm "
324
- phx-click = "apply_job "
325
- phx-value-job-id = { job . id }
326
- class = "w-full sm:w-auto "
327
- >
328
- Apply
329
- </ . button >
330
- <% end %>
331
- </ div >
332
- </ div >
333
- <% end %>
334
- <% end %>
335
- </ div >
336
-
337
- < div class = "text-center mt-8 " >
338
- < . button navigate = { ~p" /jobs" } variant = "outline " >
339
- View all positions
340
- </ . button >
341
- </ div >
342
- <% end %>
282
+ < div class = "flex flex-col gap-4 px-4 pt-6 sm:pt-10 mx-auto max-w-4xl " >
283
+ < div class = "mx-auto max-w-7xl px-6 lg:px-8 pt-24 xl:pt-0 " >
284
+ < h2 class = "font-display text-3xl font-semibold tracking-tight text-foreground sm:text-6xl mb-2 sm:mb-4 " >
285
+ Community highlights
286
+ </ h2 >
287
+ </ div >
288
+ < . events events = { @ events } />
343
289
</ div >
344
290
</ section >
345
291
@@ -697,6 +643,237 @@ defmodule AlgoraWeb.HomeLive do
697
643
assign ( socket , :user_applications , user_applications )
698
644
end
699
645
646
+ defp events ( assigns ) do
647
+ ~H"""
648
+ < ul class = "w-full pl-10 relative space-y-8 " >
649
+ < li :for = { { event , index } <- @ events |> Enum . with_index ( ) } class = "relative " >
650
+ < . event_item type = { event . type } event = { event } last? = { index == length ( @ events ) - 1 } />
651
+ </ li >
652
+ </ ul >
653
+ """
654
+ end
655
+
656
+ defp event_item ( % { type: :transaction } = assigns ) do
657
+ assigns = assign ( assigns , :transaction , assigns . event . item )
658
+
659
+ ~H"""
660
+ < div >
661
+ < div class = "relative -ml-[2.75rem] " >
662
+ < span
663
+ :if = { ! @ last? }
664
+ class = "absolute left-1 top-6 h-full w-0.5 block ml-[2.75rem] bg-muted-foreground/25 "
665
+ aria-hidden = "true "
666
+ >
667
+ </ span >
668
+ < . link
669
+ rel = "noopener "
670
+ target = "_blank "
671
+ class = "w-full group inline-flex "
672
+ href = {
673
+ if @ transaction . ticket . repository ,
674
+ do: @ transaction . ticket . url ,
675
+ else: ~p" /#{ @ transaction . linked_transaction . user . handle } /home"
676
+ }
677
+ >
678
+ < div class = "w-full relative flex space-x-3 " >
679
+ < div class = "w-full flex min-w-0 flex-1 justify-between space-x-4 pt-1.5 " >
680
+ < div class = "w-full flex items-center gap-3 " >
681
+ < div class = "flex -space-x-1 ring-8 ring-black " >
682
+ < span class = "relative shrink-0 overflow-hidden flex h-9 w-9 sm:h-12 sm:w-12 items-center justify-center rounded-xl ring-4 bg-gray-950 ring-black " >
683
+ < img
684
+ class = "aspect-square h-full w-full "
685
+ alt = { @ transaction . user . name }
686
+ src = { @ transaction . user . avatar_url }
687
+ />
688
+ </ span >
689
+ < span class = "relative shrink-0 overflow-hidden flex h-9 w-9 sm:h-12 sm:w-12 items-center justify-center rounded-xl ring-4 bg-gray-950 ring-black " >
690
+ < img
691
+ class = "aspect-square h-full w-full "
692
+ alt = { @ transaction . linked_transaction . user . name }
693
+ src = { @ transaction . linked_transaction . user . avatar_url }
694
+ />
695
+ </ span >
696
+ </ div >
697
+ < div class = "w-full z-10 flex gap-3 items-start xl:items-end " >
698
+ < p class = "text-xs transition-colors text-muted-foreground group-hover:text-foreground/90 sm:text-xl text-left " >
699
+ < span class = "font-semibold text-foreground/80 group-hover:text-foreground transition-colors " >
700
+ { @ transaction . linked_transaction . user . name }
701
+ </ span >
702
+ awarded
703
+ < span class = "font-semibold text-foreground/80 group-hover:text-foreground transition-colors " >
704
+ { @ transaction . user . name }
705
+ </ span >
706
+ a
707
+ < span class = {
708
+ classes ( [
709
+ "font-bold font-display transition-colors" ,
710
+ cond do
711
+ @ transaction . bounty_id && @ transaction . ticket . repository ->
712
+ "text-success-400 group-hover:text-success-300"
713
+
714
+ @ transaction . bounty_id && ! @ transaction . ticket . repository ->
715
+ "text-blue-400 group-hover:text-blue-300"
716
+
717
+ true ->
718
+ "text-red-400 group-hover:text-red-300"
719
+ end
720
+ ] )
721
+ } >
722
+ { Money . to_string! ( @ transaction . net_amount ) }
723
+ <%= if @ transaction . bounty_id do %>
724
+ <%= if @ transaction . ticket . repository do %>
725
+ bounty
726
+ <% else %>
727
+ contract
728
+ <% end %>
729
+ <% else %>
730
+ tip
731
+ <% end %>
732
+ </ span >
733
+ </ p >
734
+ < div class = "ml-auto xl:ml-0 xl:mb-[2px] whitespace-nowrap text-xs text-muted-foreground sm:text-sm " >
735
+ < time datetime = { @ transaction . succeeded_at } >
736
+ { cond do
737
+ @ transaction . bounty_id && ! @ transaction . ticket . repository ->
738
+ start_month = Calendar . strftime ( @ transaction . succeeded_at , "%B" )
739
+ end_date = Date . add ( @ transaction . succeeded_at , 30 )
740
+ end_month = Calendar . strftime ( end_date , "%B" )
741
+
742
+ if start_month == end_month do
743
+ "#{ start_month } #{ Calendar . strftime ( end_date , "%Y" ) } "
744
+ else
745
+ "#{ start_month } - #{ end_month } #{ Calendar . strftime ( end_date , "%Y" ) } "
746
+ end
747
+
748
+ true ->
749
+ Algora.Util . time_ago ( @ transaction . succeeded_at )
750
+ end }
751
+ </ time >
752
+ </ div >
753
+ </ div >
754
+ </ div >
755
+ </ div >
756
+ </ div >
757
+ </ . link >
758
+ </ div >
759
+ </ div >
760
+ """
761
+ end
762
+
763
+ defp event_item ( % { type: :job } = assigns ) do
764
+ assigns = assign ( assigns , :job , assigns . event . item )
765
+
766
+ ~H"""
767
+ < div :if = { @ job . user . handle } >
768
+ < div class = "relative -ml-[2.75rem] " >
769
+ < span
770
+ :if = { ! @ last? }
771
+ class = "absolute left-1 top-6 h-full w-0.5 block ml-[2.75rem] bg-muted-foreground/25 "
772
+ aria-hidden = "true "
773
+ >
774
+ </ span >
775
+ < . link
776
+ rel = "noopener "
777
+ target = "_blank "
778
+ class = "w-full group inline-flex "
779
+ href = { ~p" /#{ @ job . user . handle } /jobs" }
780
+ >
781
+ < div class = "w-full relative flex space-x-3 " >
782
+ < div class = "w-full flex min-w-0 flex-1 justify-between space-x-4 pt-1.5 " >
783
+ < div class = "w-full flex items-center gap-3 " >
784
+ < div class = "flex -space-x-1 ring-8 ring-black " >
785
+ < span class = "ml-6 relative shrink-0 overflow-hidden flex h-9 w-9 sm:h-12 sm:w-12 items-center justify-center rounded-xl " >
786
+ < img
787
+ class = "aspect-square h-full w-full "
788
+ alt = { @ job . user . name }
789
+ src = { @ job . user . avatar_url }
790
+ />
791
+ </ span >
792
+ </ div >
793
+ < div class = "w-full z-10 flex gap-3 items-start xl:items-end " >
794
+ < p class = "text-xs transition-colors text-muted-foreground group-hover:text-foreground/90 sm:text-xl text-left " >
795
+ < span class = "font-semibold text-foreground/80 group-hover:text-foreground transition-colors " >
796
+ { @ job . user . name }
797
+ </ span >
798
+ is hiring!
799
+ < span class = "font-semibold text-purple-400 group-hover:text-purple-300 transition-colors " >
800
+ { @ job . title }
801
+ </ span >
802
+ </ p >
803
+ < div class = "ml-auto xl:ml-0 xl:mb-[2px] whitespace-nowrap text-xs text-muted-foreground sm:text-sm " >
804
+ < time datetime = { @ job . inserted_at } >
805
+ { Algora.Util . time_ago ( @ job . inserted_at ) }
806
+ </ time >
807
+ </ div >
808
+ </ div >
809
+ </ div >
810
+ </ div >
811
+ </ div >
812
+ </ . link >
813
+ </ div >
814
+ </ div >
815
+ """
816
+ end
817
+
818
+ defp event_item ( % { type: :bounty } = assigns ) do
819
+ assigns = assign ( assigns , :bounty , assigns . event . item )
820
+
821
+ ~H"""
822
+ < div >
823
+ < div class = "relative -ml-[2.75rem] " >
824
+ < span
825
+ :if = { ! @ last? }
826
+ class = "absolute left-1 top-6 h-full w-0.5 block ml-[2.75rem] bg-muted-foreground/25 "
827
+ aria-hidden = "true "
828
+ >
829
+ </ span >
830
+ < . link
831
+ rel = "noopener "
832
+ target = "_blank "
833
+ class = "w-full group inline-flex "
834
+ href = {
835
+ if @ bounty . repository ,
836
+ do: @ bounty . ticket . url ,
837
+ else: ~p" /#{ @ bounty . owner . handle } /home"
838
+ }
839
+ >
840
+ < div class = "w-full relative flex space-x-3 " >
841
+ < div class = "w-full flex min-w-0 flex-1 justify-between space-x-4 pt-1.5 " >
842
+ < div class = "w-full flex items-center gap-3 " >
843
+ < div class = "flex -space-x-1 ring-8 ring-black " >
844
+ < span class = "ml-6 relative shrink-0 overflow-hidden flex h-9 w-9 sm:h-12 sm:w-12 items-center justify-center rounded-xl bg-gray-950 " >
845
+ < img
846
+ class = "aspect-square h-full w-full "
847
+ alt = { @ bounty . owner . name }
848
+ src = { @ bounty . owner . avatar_url }
849
+ />
850
+ </ span >
851
+ </ div >
852
+ < div class = "w-full z-10 flex gap-3 items-start xl:items-end " >
853
+ < p class = "text-xs transition-colors text-muted-foreground group-hover:text-foreground/90 sm:text-xl text-left " >
854
+ < span class = "font-semibold text-foreground/80 group-hover:text-foreground transition-colors " >
855
+ { @ bounty . owner . name }
856
+ </ span >
857
+ shared a
858
+ < span class = "font-bold font-display transition-colors text-cyan-400 group-hover:text-cyan-300 " >
859
+ { Money . to_string! ( @ bounty . amount ) } bounty
860
+ </ span >
861
+ </ p >
862
+ < div class = "ml-auto xl:ml-0 xl:mb-[2px] whitespace-nowrap text-xs text-muted-foreground sm:text-sm " >
863
+ < time datetime = { @ bounty . inserted_at } >
864
+ { Algora.Util . time_ago ( @ bounty . inserted_at ) }
865
+ </ time >
866
+ </ div >
867
+ </ div >
868
+ </ div >
869
+ </ div >
870
+ </ div >
871
+ </ . link >
872
+ </ div >
873
+ </ div >
874
+ """
875
+ end
876
+
700
877
defp yc_logo_cloud ( assigns ) do
701
878
~H"""
702
879
< div >
@@ -1024,4 +1201,27 @@ defmodule AlgoraWeb.HomeLive do
1024
1201
</ . drawer >
1025
1202
"""
1026
1203
end
1204
+
1205
+ defp assign_events ( socket ) do
1206
+ transactions = Payments . list_featured_transactions ( )
1207
+ bounties = Bounties . list_bounties ( status: :open , limit: 10 )
1208
+ jobs_by_user = [ ]
1209
+
1210
+ events =
1211
+ transactions
1212
+ |> Enum . map ( fn tx -> % { item: tx , type: :transaction , timestamp: tx . succeeded_at } end )
1213
+ |> Enum . concat (
1214
+ jobs_by_user
1215
+ |> Enum . flat_map ( fn { _user , jobs } -> jobs end )
1216
+ |> Enum . map ( fn job -> % { item: job , type: :job , timestamp: job . inserted_at } end )
1217
+ )
1218
+ |> Enum . concat (
1219
+ Enum . map ( bounties || [ ] , fn bounty ->
1220
+ % { item: bounty , type: :bounty , timestamp: bounty . inserted_at }
1221
+ end )
1222
+ )
1223
+ |> Enum . sort_by ( & & 1 . timestamp , { :desc , DateTime } )
1224
+
1225
+ assign ( socket , :events , events )
1226
+ end
1027
1227
end
0 commit comments