<?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: πn</title>
    <description>The latest articles on DEV Community by πn (@ptbdtq).</description>
    <link>https://dev.to/ptbdtq</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%2F1408910%2F0a00c928-2f60-4e4a-88c5-007023b2ac0f.jpg</url>
      <title>DEV Community: πn</title>
      <link>https://dev.to/ptbdtq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ptbdtq"/>
    <language>en</language>
    <item>
      <title>CustomsBuddy: Your customs broker for international parcels</title>
      <dc:creator>πn</dc:creator>
      <pubDate>Wed, 04 Mar 2026 20:06:54 +0000</pubDate>
      <link>https://dev.to/ptbdtq/customsbuddy-your-customs-broker-for-international-parcels-1fi0</link>
      <guid>https://dev.to/ptbdtq/customsbuddy-your-customs-broker-for-international-parcels-1fi0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/mlh/built-with-google-gemini-02-25-26"&gt;Built with Google Gemini: Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built with Google Gemini
&lt;/h2&gt;

&lt;p&gt;Sending international packages is a pain. Especially when it comes to filling out customs declarations (CN22/CN23).&lt;/p&gt;

&lt;p&gt;The average user doesn't know what "HS Codes" are, doesn't know how to correctly translate the names of specific items into English customs language, and often doesn't even realize that their favorite homemade cookies or cold pills are prohibited from being imported into the destination country.&lt;/p&gt;

&lt;p&gt;To solve this problem, I created &lt;strong&gt;CustomsBuddy&lt;/strong&gt; — a React application that converts a user's chaotic text description of a package into a rigorous, validated array of data.&lt;/p&gt;

&lt;p&gt;In this article, I'll show how we abandoned unreliable text parsing and used &lt;strong&gt;Structured Output (JSON Schema) and dynamic System Instructions&lt;/strong&gt; in the Gemini API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://customs-buddy-528833377593.us-central1.run.app/" rel="noopener noreferrer"&gt;Demo:&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://customs-buddy-528833377593.us-central1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;h2&gt;
  
  
  🛑 Why don't regular prompts work for UI?
&lt;/h2&gt;

&lt;p&gt;Let's imagine a typical input: "&lt;em&gt;I'm sending two used t-shirts, a new book, homemade cookies, and 50 bucks worth of medicine to Canada.&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;If you ask a regular LLM to "make a table out of this," it will return Markdown. Markdown looks great in chat, but as a developer, I need to draw React components: green checkmarks for allowed items, red warnings for prohibited items, and calculate the total.&lt;/p&gt;

&lt;p&gt;I need predictable JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Solution: Gemini Structured Output
&lt;/h2&gt;

&lt;p&gt;Instead of begging the model to "please return only JSON," we use the &lt;code&gt;responseSchema&lt;/code&gt; parameter in the Gemini API. This forces the model to strictly follow the structure we specify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Define the Ideal Structure (The Schema)
&lt;/h2&gt;

&lt;p&gt;Here is the configuration object that we will pass to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const responseSchema = {
  type: "OBJECT",
  properties: {
    items: {
      type: "ARRAY",
      items: {
        type: "OBJECT",
        properties: {
          original_description: { type: "STRING" },
          customs_description_en: { type: "STRING" },
          hs_code: { type: "STRING" }, // International customs code
          quantity: { type: "INTEGER" },
          weight_kg: { type: "NUMBER" },
          value_usd: { type: "NUMBER" },
          is_allowed: { type: "BOOLEAN" }, // The main trigger for UI!
          warning_reason: { type: "STRING" } // Explanation if prohibited
        },
        required: ["original_description", "customs_description_en", "hs_code", "quantity", "weight_kg", "value_usd", "is_allowed", "warning_reason"]
      }
    },
    general_warnings: {
      type: "ARRAY",
      items: { type: "STRING" }
    }
  },
  required: ["items", "general_warnings"]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Dynamic System Instructions
&lt;/h2&gt;

&lt;p&gt;Our app supports different destination countries (USA, Canada, Germany, etc.) and different interface languages. The US customs rules are different from Germany. That's why we make the &lt;code&gt;systemInstruction&lt;/code&gt; dynamic!&lt;/p&gt;

&lt;p&gt;In the React component, before calling the API, we form the instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Get the country name in English and the language for the explanation
const destCountryEn = countries.find(c =&amp;gt; c.code === destination).name.en;
const explanationLang = lang === 'uk' ? 'українською мовою' : 'in English';

const systemInstruction = `You are a professional international customs broker.
Your task: to analyze user input to send parcels to the country: ${destCountryEn}.
1. Translate the product descriptions into strict, standardized English.
2. Determine the exact 6-digit HS Code.
3. Logically distribute the cost/weight if only the total is specified.
4. STRONG VALIDATION: Check the customs rules of the country ${destCountryEn}. If the item is prohibited (e.g., homemade food, certain medications), set is_allowed: false and explain why, ${explanationLang} is mandatory. Respond strictly in JSON format.`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Calling the Gemini API
&lt;/h2&gt;

&lt;p&gt;Now we send all this to &lt;code&gt;gemini-2.5-flash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  contents: [{ parts: [{ text: inputText }] }],
  systemInstruction: { parts: [{ text: systemInstruction }] },
  generationConfig: {
    responseMimeType: "application/json",
    responseSchema: responseSchema // Inserting our schema!
  }
 })
});

