diff --git a/lib/algora/payments/payments.ex b/lib/algora/payments/payments.ex index 03bccf526..60c6c7aeb 100644 --- a/lib/algora/payments/payments.ex +++ b/lib/algora/payments/payments.ex @@ -838,4 +838,61 @@ defmodule Algora.Payments do end end) end + + def list_featured_transactions do + tx_query = + from(tx in Transaction, + where: tx.type == :credit, + where: not is_nil(tx.succeeded_at), + join: u in assoc(tx, :user), + left_join: b in assoc(tx, :bounty), + left_join: tip in assoc(tx, :tip), + join: t in Ticket, + on: t.id == b.ticket_id or t.id == tip.ticket_id, + left_join: r in assoc(t, :repository), + left_join: o in assoc(r, :user), + join: ltx in assoc(tx, :linked_transaction), + join: ltx_user in assoc(ltx, :user), + select: %{ + id: tx.id, + succeeded_at: tx.succeeded_at, + net_amount: tx.net_amount, + bounty_id: b.id, + tip_id: tip.id, + user: u, + ticket: %{t | repository: %{r | user: o}}, + linked_transaction: %{ltx | user: ltx_user} + } + ) + + # case Algora.Settings.get_featured_transactions() do + # ids when is_list(ids) and ids != [] -> + # where(tx_query, [tx], tx.id in ^ids) + # _ -> + tx_query = + tx_query + |> where([tx], tx.succeeded_at > ago(2, "week")) + |> order_by([tx], desc: tx.net_amount) + |> limit(10) + + # end + + transactions = + tx_query + |> Repo.all() + |> Enum.reduce(%{}, fn tx, acc -> + # Group transactions by bounty_id when repository is null and bounty_id exists + if tx.bounty_id && !tx.ticket.repository do + Map.update(acc, tx.bounty_id, tx, fn existing -> + %{existing | net_amount: Money.add!(existing.net_amount, tx.net_amount)} + end) + else + # Keep other transactions as is + Map.put(acc, tx.id, tx) + end + end) + |> Map.values() + + Enum.sort_by(transactions, & &1.succeeded_at, {:desc, DateTime}) + end end diff --git a/lib/algora_web/components/header.ex b/lib/algora_web/components/header.ex index 59168575d..b445c04aa 100644 --- a/lib/algora_web/components/header.ex +++ b/lib/algora_web/components/header.ex @@ -7,7 +7,8 @@ defmodule AlgoraWeb.Components.Header do defp nav_links do [ - %{name: "Community", path: ~p"/community"}, + %{name: "Bounties", path: ~p"/bounties"}, + %{name: "Jobs", path: ~p"/jobs"}, %{name: "Docs", path: ~p"/docs"}, %{name: "Pricing", path: ~p"/pricing"} ] diff --git a/lib/algora_web/live/community_live.ex b/lib/algora_web/live/community_live.ex index e5b0aa335..92835c8ba 100644 --- a/lib/algora_web/live/community_live.ex +++ b/lib/algora_web/live/community_live.ex @@ -11,6 +11,7 @@ defmodule AlgoraWeb.CommunityLive do alias Algora.Accounts alias Algora.Accounts.User alias Algora.Bounties + alias Algora.Payments alias Algora.Payments.Transaction alias Algora.Repo alias Algora.Workspace @@ -62,45 +63,6 @@ defmodule AlgoraWeb.CommunityLive do featured_devs = Accounts.list_featured_developers() featured_collabs = list_featured_collabs() - tx_query = - from(tx in Transaction, - where: tx.type == :credit, - where: not is_nil(tx.succeeded_at), - join: u in assoc(tx, :user), - left_join: b in assoc(tx, :bounty), - left_join: tip in assoc(tx, :tip), - join: t in Ticket, - on: t.id == b.ticket_id or t.id == tip.ticket_id, - join: r in assoc(t, :repository), - join: o in assoc(r, :user), - join: ltx in assoc(tx, :linked_transaction), - join: ltx_user in assoc(ltx, :user), - select: %{ - id: tx.id, - succeeded_at: tx.succeeded_at, - net_amount: tx.net_amount, - bounty_id: b.id, - tip_id: tip.id, - user: u, - ticket: %{t | repository: %{r | user: o}}, - linked_transaction: %{ltx | user: ltx_user} - } - ) - - tx_query = - case Algora.Settings.get_featured_transactions() do - ids when is_list(ids) and ids != [] -> - where(tx_query, [tx], tx.id in ^ids) - - _ -> - tx_query - |> where([tx], tx.succeeded_at > ago(1, "week")) - |> order_by([tx], desc: tx.net_amount) - |> limit(5) - end - - transactions = tx_query |> Repo.all() |> Enum.sort_by(& &1.succeeded_at, {:desc, DateTime}) - {:ok, socket |> assign(:screenshot?, not is_nil(params["screenshot"])) @@ -117,7 +79,7 @@ defmodule AlgoraWeb.CommunityLive do |> assign(:selected_developer, nil) |> assign(:share_drawer_type, nil) |> assign(:show_share_drawer, false) - |> assign(:transactions, transactions) + |> assign(:transactions, Payments.list_featured_transactions()) |> assign_query_opts(params["tech"]) |> assign_bounties()} end @@ -1194,6 +1156,18 @@ defmodule AlgoraWeb.CommunityLive do {:noreply, assign_bounties(socket)} end + @impl true + def handle_event("create_bounty", %{"bounty_form" => params}, socket) do + Algora.Admin.alert("Bounty intent: #{inspect(params)}", :critical) + {:noreply, redirect(socket, to: ~p"/auth/signup")} + end + + @impl true + def handle_event("create_tip", %{"tip_form" => params}, socket) do + Algora.Admin.alert("Tip intent: #{inspect(params)}", :critical) + {:noreply, redirect(socket, to: ~p"/auth/signup")} + end + @impl true def handle_event("load_more", _params, socket) do %{bounties: bounties} = socket.assigns diff --git a/lib/algora_web/live/home_live.ex b/lib/algora_web/live/home_live.ex index 9e3629633..94df8aef8 100644 --- a/lib/algora_web/live/home_live.ex +++ b/lib/algora_web/live/home_live.ex @@ -9,13 +9,17 @@ defmodule AlgoraWeb.HomeLive do alias Algora.Accounts alias Algora.Accounts.User + alias Algora.Bounties alias Algora.Jobs alias Algora.Jobs.JobPosting + alias Algora.Payments alias Algora.Payments.Transaction alias Algora.Repo alias AlgoraWeb.Components.Footer alias AlgoraWeb.Components.Header alias AlgoraWeb.Data.PlatformStats + alias AlgoraWeb.Forms.BountyForm + alias AlgoraWeb.Forms.TipForm alias Phoenix.LiveView.AsyncResult require Logger @@ -36,6 +40,13 @@ defmodule AlgoraWeb.HomeLive do jobs_by_user = Enum.group_by(Jobs.list_jobs(), & &1.user) + bounties = + Bounties.list_bounties( + status: :open, + limit: 3, + amount_gt: Money.new(:USD, 500) + ) + case socket.assigns[:current_user] do %{handle: handle} = user when is_binary(handle) -> {:ok, redirect(socket, to: AlgoraWeb.UserAuth.signed_in_path(user))} @@ -47,15 +58,39 @@ defmodule AlgoraWeb.HomeLive do |> assign(:page_title_suffix, "") |> assign(:page_image, "#{AlgoraWeb.Endpoint.url()}/images/og/home.png") |> assign(:screenshot?, not is_nil(params["screenshot"])) + |> assign(:bounty_form, to_form(BountyForm.changeset(%BountyForm{}, %{}))) + |> assign(:tip_form, to_form(TipForm.changeset(%TipForm{}, %{}))) |> assign(:featured_devs, featured_devs) |> assign(:stats, stats) |> assign(:form, to_form(JobPosting.changeset(%JobPosting{}, %{}))) |> assign(:jobs_by_user, jobs_by_user) |> assign(:user_metadata, AsyncResult.loading()) + |> assign(:transactions, Payments.list_featured_transactions()) + |> assign(:bounties, bounties) + |> assign_events() |> assign_user_applications()} end end + defp assign_events(socket) do + events = + (socket.assigns.transactions || []) + |> Enum.map(fn tx -> %{item: tx, type: :transaction, timestamp: tx.succeeded_at} end) + |> Enum.concat( + (socket.assigns.jobs_by_user || []) + |> Enum.flat_map(fn {_user, jobs} -> jobs end) + |> Enum.map(fn job -> %{item: job, type: :job, timestamp: job.inserted_at} end) + ) + |> Enum.concat( + Enum.map(socket.assigns.bounties || [], fn bounty -> + %{item: bounty, type: :bounty, timestamp: bounty.inserted_at} + end) + ) + |> Enum.sort_by(& &1.timestamp, {:desc, DateTime}) + + assign(socket, :events, events) + end + @impl true def render(assigns) do ~H""" @@ -67,27 +102,31 @@ defmodule AlgoraWeb.HomeLive do <% end %>
-
- <.pattern /> +
<.wordmark :if={@screenshot?} class="h-8 mb-6" /> + <%!--

