Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"permissions": {
"allow": [
"mcp__dual-graph__graph_scan",
"mcp__dual-graph__graph_continue",
"Bash(find G:ResgridResgridCoreResgrid.ModelBilling -type f -name *.cs)",
"Bash(python3:*)",
"Bash(xargs grep:*)",
"Bash(grep -E \"\\\\.cs$|Program\")",
"Bash(grep -l \"DepartmentMembers\\\\|DepartmentMember\" \"G:\\\\Resgrid\\\\Resgrid/Repositories/Resgrid.Repositories.DataRepository/\"*.cs)",
"Bash(find /g/Resgrid/Resgrid/Repositories/Resgrid.Repositories.DataRepository/Servers -name *.cs)",
"Bash(find G:/Resgrid/Resgrid -newer G:/Resgrid/Resgrid/CLAUDE.md -name *.cs -not -path */obj/* -not -path */.git/*)",
Comment on lines +6 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Command path formats are inconsistent and brittle

Line 6 uses G:ResgridResgridCoreResgrid.ModelBilling (missing separators), while other entries mix slash styles and quoting patterns. This is likely to break matching/execution across shells.

Normalize to one path style and quote arguments consistently.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/settings.local.json around lines 6 - 12, The command entries are
inconsistent and brittle (e.g., the malformed path
"G:ResgridResgridCoreResgrid.ModelBilling" and mixed slash styles in lines like
"G:\\Resgrid\\Resgrid/Repositories/Resgrid.Repositories.DataRepository/"), so
normalize all command paths to a single, consistent style (choose either
Windows-style with escaped backslashes like "G:\\Resgrid\\..." or POSIX-style
with forward slashes like "G:/Resgrid/Resgrid/..."), consistently quote
arguments and globs (e.g., "*.cs" and the grep target directory), and ensure any
backslashes are properly escaped for JSON; update the Bash(...) entries (grep
-l, find, and xargs patterns) to use the chosen path style and consistent
quoting so they execute reliably across shells.

"Bash(dotnet build:*)"
Comment on lines +3 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Overly broad command allowlist weakens execution safety

Bash(python3:*), Bash(xargs grep:*), and Bash(dotnet build:*) grant near-arbitrary command surfaces. On shared repos, this significantly increases misuse/blast radius if prompts are compromised.

Please narrow these to explicit command templates and constrained paths only.

Suggested tightening
-      "Bash(python3:*)",
-      "Bash(xargs grep:*)",
-      "Bash(dotnet build:*)"
+      "Bash(python3 scripts/dual_graph_*.py)",
+      "Bash(xargs -0 grep -nE \"DepartmentMembers|DepartmentMember\")",
+      "Bash(dotnet build G:/Resgrid/Resgrid/Resgrid.sln --configuration Release)"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"allow": [
"mcp__dual-graph__graph_scan",
"mcp__dual-graph__graph_continue",
"Bash(find G:ResgridResgridCoreResgrid.ModelBilling -type f -name *.cs)",
"Bash(python3:*)",
"Bash(xargs grep:*)",
"Bash(grep -E \"\\\\.cs$|Program\")",
"Bash(grep -l \"DepartmentMembers\\\\|DepartmentMember\" \"G:\\\\Resgrid\\\\Resgrid/Repositories/Resgrid.Repositories.DataRepository/\"*.cs)",
"Bash(find /g/Resgrid/Resgrid/Repositories/Resgrid.Repositories.DataRepository/Servers -name *.cs)",
"Bash(find G:/Resgrid/Resgrid -newer G:/Resgrid/Resgrid/CLAUDE.md -name *.cs -not -path */obj/* -not -path */.git/*)",
"Bash(dotnet build:*)"
"allow": [
"mcp__dual-graph__graph_scan",
"mcp__dual-graph__graph_continue",
"Bash(find G:ResgridResgridCoreResgrid.ModelBilling -type f -name *.cs)",
"Bash(python3 scripts/dual_graph_*.py)",
"Bash(xargs -0 grep -nE \"DepartmentMembers|DepartmentMember\")",
"Bash(grep -E \"\\\\.cs$|Program\")",
"Bash(grep -l \"DepartmentMembers\\\\|DepartmentMember\" \"G:\\\\Resgrid\\\\Resgrid/Repositories/Resgrid.Repositories.DataRepository/\"*.cs)",
"Bash(find /g/Resgrid/Resgrid/Repositories/Resgrid.Repositories.DataRepository/Servers -name *.cs)",
"Bash(find G:/Resgrid/Resgrid -newer G:/Resgrid/Resgrid/CLAUDE.md -name *.cs -not -path */obj/* -not -path */.git/*)",
"Bash(dotnet build G:/Resgrid/Resgrid/Resgrid.sln --configuration Release)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/settings.local.json around lines 3 - 13, The allowlist contains
overly broad entries (Bash(python3:*), Bash(xargs grep:*), Bash(dotnet build:*))
that permit near-arbitrary command execution; tighten them by replacing wildcard
patterns with explicit, constrained command templates and safe path restrictions
(e.g. restrict python3 to a specific script path or approved flags, limit
xargs/grep invocations to fixed directories or file globs, and constrain dotnet
build to exact project/solution files), update the entries for Bash(python3:*),
Bash(xargs grep:*), and Bash(dotnet build:*) accordingly, and ensure any new
templates document allowed arguments and target paths to minimize blast radius.

]
},
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -NoProfile -File \"G:/Resgrid/Resgrid/.dual-graph/prime.ps1\""
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -NoProfile -File \"G:/Resgrid/Resgrid/.dual-graph/stop_hook.ps1\""
}
]
}
],
"PreCompact": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -NoProfile -File \"G:/Resgrid/Resgrid/.dual-graph/prime.ps1\""
}
]
}
]
Comment on lines +17 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hook scripts depend on gitignored local artifacts without guardrails