const data = await response.json();
const jsonText = data.candidates[0].content.parts[0].text;
const parsedDeclaration = JSON.parse(jsonText);
// That's it! No regular expressions or text truncation!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 How does this look in practice?
&lt;/h2&gt;

&lt;p&gt;When a user types: "&lt;em&gt;I'm sending two T-shirts, a book, homemade cookies, and $50 worth of cold medicine to Canada&lt;/em&gt;"...&lt;/p&gt;

&lt;p&gt;Gemini returns a perfect JSON where for the book and T-shirts &lt;code&gt;is_allowed: true&lt;/code&gt; (and the model itself found the HS codes &lt;code&gt;4901.99&lt;/code&gt; and &lt;code&gt;6309.00&lt;/code&gt; for them). But for the cookies and medicine, the model returns &lt;code&gt;is_allowed: false&lt;/code&gt; and adds an explanation that Canadian customs blocks homemade food without factory labeling and over-the-counter medicines.&lt;/p&gt;

&lt;p&gt;In React, we simply map this array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{results.items.map((item, idx) =&amp;gt; ( 
 &amp;lt;div key={idx} className={item.is_allowed ? 'bg-slate-50' : 'bg-red-50'}&amp;gt; 
  {item.is_allowed ? &amp;lt;CheckCircle2 className="text-emerald-500" /&amp;gt; : &amp;lt;AlertOctagon className="text-red-500" /&amp;gt;} 

  &amp;lt;h3&amp;gt;{item.customs_description_en}&amp;lt;/h3&amp;gt; 
  &amp;lt;span&amp;gt;HS Code: {item.hs_code}&amp;lt;/span&amp;gt; 

  {!item.is_allowed &amp;amp;&amp;amp; ( 
  &amp;lt;div className="text-red-700"&amp;gt; 
   Failure reason: {item.warning_reason} 
  &amp;lt;/div&amp;gt; 
  )} 
 &amp;lt;/div&amp;gt;
))}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💡 Conclusion
&lt;/h2&gt;

&lt;p&gt;Structured Output (JSON Schema) completely changes the approach to developing AI applications. We no longer use LLMs simply as "smart interlocutors". We use them as reliable microservices for data analysis, classification and transformation that integrate perfectly into our frontend.&lt;/p&gt;

&lt;p&gt;Try adding &lt;code&gt;responseSchema&lt;/code&gt; to your next Gemini project - and you will forget about the pain of text parsing forever!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>geminireflections</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Maplewood Heights (Community Connect)</title>
      <dc:creator>πn</dc:creator>
      <pubDate>Mon, 02 Mar 2026 07:55:04 +0000</pubDate>
      <link>https://dev.to/ptbdtq/maplewood-heights-community-connect-25fo</link>
      <guid>https://dev.to/ptbdtq/maplewood-heights-community-connect-25fo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-02-28"&gt;DEV Weekend Challenge: Community&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today I’m excited to introduce Maplewood Heights, a warm and inviting hub for our local community! &lt;/p&gt;

&lt;p&gt;🏡 It’s a place where neighbors can learn about events, share news, and discover local gems.&lt;/p&gt;