+ Bounties, contracts & jobs +
for open source engineers +

--%>

- Hire the top 1% -
open source engineers + Bounties and jobs +
for open source engineers

- Algora connects companies and engineers
- for full-time and contract work + Algora connects companies and developers +
for full-time and contract work.

-
+ <%!--
<.button navigate={~p"/onboarding/org"} class="h-10 sm:h-14 rounded-md px-8 sm:px-12 text-sm sm:text-xl" @@ -101,10 +140,10 @@ defmodule AlgoraWeb.HomeLive do > Developers -
- <%!--
- <.events transactions={@transactions} />
--%> +
+ <.events events={@events} /> +
<%!--
-
- <%= if not Enum.empty?(@jobs_by_user) do %> - <.section> -
- <%= for {user, jobs} <- @jobs_by_user do %> - <.card class="flex flex-col p-6"> -
- <.avatar class="h-16 w-16"> - <.avatar_image src={user.avatar_url} /> - <.avatar_fallback> - {Algora.Util.initials(user.name)} - - -
-
- {user.name} -
-
- {user.bio} -
-
- <%= for {platform, icon} <- social_icons(), - url = social_link(user, platform), - not is_nil(url) do %> - <.link - href={url} - target="_blank" - class="text-muted-foreground hover:text-foreground" - > - <.icon name={icon} class="size-4" /> - - <% end %> -
-
+
+
+

+ Fund GitHub issues +

+

+ Add USD rewards on issues and pay when work is merged +

+
+
+
+
+ Fund any issue
+ + in seconds + +
+
+ Help improve the OSS you love and rely on +
+
+
+ <.icon name="tabler-check" class="h-5 w-5 text-success-400 flex-none" /> + Pay when PRs are merged
- -
- <%= for job <- jobs do %> -
-
-
-
- {job.title} -
-
-
-
- {Phoenix.HTML.raw(Algora.Markdown.render(job.description))} -
- -
-
- <%= for tech <- job.tech_stack do %> - <.badge variant="outline"> - - {tech} - - - <% end %> -
-
- <%= if MapSet.member?(@user_applications, job.id) do %> - <.button disabled class="opacity-50"> - <.icon name="tabler-check" class="h-4 w-4 mr-2 -ml-1" /> Applied - - <% else %> - <.button phx-click="apply_job" phx-value-job-id={job.id}> - <.icon name="github" class="h-4 w-4 mr-2" /> Apply with GitHub - - <% end %> -
- <% end %> +
+ <.icon name="tabler-check" class="h-5 w-5 text-success-400 flex-none" /> + Pool bounties with other sponsors
- - <% end %> +
+ <.icon name="tabler-check" class="h-5 w-5 text-success-400 flex-none" /> + Algora handles payouts, compliance & 1099s +
+
+
+ <.button + phx-click="create_bounty" + size="xl" + class="w-full text-lg drop-shadow-[0_1px_5px_#34d39980]" + > + Fund issues + +
No credit card required
+
+
- - <% end %> - - <.section class="pt-24"> -

- - Screen on autopilot - -
for OSS contributions - -

-

- Receive applications on Algora
- or import your existing applicants.
Algora will highlight applicants with relevant OSS contribution history in your tech stack. -

-
+
+
+ <.link + href="https://github.com/zed-industries/zed/issues/4440" + rel="noopener" + target="_blank" + class="relative flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:gap-x-4 rounded-xl bg-black p-4 sm:p-6 ring-1 ring-border transition-colors" + > +
+ Zed + Scott Chacon +
+
+
+ GitHub cofounder funds new feature in Zed Editor +
+
+ Zed Editor, Scott Chacon +
+
+ <.button size="lg" variant="secondary" class="mt-2 sm:mt-0"> + <.icon name="github" class="size-5 mr-3" /> View issue + + + + <.link + href="https://github.com/PX4/PX4-Autopilot/issues/22464" + rel="noopener" + target="_blank" + class="relative flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:gap-x-4 rounded-xl bg-black p-4 sm:p-6 ring-1 ring-border transition-colors" + > +
+ Alex Klimaj + PX4 + Andrew Wilkins +
+
+
+ DefenceTech CEOs fund obstacle avoidance in PX4 Autopilot +
+
+ Alex Klimaj, Founder of ARK Electronics & Andrew Wilkins, CEO of Ascend Engineering +
+
+ <.button size="lg" variant="secondary" class="mt-2 sm:mt-0"> + <.icon name="github" class="size-5 mr-3" /> View issue + + + + <.link + href={~p"/coollabsio/bounties/community"} + rel="noopener" + class="relative flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:gap-x-4 rounded-xl bg-black p-4 sm:p-6 ring-1 ring-border transition-colors" + > +
+ Coolify + Andras Bacsai +
+
+
+ Coolify community crowdfunds new feature development +
+
+ Andras Bacsai, Founder of Coolify +
+
+ <.button size="lg" variant="secondary" class="flex mt-2 sm:mt-0 ring-2 ring-emerald-500"> + View bounty board + + +
+
+ +
+
+

+ Did you know?

-

- Access your top matches from Algora based on your tech stack and preferences +

+ You can tip your favorite open source contributors with Algora.

+
+
+
+
+

+ Tip contributors + instantly +

+

+ Support the maintainers of your favorite projects +

+
+
+ <.icon name="tabler-check" class="h-5 w-5 text-indigo-400 flex-none" /> + Send tips directly to GitHub usernames +
+
+ <.icon name="tabler-check" class="h-5 w-5 text-indigo-400 flex-none" /> + Algora handles payouts, compliance & 1099s +
+
+
+ +
+ <.button + size="lg" + class="w-full drop-shadow-[0_1px_5px_#818cf880]" + phx-click="create_tip" + variant="indigo" + > + Tip contributors + +
+
+
+ +
+ Tip contributor +
+
+
+
+ +
+
Algora dashboard - - - <.section class="pt-24"> -
-
-

- Interview with
- - paid projects - -

-

- Use bounties and contract work to trial your top candidates before hiring them full-time. -

+

+ Trusted by open source YC founders +

+
+ <.yc_logo_cloud /> +
-
-
-
- <.modal_video - class="aspect-[9/16] rounded-xl lg:rounded-2xl lg:rounded-r-none" - src="https://www.youtube.com/embed/xObOGcUdtY0" - start={122} - title="$15,000 Open source bounty to hire a Rust engineer" - poster={~p"/images/people/john-de-goes.jpg"} - alt="John A De Goes" - /> -
-
- <.link - href="https://github.com/golemcloud/golem/issues/1004" - rel="noopener" - target="_blank" - class="relative flex aspect-[1121/1343] w-full items-center justify-center overflow-hidden rounded-xl lg:rounded-2xl lg:rounded-l-none" - > - Golem bounty to hire - -
+
+
+
+
+ Tal Borenstein
- -
- Golem Team -
-
-

- I always wanted to work on some Rust project and get to use the language, but I never found a good opportunity to do so. Now with huge bounty attached to this issue, it was a very good opportunity to play around with Rust. -

-
-
- -
-
- Maxim Schuwalow -
-
- Software Engineer at Golem Cloud -
-
+
+

+ Keep has 90+ integrations to alert our customers about critical events. Of these, + <.link + href="https://github.com/keephq/keep/issues?q=state%3Aclosed%20label%3A%22%F0%9F%92%8E%20Bounty%22%20%20label%3A%22%F0%9F%92%B0%20Rewarded%22%20label%3AProvider%20" + rel="noopener" + target="_blank" + class="text-success inline-flex items-center hover:text-success-300" + > + 42 integrations <.icon name="tabler-external-link" class="size-5 ml-1 mb-4" /> + + were built + using bounties on Algora. +

+
+
+
+
+ Tal Borenstein
-
-
-
-

- After a few decades of actively hiring engineers, what I found out is, a lot of times people who are very active in open source software, those engineers make fantastic additions to your team. -

-
-
- - -
-
- John A De Goes -
-
- Founder & CEO at Golem Cloud -
-
+
+ Co-founder & CEO at Keep (YC W23)
+
+
+
+
-
-
-
- <.modal_video - class="rounded-xl lg:rounded-2xl" - src="https://www.youtube.com/embed/FXQVD02rfg8" - start={8} - title="How Nick got a job with Open Source Software" - poster="https://img.youtube.com/vi/FXQVD02rfg8/maxresdefault.jpg" - alt="Eric Allam" - /> -
-
-

- It was the easiest hire - because we already knew how great he was -

-
-
-
-
- Eric Allam -
-
- Co-founder & CTO at Trigger.dev (YC W23) -
-
+
+
+

+ Build product faster +

+

+ Use bounties for outcome-based contract work with full GitHub integration. +

+
+
+
+
+ Louis Beaumont +
+
+
+

+ I posted our bounty on Upwork + to try it, overall it's 1000x more friction + than OSS bounties with Algora. +

+
+
+
+
+ Louis Beaumont +
+
+ Co-founder & CEO at Screenpipe
+
+
-
-
-
- <.modal_video - class="rounded-xl lg:rounded-2xl" - src="https://www.youtube.com/embed/3wZGDuoPajk" - start={13} - title="OSS Bounties & Hiring engineers on Algora.io | Founder Testimonial" - poster="https://img.youtube.com/vi/3wZGDuoPajk/maxresdefault.jpg" - alt="Tushar Mathur" - /> -
-
-

- Bounties help us control our burn rate, get work done & meet new hires. I've made - 4 full-time hires - using Algora -

-
-
-
-
- Tushar Mathur -
-
- Founder & CEO at Tailcall -
-
+
+
+
+
+ Josh Pigford +
+
+
+

+ Let's offer a bounty + to say "Hey, someone please prioritize this, who has the skillset for it?" I think long term I'd like to make it a + very consistent + part of our development process. +

+
+
+
+
+ Josh Pigford +
+
+ Co-founder & CEO at Maybe
-
- - - <.section class="pt-24"> -

- - Hire the best - -
using open source -

-

- Source, screen, interview and onboard .
- Guarantee role fit and job performance. -

-
    -
  • -
    - <.icon name="tabler-speakerphone" class="size-8 text-success-300" /> -
    - - Reach 50K+ devs -
    with unlimited job postings -
    -
  • -
  • -
    - <.icon name="tabler-lock-open" class="size-8 text-success-300" /> -
    - - Access top 1% users -
    matching your preferences -
    -
  • -
  • -
    - <.icon name="tabler-wand" class="size-8 text-success-300" /> -
    - - Auto-rank applicants -
    for OSS contribution history -
    -
  • -
  • -
    - <.icon name="tabler-currency-dollar" class="size-8 text-success-300" /> -
    - - Trial top candidates -
    using contracts and bounties -
    -
  • -
