Skip to content

Conversation

@restyler
Copy link

@restyler restyler commented Jun 28, 2025

This is part 1 of implementing "segments" or "dynamic subscriber lists" (#250). This PR adds the foundational SQL snippets functionality that is an isolated (and useful) feature itself and later this can be used by dynamic subscriber lists in a future PR.

The idea of "dynamic subscriber list" would be that it is just another "subscriber list" but it is dynamically updated via cron scheduler (and right before campaign is sent), by executing "sql snippet" attached to this list. I am pretty sure there will be some rough edges here, but at least this is something to start the discussion.

Summary

  • Adds SQL snippets database table and CRUD operations
  • Provides Vue.js UI for managing reusable SQL query fragments
  • Includes SQL validation against allowed tables
  • Adds count functionality to test snippet queries

Context

Splitting the dynamic segments feature into two parts:

  1. SQL snippets in database (this PR) - foundational reusable query system
  2. Dynamically updated segments (future PR) - automatic list membership management

restyler and others added 12 commits June 28, 2025 20:25
- Add SQL snippets table and migration
- Create CRUD operations for managing reusable SQL query fragments
- Add frontend UI for SQL snippets management
- Include query validation and syntax highlighting
- Add navigation menu integration
- Requires subscribers:sql_query permission

This is the first part of a larger feature split from dynamic segments.
- Fix Exec() return value handling in UpdateSQLSnippet and DeleteSQLSnippet
- Add explicit BOOLEAN casting in get-sql-snippets query to resolve parameter type ambiguity
- Change config port from 9001 to 9000 to match documentation
- Use self-closing HTML tag for empty <th>
- Add required newline at end of file
- Move SQL snippets from Subscribers to Settings section
- Add missing English translations for SQL snippets
- Update router group from 'subscribers' to 'settings'
- Remove complex LIMIT NULL logic that was causing empty results
- Add explicit default limit of 50 when pagination limit is 0
- Simplify SQL query to use direct LIMIT parameter
- Update translations for enabled/disabled status in SQL snippets
- Increase modal width for better user experience
- Refactor form field names for consistency (query_sql to querySql)
- Add live subscriber count display in SQL snippet form
- Implement loading and error states for subscriber count
- Adjust validation logic to use updated field names
- Introduce a new script for restarting the development environment
- Introduce a new API endpoint for counting subscribers matching a SQL snippet.
- Implement the HandleCountSQLSnippet function in the backend.
- Add frontend API call for counting SQL snippets with loading and error handling.
- Enhance SQL Snippets view with live validation and autocomplete for SQL snippets.
- Update navigation and UI elements for better user experience.
- Remove all console.log debug statements from Subscribers.vue
- Fix SQL snippet default data - use correct subscriber_status values
- Reduce to 2 simple, useful default snippets:
  - Enabled Subscribers (subscribers.status = 'enabled')
  - Recent Signups (last 30 days)
- Removes problematic snippets that used invalid status values

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Replace clunky notification with minimalistic validation icon
- Hide "Validate Query" button when live validation is enabled
- Improve "Live SQL validation" checkbox styling with native input
- Add inline success/error icons with tooltips
- Make validation status more subtle and space-efficient
- Reduce button size to is-small for better proportions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Change from snake_case (created_at, updated_at) to camelCase (createdAt, updatedAt)
- Matches HTTP interceptor conversion mentioned in CLAUDE.md
- Fixes empty Created/Updated columns in SQL snippets table
- Ensures proper date formatting in UI

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Simplify error handling in SqlSnippets.vue and Subscribers.vue by removing console error logs.
- Implement silent failure for loading subscriber counts and SQL snippets, ensuring the application remains functional without displaying error messages.
- Set default values for sqlSnippets to an empty array on failure to enhance user experience.
@restyler
Copy link
Author

restyler commented Jun 28, 2025

Video demo:

Screen.Recording.2025-06-28.at.23.18.01_compressed.mp4

@restyler restyler changed the title Add SQL snippets functionality (part 1 of dynamic segments) Add SQL snippets functionality (part 1 of dynamic subscriber lists) Jun 28, 2025
@relikd
Copy link
Contributor

relikd commented Jun 28, 2025

Hahaha, nice to see my idea (#250 (comment)) implemented :D thank you!

Regarding your video demo, it looks like the snippet is just pasted in the subscribers list. Maybe it would be better if the filter would use the snippet reference? E.g., if I update the snippet, all occurrences are updated as well. Or is it intentional? If so, what is your imagined scenario? An admin pre-configuring some sample queries for users to be able to edit them?

Thanks for the progress :)

@restyler
Copy link
Author

restyler commented Jun 28, 2025

Thanks!

E.g., if I update the snippet, all occurrences are updated as well. Or is it intentional?

This is a great question. I want to minimize the stress for current users (which might be used to editing snippets right here, in textarea at "Advanced" section) and add "progressive enhacement" to the textarea instead of strong reference to SQL snippets.

I have just added a way to quickly save snippet from Subscribers page - and live counter.
2025-06-29 at 01 11

@knadh
Copy link
Owner

knadh commented Jul 2, 2025

Thanks for the PR @restyler! Looks fine prima-facie. I'll review it closer to the next big release.

About the live counter though, I'm not sure if it's ideal. Executing queries continuously on large instances (ours at work has ~17+ million subscribers) just to get the count is going to be very problematic, especially when it's as you type. Also, from a UX perspective, the count is just one-click away anyway.

@knadh knadh self-assigned this Jul 2, 2025
@restyler
Copy link
Author

restyler commented Jul 6, 2025

I'll review it closer to the next big release.

Oh, this would be so nice to have this included in next major release! Thanks!

Executing queries continuously on large instances (ours at work has ~17+ million subscribers) just to get the count is going to be very problematic, especially when it's as you type.

I agree this might be a problem on huge lists. That said, I suspect such extreme cases are quite rare, and for them live editing can be easily disabled with a single checkbox (I think this setting state should be stored, so it is disabled once and forever), and I remember that when I started writing SQL queries in Listmonk, my biggest pain was the lack of interactivity and the lack of transparency around what “attribs” my subscribers actually have (this could be solved by some lightweight sniffing feature - again, it could be a bit slow on 2% of listmonk installations - but for another 98% this would mean much easier adoption and usage).

@ilkkao
Copy link

ilkkao commented Aug 7, 2025

Also there could be a "Execute snippet" button that updates the number. This would be similar e.g. how formula editing in Grafana dashboards work.

@Rush
Copy link

Rush commented Sep 30, 2025

I’m considering adopting Listmonk, and the addition of this feature (dynamic subscriber lists) would make it an easy decision to switch.

Example use case: localized newsletters and user selecting their language of choice in the application.

With this feature, it'd be a simple case of querying for the right language property.

Without this feature, the application has to move the user between lists.

@knadh knadh mentioned this pull request Oct 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants