<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitry Semenov</title>
    <description>The latest articles on DEV Community by Dmitry Semenov (@dmitrysemenov).</description>
    <link>https://dev.to/dmitrysemenov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F786084%2F1e641cf7-441b-4dc2-9de6-d764e6fd6af8.jpeg</url>
      <title>DEV Community: Dmitry Semenov</title>
      <link>https://dev.to/dmitrysemenov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmitrysemenov"/>
    <language>en</language>
    <item>
      <title>Device limit reached for this Apple ID - Solution</title>
      <dc:creator>Dmitry Semenov</dc:creator>
      <pubDate>Wed, 05 Jun 2024 14:46:28 +0000</pubDate>
      <link>https://dev.to/dmitrysemenov/device-limit-reached-for-this-apple-id-solution-16ak</link>
      <guid>https://dev.to/dmitrysemenov/device-limit-reached-for-this-apple-id-solution-16ak</guid>
      <description>&lt;p&gt;During the new device onboarding process, if you have been using the Apple ecosystem for a while and have changed multiple devices over time, you may encounter the Device Limit issue that Apple displays when you try to use your device with Apple Music (and likely other media apps as well) but you have a number of devices already associated with your Apple ID.&lt;/p&gt;

&lt;p&gt;Apple's instructions for addressing this issue are outdated. They refer to buttons that no longer exist, likely due to previous operating system versions. Additionally, a solution to this problem does not appear to have been posted online prior to &lt;a href="https://www.reddit.com/r/iphone/comments/1at4bg1/comment/kzcmolp/"&gt;my comment&lt;/a&gt; on Reddit. It received significant attention, so I decided to cross-post the solution here to help more people find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  MacOS solution
&lt;/h2&gt;

&lt;p&gt;Follow these steps to access the interface that allows you to remove old devices from your media limit count.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;System settings&lt;/li&gt;
&lt;li&gt;Apple ID&lt;/li&gt;
&lt;li&gt;Media &amp;amp; Purchases&lt;/li&gt;
&lt;li&gt;Account - Manage&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hidden Items section - Manage Devices&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii760xgu2ccgom17xzow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii760xgu2ccgom17xzow.png" alt="hidden items" width="800" height="122"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click "Remove" on each device you no longer use, allowing you to access Apple Music on your new device.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feijgz3vrq0f1wvbhskqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feijgz3vrq0f1wvbhskqc.png" alt="manage devices" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enjoy!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>apple</category>
    </item>
    <item>
      <title>Remote power monitoring</title>
      <dc:creator>Dmitry Semenov</dc:creator>
      <pubDate>Wed, 05 Jun 2024 14:44:11 +0000</pubDate>
      <link>https://dev.to/dmitrysemenov/remote-power-monitoring-70a</link>
      <guid>https://dev.to/dmitrysemenov/remote-power-monitoring-70a</guid>
      <description>&lt;p&gt;The power delivery in Ukraine currently faces challenges due to a power deficit caused by Russian terrorist attacks on the Ukrainian infrastructure. As the balance between consumption and generation fluctuates, periodic shutdowns occur in different districts based on a semi-accurate schedule.&lt;/p&gt;

&lt;p&gt;My apartment is on a high floor, higher than most people are willing to climb by stairs. Typically, during a power outage, you would prefer to spend more time outside. Thus, my family and I need to know when the electricity is on or off, no matter where we are.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Goals
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Receive notifications when power goes online and offline.&lt;/li&gt;
&lt;li&gt;Gain insight into power outage patterns and durations to improve home routines and travel planning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Potential Solutions
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Rely on one of the smart devices that already sends notifications when it goes offline.&lt;/li&gt;
&lt;li&gt;Leverage the full capabilities of homelab infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t think I need to explain why the latter option is the best one, right?&lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;One power independent server&lt;/li&gt;
&lt;li&gt;One indicator of the electricity presence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a power-independent server, I’ll use my existing AWS Lightsail instance, which I maintain for my pet projects. It will handle all management tasks, including checking the home power status, keeping a history, and sending notifications. To check if my home has electricity, I’ll use a network device with a persistent IP address that is always connected to the network. There are countless ways to check if the home is offline, but the method I chose fits well with my secondary goal of trying out the Tailscale subnet router feature.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tailscale
&lt;/h1&gt;

&lt;p&gt;Tailscale is an app that creates a flexible and easy-to-use software-defined network on top of the fast and secure WireGuard protocol. I have my own network that I can access from anywhere in the world. This network allows me to connect to any device I’ve joined, with a granular access control list (ACL) that lets me decide which devices can see and access specific resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2pqa3u0xrgmuo79idmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2pqa3u0xrgmuo79idmm.png" alt="tailnet" width="451" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this project, an additional server acts as a Tailnet subnet router. It connects the device used to detect electricity status to the Tailnet since direct installation on the device is impossible. Although I could use this server instead of the AWS one, I prefer the Lightsail instance due to the Uptime Kuma app already installed there.&lt;/p&gt;

&lt;h1&gt;
  
  
  Uptime Kuma
&lt;/h1&gt;

&lt;p&gt;The core of my monitoring system is Uptime Kuma, a self-hosted monitoring tool that supports various monitoring methods such as HTTP(S) requests, ping, gRPC, DNS, and others. To check device connectivity, I’ll use a simple ping through Tailnet. As long as the ping is successful, it indicates the device is connected to the network and powered on. After configuring the ping monitor, I immediately started collecting outage events, addressing one of my initial goals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlxs2zxu6po4xcfeyefz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlxs2zxu6po4xcfeyefz.png" alt="uptime kuma" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Notifications
&lt;/h1&gt;

&lt;p&gt;To inform my family about the power status, relying solely on the status page is insufficient. Uptime Kuma offers a variety of notification options out of the box. I tested two: webhooks and Telegram. Using webhooks with n8n allowed me to customize texts or payloads sent to users, including AI-powered jokes. However, after trying both, I opted for native Telegram notifications for their reliability, avoiding unnecessary complications. While this means fewer fun messages, it reduces dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felsomod5q7u8tey14wro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felsomod5q7u8tey14wro.png" alt="telegram notifications" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Future Enhancements
&lt;/h1&gt;

&lt;p&gt;Initially, I considered using the EcoFlow API, which not only indicates power status but also provides data on current backup battery charge. However, they require a business week to review API access requests, which delayed this integration. Once approved, this can be a great addition to the intranet monitoring system in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ocjntnk9jqawyunzwbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ocjntnk9jqawyunzwbj.png" alt="EcoFlow under review" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
    </item>
    <item>
      <title>Use Slugs instead of IDs</title>
      <dc:creator>Dmitry Semenov</dc:creator>
      <pubDate>Sun, 20 Feb 2022 00:38:46 +0000</pubDate>
      <link>https://dev.to/dmitrysemenov/phoenix-how-to-use-slugs-instead-of-ids-47kc</link>
      <guid>https://dev.to/dmitrysemenov/phoenix-how-to-use-slugs-instead-of-ids-47kc</guid>
      <description>&lt;p&gt;While articles with the entire feature development process can be attractive to a couple of visitors, it appears that How-To's for a specific topic is way easier to find in the search engines. I'll keep posting project-related articles to share some product/discovery cases and focus more on guides for tech topics.&lt;/p&gt;

&lt;p&gt;This time let's talk about slugs in the Phoenix framework. Slug is the part of the URL that uses human-readable keywords to identify a page. Slug is the first thing you want to support for blog post URLs, public profiles, FAQ articles, etc.&lt;br&gt;
Generators of the Phoenix framework use IDs by default, and you have to let the framework know about your intention to use slugs instead.&lt;br&gt;
So let's see the steps required to use slugs instead of IDs in your URL.&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic slug support
&lt;/h3&gt;

&lt;p&gt;First, define a method the app will use to search your objects using the slug, name, title, or any other field of your choice.&lt;br&gt;
You can put this near &lt;code&gt;get_organization!&lt;/code&gt; method that the CRUD generator created for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_by_slug!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_by!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;slug:&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now replace &lt;code&gt;get_organization!&lt;/code&gt; calls in all live/organizations methods that you'll find (show, edit, delete actions should use this to find an organization by &lt;code&gt;id&lt;/code&gt;) with brand new &lt;code&gt;get_by_slug!&lt;/code&gt; call.&lt;br&gt;
It won't work yet since we need to tell &lt;code&gt;Phoenix&lt;/code&gt; that we changed a key parameter from &lt;code&gt;id&lt;/code&gt; to &lt;code&gt;slug&lt;/code&gt;. Add this at the top of your &lt;code&gt;organizations&lt;/code&gt; schema to achieve this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="nv"&gt;@derive&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;key:&lt;/span&gt; &lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Param.html#module-custom-parameters"&gt;Phoenix.Param&lt;/a&gt; protocol here.&lt;/p&gt;

&lt;p&gt;We have covered the happy path scenario, where users click URLs generated by the framework, but we should also consider users who can type URLs in their browser window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle MixedCase uniqueness
&lt;/h3&gt;

&lt;p&gt;My organization slug is &lt;code&gt;Fluxdash.&lt;/code&gt; I still want to enter &lt;code&gt;fluxdash&lt;/code&gt; and get the same results. We also wish to treat slugs' uniqueness despite CaseSelection.&lt;br&gt;
To get this done, we need to create a new unique index that will apply the &lt;code&gt;lower()&lt;/code&gt; function to the passed &lt;code&gt;slug.&lt;/code&gt; Let's generate new migration for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="n"&gt;mix&lt;/span&gt; &lt;span class="n"&gt;ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;migration&lt;/span&gt; &lt;span class="n"&gt;add_organization_slug_unique_lower_index&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:organizations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"lower(slug)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;unique:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're here from the first post where we created the organization's table, you should also drop the unique index that we added at the beginning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="n"&gt;drop_if_exists&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"organizations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;slug:&lt;/span&gt; &lt;span class="ss"&gt;:organizations_slug_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, as we also got a new index slug, we must reflect that in our constraint validation in the changeset; let's put the new index slug here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unique_constraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;slug:&lt;/span&gt; &lt;span class="ss"&gt;:organizations_lower_slug_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we should update our &lt;code&gt;get_by_slug!&lt;/code&gt; function to use the index, and downcase input string to perform case agnostic searches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_by_slug!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;one!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lower(?)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use show/edit/delete with organization slugs instead of IDs.&lt;/p&gt;

&lt;p&gt;If you need to generate slugs for more complex scenarios, such as blog titles without strict format requirements, take a look at the &lt;a href="https://github.com/sobolevn/ecto_autoslug_field"&gt;https://github.com/sobolevn/ecto_autoslug_field&lt;/a&gt; library, which can generate slugs based on the existing field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update routes to use &lt;code&gt;slug&lt;/code&gt; param name
&lt;/h3&gt;

&lt;p&gt;If you have both types of routes, with &lt;code&gt;id&lt;/code&gt;s and &lt;code&gt;slug&lt;/code&gt;s, it makes sense to adjust router with expected attribute name to avoid confusions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# lib/fluxdash_web/live/organization_live/show.ex&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_params&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="s2"&gt;"slug"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# routes.ex&lt;/span&gt;
  &lt;span class="n"&gt;live&lt;/span&gt; &lt;span class="s2"&gt;"/organizations/:slug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OrganizationLive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PS. Originally posted as part of &lt;a href="https://monobit.dev/blog/fluxdash-organizations-3-details/#use-organization-name-in-urls"&gt;Fluxdash Organizations&lt;/a&gt; series.&lt;/p&gt;

</description>
      <category>phoenix</category>
      <category>howto</category>
      <category>webdev</category>
      <category>elixir</category>
    </item>
    <item>
      <title>Phoenix LiveView Tailwind CSS Modal</title>
      <dc:creator>Dmitry Semenov</dc:creator>
      <pubDate>Tue, 04 Jan 2022 20:59:45 +0000</pubDate>
      <link>https://dev.to/dmitrysemenov/phoenix-liveview-tailwind-css-modal-1j38</link>
      <guid>https://dev.to/dmitrysemenov/phoenix-liveview-tailwind-css-modal-1j38</guid>
      <description>&lt;p&gt;It took me a while to figure out how to style a modal window with Tailwind CSS (had to Google how to center a div), so want to share what I've got for anyone who will be searching for LiveView Tailwind CSS Modal styles.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;assigns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assign_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:return_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;div id="&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;overflow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;fixed&lt;/span&gt; &lt;span class="n"&gt;inset&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="s2"&gt;" phx-remove={hide_modal()}&amp;gt;
      &amp;lt;div class="&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;justify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;div class="&lt;/span&gt;&lt;span class="n"&gt;fixed&lt;/span&gt; &lt;span class="n"&gt;inset&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="s2"&gt;" aria-hidden="&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
          &amp;lt;div class="&lt;/span&gt;&lt;span class="n"&gt;absolute&lt;/span&gt; &lt;span class="n"&gt;inset&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gray&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;div id="&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="n"&gt;overflow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bottom&lt;/span&gt; &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt; &lt;span class="n"&gt;rounded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lg&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xl&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;middle&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lg&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="ss"&gt;sm:&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="s2"&gt;" role="&lt;/span&gt;&lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="s2"&gt;" aria-modal="&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="s2"&gt;" aria-labelledby="&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;headline&lt;/span&gt;&lt;span class="s2"&gt;"
            phx-click-away={JS.dispatch("&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="s2"&gt;", to: "&lt;/span&gt;&lt;span class="c1"&gt;#modal-close")}&lt;/span&gt;
            &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keydown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;JS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="s2"&gt;"#modal-close"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
            &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"escape"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"relative"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;@return_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;live_patch&lt;/span&gt; &lt;span class="s2"&gt;"✖"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="s2"&gt;"modal-close"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="nv"&gt;@return_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;phx_click:&lt;/span&gt; &lt;span class="n"&gt;hide_modal&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="ss"&gt;class:&lt;/span&gt; &lt;span class="s2"&gt;"inline-flex absolute right-0 text-gray-400 bg-white rounded-md phx-modal-close hover:text-gray-500 focus:outline-none"&lt;/span&gt;
                &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
             &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"close"&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"#"&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"inline-flex absolute right-0 text-gray-400 bg-white rounded-md phx-modal-close hover:text-gray-500 focus:outline-none"&lt;/span&gt; &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hide_modal&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;✖&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render_slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@inner_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;"""
  end


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And this is how it looks now.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvey1cmdt0hhif1wue39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvey1cmdt0hhif1wue39.png" alt="Modal example"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested with:&lt;/strong&gt; phoenix_live_view: 0.17.5, tailwindcss: 3.0.2.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>liveview</category>
    </item>
  </channel>
</rss>