- - - <.section class="pt-12 pb-20"> -
-
-
-
- <.simple_form - for={@form} - phx-change="validate_job" - phx-submit="create_job" - class="w-full space-y-6" - > -
- <.input - id="final_section_email" - field={@form[:email]} - label="Email" - data-domain-target - phx-hook="DeriveDomain" - phx-change="email_changed" - phx-debounce="300" - /> - <.input id="final_section_url" field={@form[:url]} label="Job Posting URL" /> - <.input - id="final_section_company_url" - field={@form[:company_url]} - label="Company URL" - data-domain-source - /> - <.input - id="final_section_company_name" - field={@form[:company_name]} - label="Company Name" - /> -
+
-
- <.button size="xl" class="w-full"> -
- Hire now -
- -
-
- <%= if logo = get_in(@user_metadata.result, [:org, :favicon_url]) do %> - - <% end %> -
-
- {get_change(@form.source, :company_name)} -
- <%= if description = get_in(@user_metadata.result, [:org, :og_description]) do %> -
- {description} -
- <% end %> -
-
+
+
+
+
+ John A De Goes +
+
+
+

+ We used Algora extensively at Ziverge to reward over + $115,000 + in bounties and introduce a whole + new generation of contributors + to the ZIO and Golem ecosystems. +

+
+
+
+
+ John A De Goes +
+
+ Founder & CEO at Ziverge
- +
- -
+
+
+ +
+
+

+ Join the open source economy +

+
+ <.button + navigate={~p"/onboarding/org"} + class="h-10 sm:h-14 rounded-md px-8 sm:px-12 text-sm sm:text-xl" + > + Companies + + <.button + navigate={~p"/onboarding/dev"} + variant="secondary" + class="h-10 sm:h-14 rounded-md px-8 sm:px-12 text-sm sm:text-xl" + > + Developers + +
+ <%!--
+ <.button navigate={~p"/auth/signup"}> + Get started + + <.button href={AlgoraWeb.Constants.get(:github_repo_url)} variant="secondary"> + <.icon name="github" class="size-4 mr-2 -ml-1" /> View source code + +
--%> +
+
@@ -584,6 +596,17 @@ defmodule AlgoraWeb.HomeLive do """ end + @impl true + def handle_event("create_bounty", _params, socket) do + {:noreply, redirect(socket, to: ~p"/auth/signup")} + end + + @impl true + def handle_event("create_tip", _params, socket) do + {:noreply, redirect(socket, to: ~p"/auth/signup")} + end + + @impl true def handle_event("email_changed", %{"job_posting" => %{"email" => email}}, socket) do if String.match?(email, ~r/^[^\s]+@[^\s]+$/i) do {:noreply, start_async(socket, :fetch_metadata, fn -> Algora.Crawler.fetch_user_metadata(email) end)} @@ -783,4 +806,461 @@ defmodule AlgoraWeb.HomeLive do assign(socket, :user_applications, user_applications) end + + defp events(assigns) do + ~H""" +
    +
  • Enum.with_index()} class="relative"> + <.event_item type={event.type} event={event} last?={index == length(@events) - 1} /> +
  • +
+ """ + end + + defp event_item(%{type: :transaction} = assigns) do + assigns = assign(assigns, :transaction, assigns.event.item) + + ~H""" +
+
+ + <.link + rel="noopener" + target="_blank" + class="w-full group inline-flex" + href={ + if @transaction.ticket.repository, + do: @transaction.ticket.url, + else: ~p"/#{@transaction.linked_transaction.user.handle}/home" + } + > +
+
+
+
+ + {@transaction.user.name} + + + {@transaction.linked_transaction.user.name} + +
+
+

+ + {@transaction.linked_transaction.user.name} + + awarded + + {@transaction.user.name} + + a + + "text-success-400 group-hover:text-success-300" + + @transaction.bounty_id && !@transaction.ticket.repository -> + "text-blue-400 group-hover:text-blue-300" + + true -> + "text-red-400 group-hover:text-red-300" + end + ]) + }> + {Money.to_string!(@transaction.net_amount)} + <%= if @transaction.bounty_id do %> + <%= if @transaction.ticket.repository do %> + bounty + <% else %> + contract + <% end %> + <% else %> + tip + <% end %> + +

