<?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: Pankaj Jangid</title>
    <description>The latest articles on DEV Community by Pankaj Jangid (@oyepanky).</description>
    <link>https://dev.to/oyepanky</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4108349%2Fea826dca-c120-4514-8feb-2da3bfd8cc1a.jpg</url>
      <title>DEV Community: Pankaj Jangid</title>
      <link>https://dev.to/oyepanky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oyepanky"/>
    <language>en</language>
    <item>
      <title>I Built a Full IT Ticket System in Power Apps — Here's the SLA Engine That Runs Without Power Automate</title>
      <dc:creator>Pankaj Jangid</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:57:33 +0000</pubDate>
      <link>https://dev.to/oyepanky/i-built-a-full-it-ticket-system-in-power-apps-heres-the-sla-engine-that-runs-without-power-40c8</link>
      <guid>https://dev.to/oyepanky/i-built-a-full-it-ticket-system-in-power-apps-heres-the-sla-engine-that-runs-without-power-40c8</guid>
      <description>&lt;p&gt;I recently built a complete IT ticket management system in Power Apps —&lt;br&gt;
9 screens, role-based access, live SLA tracking, and automatic email&lt;br&gt;
notifications. The part I want to actually talk about here isn't the UI,&lt;br&gt;
it's the SLA engine, because I built it to work &lt;strong&gt;without Power Automate&lt;/strong&gt;,&lt;br&gt;
and the trick is simpler than it looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;SLA tracking normally means: a ticket is "Critical" → 60 minute target →&lt;br&gt;
somebody needs to know if it's about to breach or already has. The&lt;br&gt;
obvious way to do this is a scheduled Power Automate flow that checks&lt;br&gt;
every ticket on a timer and flags the ones in trouble.&lt;/p&gt;

&lt;p&gt;I wanted this app to run on Power Apps collections only — no flow, no&lt;br&gt;
external data source — so a scheduled flow wasn't an option. The&lt;br&gt;
question was: can you get "live" SLA status without a background job?&lt;/p&gt;

&lt;h2&gt;
  
  
  The trick: recalculate on every read, not on a timer
&lt;/h2&gt;

&lt;p&gt;Instead of a flow updating a &lt;code&gt;SLAStatus&lt;/code&gt; field periodically, I recalculate&lt;br&gt;
it every time the app or a screen is opened, using &lt;code&gt;Now()&lt;/code&gt; against the&lt;br&gt;
stored due date:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
UpdateIf(&lt;br&gt;
    colTickets,&lt;br&gt;
    Status &amp;lt;&amp;gt; "Resolved" &amp;amp;&amp;amp; Status &amp;lt;&amp;gt; "Closed",&lt;br&gt;
    {&lt;br&gt;
        SLAStatus: If(&lt;br&gt;
            Now() &amp;gt; DueDate,&lt;br&gt;
            "Breached",&lt;br&gt;
            DateDiff(Now(), DueDate, TimeUnit.Minutes) &amp;lt;= SLAMinutes * 0.2,&lt;br&gt;
            "At Risk",&lt;br&gt;
            "On Track"&lt;br&gt;
        )&lt;br&gt;
    }&lt;br&gt;
);&lt;br&gt;
UpdateIf(colTickets, Status = "Resolved" || Status = "Closed", {SLAStatus: "Met"})&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This runs in &lt;code&gt;App.OnStart&lt;/code&gt;, at the top of every screen's &lt;code&gt;OnVisible&lt;/code&gt;, and&lt;br&gt;
behind a manual "Refresh SLA" button. The At Risk threshold is 20% of&lt;br&gt;
the SLA window remaining — so a Critical ticket (60 min target) goes&lt;br&gt;
At Risk with 12 minutes left; a Low ticket (1440 min / 24 hrs) goes At&lt;br&gt;
Risk with 4.8 hours left.&lt;/p&gt;

&lt;p&gt;The honest tradeoff: this only updates when someone has the app open.&lt;br&gt;
A ticket breaching at 2am with nobody looking won't trigger anything&lt;br&gt;
until the next visit. For a real production deployment I'd pair this&lt;br&gt;
with a scheduled flow for after-hours detection — but for a demo, an&lt;br&gt;
internal tool with regular traffic, or anything where "eventually&lt;br&gt;
consistent within the next visit" is fine, skipping the flow&lt;br&gt;
entirely removes a moving part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Role-based navigation, driven by data not code
&lt;/h2&gt;

&lt;p&gt;The other pattern worth sharing: instead of hardcoding &lt;code&gt;If(role = "Admin", ...)&lt;/code&gt;&lt;br&gt;
checks scattered across the app, navigation is driven by a single table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;th&gt;AllowedRoles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dashboard&lt;/td&gt;
&lt;td&gt;Dashboard&lt;/td&gt;
&lt;td&gt;,Admin,Manager,Assigned Person,User,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reports&lt;/td&gt;
&lt;td&gt;Reports&lt;/td&gt;
&lt;td&gt;,Admin,Manager,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;users&lt;/td&gt;
&lt;td&gt;Users&lt;/td&gt;
&lt;td&gt;,Admin,&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The nav rail filters itself against the signed-in user's role:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Items: SortByColumns(&lt;br&gt;
    Filter(colNavigation,&lt;br&gt;
        !IsBlank(Find("," &amp;amp; varCurrentUser.Role &amp;amp; ",", AllowedRoles))),&lt;br&gt;
    "Sort"&lt;br&gt;
)&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Adding a new role, or changing who sees what, is a data edit — not a&lt;br&gt;
formula change on nine screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else is in there
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Automatic Outlook emails on create/assign/status-change/resolve/close,
all sharing one HTML template (nested tables, inline CSS — the only
way HTML survives Outlook's rendering engine)&lt;/li&gt;
&lt;li&gt;PDF export of a ticket or the whole list, emailed directly from the app&lt;/li&gt;
&lt;li&gt;A 7-day ticket trend chart alongside the usual KPI tiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full build walkthrough (I go through every screen and formula) is here:&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/UHQTdWVWDts" width="710" height="399"&gt;
  &lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;If you want the finished template instead of rebuilding it — SLA engine,&lt;br&gt;
email templates, all 9 screens, plus migration guides to move it onto&lt;br&gt;
SharePoint or Dataverse when you outgrow collections — I've packaged it&lt;br&gt;
here: &lt;a href="https://dialforit.com/products/6a9914af55456cb98d94a92e" rel="noopener noreferrer"&gt;dialforit.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions on the SLA math or the Outlook HTML in the&lt;br&gt;
comments — those were the two things that took the most iteration to&lt;br&gt;
get right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzbgeve2y3jd7eoeneo2g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzbgeve2y3jd7eoeneo2g.gif" alt=" " width="600" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>powerplatform</category>
      <category>powerapps</category>
      <category>powerfuldevs</category>
      <category>powerautomate</category>
    </item>
  </channel>
</rss>