Lines 23, 34, and 45 invoke scripts under .dual-graph/, but that directory is gitignored in this repo context. Fresh environments will fail hook execution unless users manually provision those files.

Add an existence check wrapper (or no-op fallback) before calling the scripts.

Suggested resilient hook command pattern
-            "command": "powershell -NoProfile -File \"G:/Resgrid/Resgrid/.dual-graph/prime.ps1\""
+            "command": "powershell -NoProfile -Command \"if (Test-Path 'G:/Resgrid/Resgrid/.dual-graph/prime.ps1') { & 'G:/Resgrid/Resgrid/.dual-graph/prime.ps1' }\""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/settings.local.json around lines 17 - 49, The hooks call external
scripts under ".dual-graph" (seen in the SessionStart, Stop, and PreCompact hook
command entries) but those files are gitignored and may be missing; wrap each
PowerShell invocation with a pre-check using Test-Path for the target file and
only execute the script when it exists, otherwise silently no-op (or emit a
clear info message) so fresh environments don’t fail hook execution; update the
"command" values for the SessionStart, Stop, and PreCompact hook objects to
perform this existence check before calling prime.ps1 or stop_hook.ps1.

}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,4 @@ Web/Resgrid.WebCore/wwwroot/js/ng/*
Web/Resgrid.WebCore/wwwroot/lib/*
/Web/Resgrid.Web/wwwroot/lib
.dual-graph/
.claude/settings.local.json
95 changes: 0 additions & 95 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,98 +92,3 @@ When the user signals they are done (e.g. "bye", "done", "wrap up", "end session
- **Next Steps**: bullet list, max 3 items

Keep `CONTEXT.md` under 20 lines total. Do NOT summarize the full conversation — only what's needed to resume next session.

---

# Coding Standards

## General

Write C# code that maximises readability, maintainability, and correctness while minimising complexity and coupling. Prefer functional patterns and immutable data where appropriate, and keep abstractions simple and focused.

- Write clear, self-documenting code
- Keep abstractions simple and focused
- Minimise dependencies and coupling
- Use modern C# features appropriately
- Use the repository pattern with Dapper at the repository layer for SQL Server and PostgreSQL database communication

## Code Organisation

**Use meaningful names — no unclear abbreviations:**
```csharp
// Good
public async Task<Result<Order>> ProcessOrderAsync(OrderRequest request, CancellationToken cancellationToken)

// Avoid
public async Task<Result<T>> ProcAsync<T>(ReqDto r, CancellationToken ct)
```

**Separate state from behaviour:**
```csharp
// Good
public sealed record Order(OrderId Id, List<OrderLine> Lines);

public static class OrderOperations
{
public static decimal CalculateTotal(Order order) =>
order.Lines.Sum(line => line.Price * line.Quantity);
}
```

**Prefer pure methods — avoid hidden side effects:**
```csharp
// Good
public static decimal CalculateTotalPrice(IEnumerable<OrderLine> lines, decimal taxRate) =>
lines.Sum(line => line.Price * line.Quantity) * (1 + taxRate);

// Avoid
public void CalculateAndUpdateTotalPrice()
{
this.Total = this.Lines.Sum(l => l.Price * l.Quantity);
this.UpdateDatabase();
}
```

**Use extension methods for domain-specific operations:**
```csharp
public static class OrderExtensions
{
public static bool CanBeFulfilled(this Order order, Inventory inventory) =>
order.Lines.All(line => inventory.HasStock(line.ProductId, line.Quantity));
}
```

**Design for testability — avoid hidden dependencies:**
```csharp
// Good: pure, easily testable
public static decimal CalculateDiscount(decimal price, int quantity, CustomerTier tier) => ...

// Avoid: hidden service calls make this impossible to unit test
public decimal CalculateDiscount()
{
var user = _userService.GetCurrentUser();
var settings = _configService.GetSettings();
...
}
```

## Dependency Management

**Minimise constructor injection — too many dependencies signal a design problem:**
```csharp
// Good
public sealed class OrderProcessor(IOrderRepository repository) { }

// Avoid
public class OrderProcessor(
IOrderRepository repository,
ILogger logger,
IEmailService emailService,
IMetrics metrics,
IValidator validator) { }
```

**Prefer composition via interfaces:**
```csharp
public sealed class EnhancedLogger(ILogger baseLogger, IMetrics metrics) : ILogger { }
```
51 changes: 51 additions & 0 deletions Core/Resgrid.Config/PaymentProviderConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ public static class PaymentProviderConfig
public static string TenEntityPackage = "6f4c5f8b-584d-4291-8a7d-29bf97ae6aa9";
public static string TenEntityPackageTest = "6f4c5f8b-584d-4291-8a7d-29bf97ae6aa9";

public static string PaddleProductionApiKey = "";
public static string PaddleTestApiKey = "";
public static string PaddleProductionBillingWebhookSigningKey = "";
public static string PaddleTestBillingWebhookSigningKey = "";
public static string PaddlePTT10UserAddonPackage = "";
public static string PaddlePTT10UserAddonPackageTest = "";
public static string PaddleProductionEnvironment = "production";
public static string PaddleTestEnvironment = "sandbox";
public static string PaddleProductionClientToken = "";
public static string PaddleTestClientToken = "";

public static string GetStripeClientKey()
{
if (IsTestMode)
Expand Down Expand Up @@ -72,5 +83,45 @@ public static string GetTenEntityPackageId()
else
return TenEntityPackage;
}

public static string GetPaddleApiKey()
{
if (IsTestMode)
return PaddleTestApiKey;
else
return PaddleProductionApiKey;
}

public static string GetPaddleBillingWebhookSigningKey()
{
if (IsTestMode)
return PaddleTestBillingWebhookSigningKey;
else
return PaddleProductionBillingWebhookSigningKey;
}

public static string GetPaddlePTT10UserAddonPackageId()
{
if (IsTestMode)
return PaddlePTT10UserAddonPackageTest;
else
return PaddlePTT10UserAddonPackage;
}

public static string GetPaddleEnvironment()
{
if (IsTestMode)
return PaddleTestEnvironment;
else
return PaddleProductionEnvironment;
}

public static string GetPaddleClientToken()
{
if (IsTestMode)
return PaddleTestClientToken;
else
return PaddleProductionClientToken;
}
}
}
6 changes: 6 additions & 0 deletions Core/Resgrid.Config/SystemBehaviorConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public static class SystemBehaviorConfig
/// </summary>
public static string BillingApiBaseUrl = "";

/// <summary>
/// When true, new accounts must select and pay for a plan during registration.
/// Free tier signups are not allowed. Intended for EU-Central deployments.
/// </summary>
public static bool RequirePlanSelectionDuringSignup = false;

/// <summary>
/// This will prevent the system from sending any outbound messages, for example
/// email, push, text or call. Allows for testing the system without risk of sending
Expand Down
30 changes: 1 addition & 29 deletions Core/Resgrid.Config/TelemetryConfig.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
namespace Resgrid.Config
namespace Resgrid.Config
{
public static class TelemetryConfig
{
public static string Exporter = "";

public static TelemetryExporters ExporterType = TelemetryExporters.None;
public static string PostHogUrl = "";
public static string PostHogApiKey = "";

public static string AptabaseUrl = "";
public static string AptabaseWebApiKey = "";
public static string AptabaseServicesApiKey = "";
public static string AptabaseResponderApiKey = "";
public static string AptabaseUnitApiKey = "";
public static string AptabaseBigBoardApiKey = "";
public static string AptabaseDispatchApiKey = "";

public static string CountlyUrl = "";
public static string CountlyWebKey = "";

public static string GetAnalyticsKey()
{
if (ExporterType == TelemetryExporters.PostHog)
return PostHogApiKey;
else if (ExporterType == TelemetryExporters.Aptabase)
return AptabaseWebApiKey;
return string.Empty;
}
}

public enum TelemetryExporters
{
None = 0,
PostHog = 1,
Aptabase = 2
}
}
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.ar.resx
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,10 @@
<data name="Username" xml:space="preserve">
<value>اسم المستخدم</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Benutzername</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,10 @@
<data name="Username" xml:space="preserve">
<value>Username</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.es.resx
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,10 @@
<data name="Username" xml:space="preserve">
<value>Nombre de usuario</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.fr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Nom d'utilisateur</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.it.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Nome utente</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.pl.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Nazwa użytkownika</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.sv.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Användarnamn</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
6 changes: 6 additions & 0 deletions Core/Resgrid.Localization/Account/Login.uk.resx
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,10 @@
<data name="Username" xml:space="preserve">
<value>Ім'я користувача</value>
</data>
<data name="DiscountCode" xml:space="preserve">
<value>Discount Code</value>
</data>
<data name="AffiliateCode" xml:space="preserve">
<value>Affiliate Code</value>
</data>
</root>
Loading
Loading