+
+ +
+
+
+
+
+ +
+
+ """ + end + + defp event_item(%{type: :job} = assigns) do + assigns = assign(assigns, :job, assigns.event.item) + + ~H""" +
+
+ + <.link + rel="noopener" + target="_blank" + class="w-full group inline-flex" + href={~p"/#{@job.user.handle}/jobs"} + > +
+
+
+
+ + {@job.user.name} + +
+
+

+ + {@job.user.name} + + is hiring! + + {@job.title} + +

+
+ +
+
+
+
+
+ +
+
+ """ + end + + defp event_item(%{type: :bounty} = assigns) do + assigns = assign(assigns, :bounty, assigns.event.item) + + ~H""" +
+
+ + <.link + rel="noopener" + target="_blank" + class="w-full group inline-flex" + href={ + if @bounty.repository, + do: @bounty.ticket.url, + else: ~p"/#{@bounty.owner.handle}/home" + } + > +
+
+
+
+ + {@bounty.owner.name} + +
+
+

+ + {@bounty.owner.name} + + shared a + + {Money.to_string!(@bounty.amount)} bounty + +

+
+ +
+
+
+
+
+ +
+
+ """ + end + + defp user_features do + [ + %{ + title: "Bounties & contracts", + description: "Work on new projects and grow your career", + src: ~p"/images/screenshots/user-dashboard.png" + }, + %{ + title: "Your new resume", + description: "Showcase your open source contributions", + src: ~p"/images/screenshots/profile.png" + }, + %{ + title: "Embed on your site", + description: "Let anyone share a bounty/contract with you", + src: ~p"/images/screenshots/embed-profile.png" + }, + %{ + title: "Payment history", + description: "Monitor your earnings in real-time", + src: ~p"/images/screenshots/user-transactions.png" + } + ] + end + + defp yc_logo_cloud(assigns) do + ~H""" +
+
+ <.link + class="font-bold font-display text-base sm:text-4xl whitespace-nowrap flex items-center justify-center" + navigate={~p"/browser-use"} + > + Browser Use + + <.link class="relative flex items-center justify-center" navigate={~p"/outerbase"}> + + + + + + + + + + + + + + + + + + + + + + <.link class="relative flex items-center justify-center" navigate={~p"/triggerdotdev"}> + Trigger.dev + + <.link class="relative flex items-center justify-center" navigate={~p"/traceloop"}> + Traceloop + + <.link + class="font-bold font-display text-base sm:text-5xl whitespace-nowrap flex items-center justify-center" + navigate={~p"/trieve"} + > + Trieve logo Trieve + + <.link + class="font-bold font-display text-base sm:text-5xl whitespace-nowrap flex items-center justify-center" + navigate={~p"/twentyhq"} + > + + + + Twenty + + <.link class="relative flex items-center justify-center" navigate={~p"/aidenybai"}> + Million + + <.link class="relative flex items-center justify-center" navigate={~p"/moonrepo"}> + moon + + <.link class="relative flex items-center justify-center" navigate={~p"/dittofeed"}> + Dittofeed + + + <.link + class="relative flex items-center justify-center brightness-0 invert" + navigate={~p"/onyx-dot-app"} + > + Onyx Logo + + + <.link + class="font-bold font-display text-base sm:text-4xl whitespace-nowrap flex items-center justify-center brightness-0 invert" + aria-label="Logo" + navigate={~p"/mendableai"} + > + 🔥 + Firecrawl + + + <.link class="relative flex items-center justify-center" navigate={~p"/keephq"}> + Keep + + + <.link + class="font-bold font-display text-base sm:text-5xl whitespace-nowrap flex items-center justify-center" + navigate={~p"/windmill-labs"} + > + Windmill Windmill + + + <.link class="relative flex items-center justify-center" navigate={~p"/panoratech"}> + Panora + + + <.link class="relative flex items-center justify-center" navigate={~p"/highlight"}> + Highlight + +
+
+ """ + end end