&lt;p&gt;☁️ I'd love to hear your thoughts on the design!&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://community-connect-528833377593.us-central1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;&lt;em&gt;The app is already deployed to Google Cloud:&lt;/em&gt;: &lt;a href="https://community-connect-528833377593.us-central1.run.app" rel="noopener noreferrer"&gt;https://community-connect-528833377593.us-central1.run.app&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;🛠️ &lt;strong&gt;Functionality of the first version already has a lot of useful features:&lt;/strong&gt;&lt;br&gt;
📅 View upcoming events &lt;br&gt;
💬 Live feed of community updates &lt;br&gt;
☕ Favorite local places &lt;br&gt;
🤝 Neighbor profiles and statistics&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updates feed: Multi-level comment threads, quick reactions, and emojis. &lt;/li&gt;
&lt;li&gt;Community: Get to know your neighbors and quickly message them. &lt;/li&gt;
&lt;li&gt;Events: A board of upcoming activities (from farmers' markets to volunteering). &lt;/li&gt;
&lt;li&gt;Local "gems": Recommendations for the best bakeries, parks, or coffee shops in the area.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My main goal was to combine the speed of modern frontend technologies with a stylish, warm UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack &amp;amp; Architecture:&lt;/strong&gt; &lt;br&gt;
⚡ React 18 + Vite (quick build via SWC) &lt;br&gt;
🎨 Tailwind CSS v3 + CSS-variables (fully custom light/dark theme in honey/sage green tones) &lt;br&gt;
🧱 shadcn/ui (integrated ~50 Radix UI components) &lt;br&gt;
✨ Framer Motion (controls orchestrating animations when scrolling and microinteractions) &lt;br&gt;
🚢 &lt;strong&gt;CI/CD:&lt;/strong&gt; Multi-stage Docker build on top of Nginx -&amp;gt; deployment to Google Cloud Run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's inside?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watercolor hero block.&lt;/li&gt;
&lt;li&gt;Update feed with support for recursive N-level comments and reactions.&lt;/li&gt;
&lt;li&gt;Neighborhood mutual aid module.&lt;/li&gt;
&lt;li&gt;Own context-messaging right in the browser.&lt;/li&gt;
&lt;li&gt;When design (DM Serif Display + warm tones) works in synergy with cool UX, users really want to come back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Happy to connect people through technology.&lt;/strong&gt; ✨&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>showdev</category>
    </item>
    <item>
      <title>My Portfolio Has a Split Personality: GUI vs Terminal</title>
      <dc:creator>πn</dc:creator>
      <pubDate>Mon, 02 Feb 2026 07:58:24 +0000</pubDate>
      <link>https://dev.to/ptbdtq/my-portfolio-has-a-split-personality-gui-vs-terminal-3olc</link>
      <guid>https://dev.to/ptbdtq/my-portfolio-has-a-split-personality-gui-vs-terminal-3olc</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hello, network! 👋&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For the "&lt;strong&gt;New Year, New You&lt;/strong&gt;" challenge, I didn't want to just update my CSS. I wanted to build something that represents the duality of a &lt;em&gt;Full Stack Developer&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;The Professional: &lt;em&gt;Clean, modern UI using the trendy Bento Grid layout.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Geek: &lt;em&gt;A hacker-style Terminal for those who prefer CLI over GUI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it out!&lt;/strong&gt;&lt;br&gt;
Check out the live demo and let me know: are you team #GUI or team #Terminal? 👇&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://devportfolio-528833377593.us-central1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;&lt;em&gt;Available at&lt;/em&gt;: &lt;a href="https://devportfolio-528833377593.us-central1.run.app/" rel="noopener noreferrer"&gt;https://devportfolio-528833377593.us-central1.run.app/&lt;/a&gt; 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Concept&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Usually, portfolios are static. You read, you scroll, you leave. I wanted interaction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GUI Mode:&lt;/strong&gt; Visualizes my projects and skills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal Mode:&lt;/strong&gt; Lets you type commands like &lt;code&gt;help&lt;/code&gt;, &lt;code&gt;skills&lt;/code&gt;, or even &lt;code&gt;ask&lt;/code&gt; to talk to AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The AI Magic ✨&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of searching for my experience in a long text, recruiters can now ask:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Tell me about your skills?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AI analyzes my JSON data and generates a professional answer instantly. It lives both in a chat modal (GUI) and as a command (Terminal).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tech Stack&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; React + Vite&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Google Gemini API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Icons:&lt;/strong&gt; Lucide React&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most time was spent not on the code, but on making the site feel "like me": a little mysterious, a little playful, but at the same time very professional.&lt;/p&gt;

&lt;p&gt;This is not just a portfolio. This is my manifesto for 2026: code with passion, use the best tools (thanks Google AI) and don't be afraid to be yourself.&lt;/p&gt;

&lt;p&gt;Thanks for stopping by.&lt;/p&gt;

&lt;p&gt;Write in the comments what you liked/what you would change - I'd be happy to hear your feedback.&lt;br&gt;
And if you also participated - drop a link, I'll take a look and like 🚀&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
