Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
159742 stories
·
33 followers

Create a Kendo UI for Angular Dinner Picker

1 Share

Sick of picking where to go for dinner? Build this little Angular app to help you choose!

Do you ever argue about where to eat? If you’re like me, you can’t think of places half the time, and you just wish someone would pick for you. Well, here you go!

Angular Restaurant Picker with options for dine-in or take-out

TL;DR

This app will create a random dinner picker from JSON data in Angular using Progress Kendo UI. You can choose from Takeout or Dine-in.

Setup

Create a new Angular app:

ng new angular-dinner-picker

Get a Kendo UI License

Log in to Progress and purchase a Kendo UI for Angular license, or try it for free.

Kendo UI License Key

Download the actual key, as you will need it later for deployment.

Install Kendo UI Licensing

npm i -S @progress/kendo-licensing

Then run:

npx kendo-ui-license activate

This will set up Kendo UI on your local machine for this project. Make sure you have a working license at this point, and to follow the correct setup.

Install Packages

ng add @progress/kendo-angular-buttons
ng add @progress/kendo-angular-dropdowns
ng add @progress/kendo-angular-layout
npm i -S npm install @angular/forms @progress/kendo-svg-icons @progress/kendo-theme-default

You may need to also install @angular/localize.

Install Tailwind

Make sure Angular is configured for Tailwind. Follow the Tailwind Guide.

Configure Styles

Make sure your styles are shown in styles.css correctly.

@import '@progress/kendo-theme-default/dist/default-main.css';
@import 'tailwindcss';

Picker Component

Generate a new picker component.

ng g c picker

Model

Create a model at picker.model.ts.

export type RestaurantMode = 'dine-in' | 'takeout';

export type Restaurant = {
  id: number;
  name: string;
  cuisine: string;
  mode: RestaurantMode;
};

export type RestaurantModeFilter = 'all' | RestaurantMode;

Data

You can declare your data in the restaurants.ts. You could obviously get more sophisticated and import data from a database if you like.

import { Restaurant } from "./picker.model";

export const restaurants: Restaurant[] = [
  {
    "id": 1,
    "name": "Monjunis",
    "cuisine": "Italian",
    "mode": "dine-in"
  },
  {
    "id": 2,
    "name": "Strawn's Eat Shop",
    "cuisine": "Southern Diner",
    "mode": "dine-in"
  },
  {
    "id": 3,
    "name": "Country Tavern",
    "cuisine": "BBQ",
    "mode": "dine-in"
  },
  
  ...
  
];

I used my city’s info so I can actually use the app! You can customize this and deploy it separately for different situations or cities!

Picker Class

Create the picker classes at picker.ts.

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { ButtonsModule } from '@progress/kendo-angular-buttons';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';

import { restaurants } from './restaurants';
import { Restaurant, RestaurantModeFilter } from './picker.model';

type ModeOption = {
  label: string;
  value: RestaurantModeFilter;
};

@Component({
  selector: 'app-picker',
  standalone: true,
  imports: [
    FormsModule,
    ButtonsModule,
    DropDownsModule
  ],
  templateUrl: './picker.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Picker {
  readonly restaurants = restaurants;

  readonly modeOptions: ModeOption[] = [
    {
      label: 'Any',
      value: 'all'
    },
    {
      label: 'Dine-in',
      value: 'dine-in'
    },
    {
      label: 'Takeout',
      value: 'takeout'
    }
  ];

  selectedMode: RestaurantModeFilter = 'all';
  selectedRestaurant: Restaurant | null = null;

  get filteredRestaurants(): Restaurant[] {
    if (this.selectedMode === 'all') {
      return this.restaurants;
    }

    return this.restaurants.filter((restaurant) => restaurant.mode === this.selectedMode);
  }

  pickRestaurant(): void {
    const choices = this.filteredRestaurants;

    if (choices.length === 0) {
      this.selectedRestaurant = null;
      return;
    }

    const pickableChoices =
      this.selectedRestaurant && choices.length > 1
        ? choices.filter((restaurant) => restaurant.id !== this.selectedRestaurant?.id)
        : choices;

    const index = Math.floor(Math.random() * pickableChoices.length);

    this.selectedRestaurant = pickableChoices[index];
  }
}
  • Pick the restaurant with pickResaturant() function choosing a random JSON entry from the filtered results.
  • We have any, dine-in and takeout.
  • We will use the filtered results in the html template from filteredResautrants().

Picker Template

<main
  class="grid min-h-screen place-items-center bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.95),rgba(255,255,255,0)_28%),linear-gradient(180deg,#f7efe7_0%,#eef2f7_100%)] p-4 sm:p-6">
  <section
    class="w-full max-w-sm rounded-4xl border border-slate-300/40 bg-white/92 px-6 py-7 shadow-[0_24px_60px_rgba(15,23,42,0.12)] backdrop-blur-[14px] sm:px-8 sm:py-8">
    <header class="space-y-3 text-center">
      <h1 class="m-0 text-[2.05rem] font-semibold leading-[0.98] tracking-tight text-slate-800 sm:text-[2.3rem]">
        Restaurant Picker
      </h1>

      <p class="mx-auto max-w-64 text-[1rem] leading-7 text-slate-500">
        Pick where to eat without thinking about it.
      </p>
    </header>

    <div class="my-6 h-px bg-slate-200/80"></div>

    <div class="space-y-6">
      <div class="space-y-3 text-center">
        <label for="mode" class="block text-[1.15rem] font-semibold leading-tight text-slate-800">
          What kind?
        </label>

        <kendo-dropdownlist id="mode" class="w-full text-left" [data]="modeOptions" textField="label" valueField="value"
          [valuePrimitive]="true" [(ngModel)]="selectedMode" />
      </div>

      <button kendoButton class="min-h-14 w-full text-base font-semibold" themeColor="primary" size="large"
        rounded="large" [disabled]="filteredRestaurants.length === 0" (click)="pickRestaurant()">
        Pick Restaurant
      </button>

      @if (selectedRestaurant) {
      <section
        class="rounded-[1.75rem] border border-rose-200/80 bg-[linear-gradient(135deg,rgba(255,241,242,0.92),rgba(255,255,255,0.98))] px-6 py-6 shadow-[inset_0_1px_0_rgba(255,255,255,0.7)]"
        aria-live="polite">
        <div class="grid justify-items-center gap-4 text-center">
          <p class="m-0 text-[0.78rem] font-bold uppercase tracking-[0.2em] text-rose-700">Tonight's pick</p>

          <h2
            class="m-0 max-w-[11ch] text-[clamp(1.7rem,4vw,2rem)] font-semibold leading-[1.08] tracking-tight text-slate-900 text-balance">
            {{ selectedRestaurant.name }}
          </h2>

          <div class="grid justify-items-center gap-3">
            <p class="m-0 text-[1rem] leading-6 text-slate-700">
              {{ selectedRestaurant.cuisine }}
            </p>

            <span
              class="inline-flex min-h-10 items-center justify-center rounded-full border border-slate-300/40 bg-white/90 px-5 text-sm font-bold text-slate-900 shadow-[0_6px_18px_rgba(15,23,42,0.06)]">
              {{ selectedRestaurant.mode === 'dine-in' ? 'Dine-in' : 'Takeout' }}
            </span>
          </div>
        </div>
      </section>
      } @else {
      <div
        class="rounded-[1.75rem] border border-dashed border-slate-400/60 bg-slate-50/75 px-5 py-6 text-center text-[0.95rem] leading-6 text-slate-500">
        @if (filteredRestaurants.length === 0) {
        No restaurants found for this option.
        } @else {
        Click the button to pick a restaurant.
        }
      </div>
      }

      <p class="text-center text-[0.82rem] font-semibold text-slate-500">
        {{ filteredRestaurants.length }}
        restaurant{{ filteredRestaurants.length === 1 ? '' : 's' }} available
      </p>
    </div>
  </section>
</main>

Here’s what’s going on above:

  • When we run pickRestaurant(), the app displays the filtered items as a signal.
  • The kendo-dropdownlist component uses data field for options with label and value matching the drop down.
  • We just add kendoButton to our button component to get the look we want. We can use Tailwind with the look!

And it’s that simple!

Repo: GitHub
Demo: Vercel Serverless

You can try all of this yourself with the Kendo UI for Angular trial, free for 30 days.

Try Now

Read the whole story
alvinashcraft
44 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

AI UX Patterns for User Transparency

1 Share

Many users won’t allow AI to integrate into their workflow if they don’t know what that access really means. To get user buy-in, we need to provide transparency.

AI tools are strongest when they integrate into the rest of a user’s workflow—referencing internal documents, sending emails, making calendar appointments, drafting and sharing content with coworkers, so on and so forth.

However, most users won’t integrate what they can’t see and understand. If the AI applications we build are going to become part of their daily lives, users need transparency into exactly what they do, what data they have access to and what it will cost them (in both time and money).

The more transparent we can make these features, the less hesitation our users will have adopting them.

UX Pattern: Permissions

The concept of asking for user permissions is certainly not AI-specific, but the stakes can feel a lot higher when you’re asking users for permission to allow AI to act on their behalf.

We need to make it easy for users to see which applications we’ve allowed our tool to access and in what ways, keeping in mind that permissions are often more than just “yes” or “no.”

Can your agent just reference data from this database, or is it allowed to delete tables? Can it only read a user’s emails, or is it allowed to send from their address as well? Can it run scripts? Search local files? Each one of these actions requires direct user sign-off.

Permissions are also not a one-and-done situation. A user might feel comfortable allowing an action to happen once under their direct supervision, but don’t want to allow it permanently. They might give access to a specific folder, but not every folder. They might approve an action, but want to be notified each time it’s taken. Consider what gray zones might exist in your permission structure, and try to accommodate as many options as you can.

Extensive permission options in VS Code Copilot

Auto-approval permissions in VS Code Copilot

Another common sticking point with AI is data collection. Often, it can be helpful to save information from past interactions, but storing this kind of data requires real attention to user permission and data management.

If you want to create a system that can “remember” things, you also need to make sure it can “forget” as well—and that the user can not only see but has final say in what exactly what gets remembered. An ideal permissions flow will not only ask for a user’s approval in the moment, but also create a space where they can see the history of what they’ve given access to and when—and allow them to revoke that access or permission at any time.

UX Pattern: Estimates and Confirmation

This one is pretty simple, so we won’t spend too much time on it, but another crucial aspect of transparency is making sure users know exactly what they’re committing to when they approve an action request. In addition to approving the actual steps and plan (as we discussed earlier in the Trust post), they also need to know how much time it’s going to take and what it will cost them in money or tokens.

Figma's AI Balance token tracking meter

Model and token usage reports in VS Code Copilot

Even if we can’t specify an exact amount, providing rough estimates generally gives users enough information to work with—and to potentially revise their request if it’s going to exceed what they’re comfortable with.

UX Pattern: Solo Execution Indicator

Similarly to the marking that denotes which content was AI-generated, it’s also a good idea to have some kind of visual signal for when an AI tool is acting independently. If, for example, you’re going to allow an agent to take control of the user’s browser, then there needs to be a banner, sidebar, outline or some other kind of indication that the user is no longer driving the interaction.

The glowing orange border denotes agent control of the browser in Claude's browser extension

Not only is this a good thing to do just for transparency—so the user understands the current state of the system—but also so they don’t unintentionally interrupt or confuse an ongoing process. It’s an especially important consideration for processes that you know will take an extended period of time, where the user may step away and come back without keeping track of everything that’s happened while they were gone.

Read the whole story
alvinashcraft
50 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Ask, Plan, Confirm: Making Agents Stop Before They Start

1 Share

The scariest thing about a genuinely capable coding agent is how quickly it commits. You type two sentences, and forty seconds later there are changes across nine files, half of which you did not want and one of which quietly changed a query you spent a week hardening. The agent was not wrong about how to implement the thing. It was wrong about what the thing was, and it never stopped to check.

I have watched this happen enough times that I stopped treating it as a prompting problem and started treating it as a workflow problem. A better one-shot prompt is not the fix. A gate is: the agent is not allowed to edit files until it has asked what it needs to ask, shown me a plan, and gotten a yes.

Why speed is the problem

Human engineers have a built-in pause. Before a senior developer touches your codebase they ask a couple of questions, sketch the approach, maybe drop a comment on the ticket. That pause is where the wrong-work gets caught, cheaply, in a sentence, instead of expensively, in a diff you have to read and reject.

Agents removed the pause. That is most of their value and most of their danger in the same motion. An agent that implements immediately optimizes for the wrong thing: it treats “produce a diff” as the goal, when the goal was “produce the diff we agreed on.” The gap between those two is where the deleted work and the silent scope creep live.

That gap is not a knowledge problem. The agent knows how to write the code. It just never checked that it was writing the right code.

Diagram explaining the importance of speed in work processes, contrasting 'no gate' and 'the gate' approaches. It highlights potential problems and costs associated with each method.

The same task, gated and ungated. Skipping the pause trades a small, predictable cost for a large, unpredictable one.

So I gave the pause back, deliberately, as a standing instruction on every agent in the InterlinedList repo.

The three beats

The workflow is written down in .claude/workflows/plan-first.md and every agent links to it. It is three beats, in order, and implementation is gated behind all three.

A flowchart titled 'The Gate' illustrating a process for implementation that requires three steps: Prompt, Ask, Plan, Confirm, and Implement. It emphasizes the sequence and conditions under which implementation occurs, highlighting the need for clarification and approval before proceeding.

The three beats, in order. Nothing is edited until all three pass, and work that grows past the approved plan loops back to re-plan rather than quietly expanding.

Ask. Surface what the prompt left open before committing to an approach. Ambiguous scope, unstated edge cases, a product decision hiding inside a technical request, whether a feature should be tier-gated. The migrations agent asks about column types and nullability and whether anything destructive is implied. The Next.js agent asks which surfaces are in and out. The rule has an escape hatch, because asking three questions about a one-line copy fix is its own kind of annoying: skip the questions only when the request is genuinely unambiguous and low-risk. When in doubt, ask. A pointed question is cheaper than a wrong build every single time.

Plan. Before touching files, lay out the shape of the change: the files and routes and components you will touch, the ones you will deliberately leave alone, the approach, any migration (additive, always), the tests the change needs, and anything risky. In this repo “risky” has a specific meaning: auth, IDOR, subscription gating, SSRF, secret handling, anything destructive or hard to reverse. The plan is a decision aid, not a document. It should be short enough to read in one breath and specific enough that approving it means something.

Confirm. Implement only after an explicit yes. If the plan changes in the back-and-forth, restate the revised version and get the yes again. And the part that actually matters over a long session: approval is scoped to the plan that was approved. If the work grows past it, the agent stops and re-plans instead of quietly expanding. That last clause is what keeps a “small fix” from turning into an afternoon of changes I never signed off on.

What it looks like per agent

I did not want one generic paragraph pasted eight times. The gate is the same, but what you ask about depends on the job, so each agent got the beats written for its lane.

A diagram featuring multiple lanes labeled with different topics: Migrations, Next.js, End-to-end, Docs, Unit testing, and Blog. Each lane has brief descriptions of its scope, accompanied by specific tags.

One gate, written for each lane. The two read-only reviewers pick the full gate back up the moment they move from finding to fixing.

The migrations agent plans the exact idempotent migration.sql and confirms it is purely additive before it applies anything. The unit-testing agent asks which behaviors to lock in and which boundaries to mock, then lists the cases each test file will assert. The e2e agent names the flows, the auth and seed prerequisites, and the breakpoints that matter. The docs agent confirms which of the three docs is in scope and whether a new page is needed. The blog agent (yes, this one) settles the angle and the section arc and which real code it will verify claims against before drafting a word.

The two read-only reviewers are the interesting edge. Security and UX do not implement, so there is no edit to gate. For them the gate degrades to its first beat: confirm the review scope if it is ambiguous (which routes, how deep, which breakpoints), then produce findings. But the moment the user says “now fix what you found,” they are implementers, and the full ask-plan-confirm gate snaps back on before they touch code. The reviewer does not get to slide from “here is a finding” into “and I fixed it” without crossing the same line everyone else crosses.

The obvious objection

This is slower. That is the point, and it is also not as true as it sounds. The plan step costs you a few seconds and one read. Rejecting a forty-second nine-file diff that went the wrong direction costs you the read plus the reject plus the re-prompt plus the nagging worry about what it touched that you did not catch. The gate front-loads a small, predictable cost to avoid a larger, unpredictable one. Over a day of handoffs it is not close.

It also composes with the other habit I built into these agents: every one of them does its work in an isolated git worktree, on its own branch, torn down when the task lands. Plan first, then do the approved work in a sandbox that cannot collide with anyone else. The worktree contains the blast radius. The plan makes sure there is not supposed to be a blast in the first place.

The pause was always the expensive part of good engineering. Worth teaching the machines to keep it.

I’m Adron, brainstorming and building InterlinedList.

Read the whole story
alvinashcraft
59 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

CritterWatch 1.0 is live!

1 Share

CritterWatch 1.0 dropped yesterday, and you can read the official release on the JasperFx Software site.

This blog post is just me being thankful for all the folks who helped build CritterWatch or test it along the way:

  • Babu Annamalai was instrumental in CritterWatch, our company infrastructure, and the Critter Stack in general
  • Jeffry Gonzalez helped lay the foundation
  • Anne Erdstrieck made CritterWatch (and Marten/Wolverine) a lot better by testing early versions on a very complex and high volume system
  • Ben Virkler gave us a lot of feedback on usability and early problems (and we owe Ben some additional improvements for 1.1 that didn’t make the 1.0 wire)
  • Laurence Gillian was an early tester
  • Several other folks in Discord, and all of that feedback was helpful

And to Oskar Dudycz for all his contributions across the Critter Stack as much of CritterWatch are things that he and I were talking about years ago.

For my part, I founded JasperFx Software on the idea that we’d go down the “Open Core” model with business offerings of consulting, formal support agreements, and training around the Critter Stack tools, then supplement that with commercial add on tools for very advanced features, monitoring, and management. It took much longer than I’d hoped, but a couple months into JasperFx’s fourth year in business, we’re finally realizing the original vision.

And with all this out of the way, it’s time for me to get just a little break then start on CritterWatch 1.1!



Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

What's New for Developers in SQL Server 2025

1 Share
SQL Server 2025 gives developers new ways to work with text, JSON, real-time data changes and AI-ready vector data. Microsoft MVP Leonard Lobel explains why he considers it the most significant SQL Server release for developers in a decade and previews the new capabilities he'll demonstrate at Live! 360 Tech Con 2026.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Protecting Modern JavaScript: What’s new in JSDefender 2.12.0

1 Share

As JavaScript continues to evolve through regular ECMAScript and Node.js updates, JSDefender evolves alongside it to keep pace with the ever-changing ecosystem of frameworks and tooling. As security teams and developers adopt new language features and upgrade their build environments, it’s essential that security tools remain compatible without increasing their workload. The JSDefender 2.12.0 release enables teams to maintain security measures alongside modern JavaScript development without slowing them down. 

What JSDefender 2.12.0 offers

Protecting and debugging JavaScript applications just got easier with this release. Here’s what’s improved:

Debugging with V3 Source Map Generation

Application protection techniques  deliberately transform JavaScript code, making it significantly harder to reverse engineer. While this strengthens app security, it can also make runtime errors more difficult to investigate. Once code is obfuscated, runtime errors are hard to trace and debugging becomes harder, since obfuscated stack traces reference renamed methods and transformed code.

JSDefender now generates Version 3 source maps during obfuscation and minification of applications, allowing developers to debug obfuscated code and map it back to their original source code using standard browser developer tools. 

Node.js 24 and 25 Compatibility

Many organizations and development teams frequently update their environments to remain current with the latest Long-term Support (LTS) releases and platform enhancements. In this release, JSDefender adds compatibility with Node.js 24 and 25 while also maintaining support for previous versions. Dev teams can continue using JSDefender in their modern build environments without compromise.   

ECMAScript 2025 and 2026 Support

JavaScript continues to introduce new syntax and language capabilities every year through ECMAScript standards. Developers and teams who stay updated with the latest standards also need their security tools to understand the modern language constructs in order to safeguard their code. In this release, JSDefender now supports ECMAScript 2025 & 2026 language specifications. This enables developers to effectively protect their applications by recognizing the modern syntax and ensuring reliable security with the latest JavaScript features.   

What this means for teams

Whether teams are transitioning to the latest Node.js runtime, adopting new ECMAScript features, or remediating bugs in existing secured applications, JSDefender 2.12.0 helps minimize friction throughout the SDLC.

PreEmptive safeguards your applications

As threats evolve, PreEmptive remains dedicated to maintaining consistency across the platforms we help protect. Customers with active licenses will receive JSDefender 2.12.0 automatically and can continue securing their JavaScript applications with ease. 

New to JSDefender? Start a free trial to see how easy it is to add enterprise-grade protection to your JavaScript applications.

For full release details, see the change log.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories