Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efcf06969c | |||
| 56c931f7da | |||
| 54edbd21f6 |
@@ -14,7 +14,41 @@
|
||||
"Bash(powershell -Command \"& { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::OpenRead\\(''updates/0.20/ver_0.296.zip''\\).Entries | ForEach-Object { Write-Output $_.FullName } }\")",
|
||||
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.296.zip'' -Force\")",
|
||||
"Bash(powershell -Command \"Add-Type -AssemblyName System.IO.Compression.FileSystem; [IO.Compression.ZipFile]::OpenRead\\(\\(Resolve-Path ''updates/0.20/ver_0.296.zip''\\)\\).Entries.FullName\")",
|
||||
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.297.zip'' -Force\")"
|
||||
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.297.zip'' -Force\")",
|
||||
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../../updates/0.20/ver_0.299.zip'' -Force\")",
|
||||
"Bash(powershell -Command \"Remove-Item -Recurse -Force 'c:/visual studio code/projekty/shopPRO/temp/temp_299'\":*)",
|
||||
"Bash(powershell -Command \"\\(Get-ChildItem ''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Length; [System.IO.Compression.ZipFile]::OpenRead\\(''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Entries | ForEach-Object { $_.FullName }\")",
|
||||
"Bash(powershell -Command \"Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::OpenRead\\(''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Entries | ForEach-Object { $_.FullName }\")",
|
||||
"Bash(unzip:*)",
|
||||
"mcp__serena__find_symbol",
|
||||
"mcp__serena__find_file",
|
||||
"mcp__serena__activate_project",
|
||||
"mcp__serena__check_onboarding_performed",
|
||||
"Bash(tail:*)",
|
||||
"WebFetch(domain:shoppro.project-dc.pl)",
|
||||
"Bash(cd:*)",
|
||||
"mcp__serena__get_symbols_overview",
|
||||
"mcp__serena__search_for_pattern",
|
||||
"mcp__serena__read_file",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && powershell.exe -ExecutionPolicy Bypass -File \"C:/visual studio code/projekty/shopPRO/test.ps1\" 2>&1)",
|
||||
"mcp__serena__replace_content",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && npx sass admin/layout/style-scss/style.scss admin/layout/style-css/style.css --source-map 2>&1)",
|
||||
"Bash(head:*)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_304 && powershell -File \"./build-update.ps1\" -FromTag v0.303 -ToTag v0.304 -ChangelogEntry \"NEW - konfigurowalne limity kwotowe metod platnosci \\(min/max kwota zamowienia\\)\" 2>&1)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && powershell -File \"./build-update.ps1\" -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
|
||||
"Bash(xxd:*)",
|
||||
"mcp__serena__list_dir",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.305 -ToTag v0.306 -ChangelogEntry \"FIX - ukrywanie form dostawy gdy nie ma dostepnych form platnosci\" 2>&1)",
|
||||
"Bash(powershell:*)",
|
||||
"Bash(powershell.exe:*)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -f updates/0.30/ver_0.304.zip updates/0.30/ver_0.304_manifest.json updates/0.30/ver_0.304_sql.txt updates/0.30/ver_0.304_files.txt && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.303 -ToTag v0.304 -ChangelogEntry \"NEW - konfigurowalne limity kwotowe metod platnosci \\(min/max kwota zamowienia\\)\" 2>&1)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -f updates/0.30/ver_0.305.zip updates/0.30/ver_0.305_manifest.json updates/0.30/ver_0.305_sql.txt updates/0.30/ver_0.305_files.txt && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
|
||||
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && sleep 2 && powershell -ExecutionPolicy Bypass -Command \"& { \\\\$env:DOTNET_GCServer = 1; & './build-update.ps1' -FromTag v0.304 -ToTag v0.305 -ChangelogEntry 'FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku' }\" 2>&1)",
|
||||
"Bash(python3:*)",
|
||||
"Bash(python:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(grep ^<b>ver:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
.serena/.gitignore
vendored
Normal file
1
.serena/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/cache
|
||||
117
.serena/project.yml
Normal file
117
.serena/project.yml
Normal file
@@ -0,0 +1,117 @@
|
||||
# the name by which the project can be referenced within Serena
|
||||
project_name: "shopPRO"
|
||||
|
||||
|
||||
# list of languages for which language servers are started; choose from:
|
||||
# al bash clojure cpp csharp
|
||||
# csharp_omnisharp dart elixir elm erlang
|
||||
# fortran fsharp go groovy haskell
|
||||
# java julia kotlin lua markdown
|
||||
# matlab nix pascal perl php
|
||||
# php_phpactor powershell python python_jedi r
|
||||
# rego ruby ruby_solargraph rust scala
|
||||
# swift terraform toml typescript typescript_vts
|
||||
# vue yaml zig
|
||||
# (This list may be outdated. For the current list, see values of Language enum here:
|
||||
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
||||
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
||||
# Note:
|
||||
# - For C, use cpp
|
||||
# - For JavaScript, use typescript
|
||||
# - For Free Pascal/Lazarus, use pascal
|
||||
# Special requirements:
|
||||
# Some languages require additional setup/installations.
|
||||
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
||||
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
||||
# The first language is the default language and the respective language server will be used as a fallback.
|
||||
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
||||
languages:
|
||||
- typescript
|
||||
|
||||
# the encoding used by text files in the project
|
||||
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
||||
encoding: "utf-8"
|
||||
|
||||
# whether to use project's .gitignore files to ignore files
|
||||
ignore_all_files_in_gitignore: true
|
||||
|
||||
# list of additional paths to ignore in this project.
|
||||
# Same syntax as gitignore, so you can use * and **.
|
||||
# Note: global ignored_paths from serena_config.yml are also applied additively.
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
|
||||
included_optional_tools: []
|
||||
|
||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
||||
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
||||
fixed_tools: []
|
||||
|
||||
# list of mode names to that are always to be included in the set of active modes
|
||||
# The full set of modes to be activated is base_modes + default_modes.
|
||||
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
|
||||
# Otherwise, this setting overrides the global configuration.
|
||||
# Set this to [] to disable base modes for this project.
|
||||
# Set this to a list of mode names to always include the respective modes for this project.
|
||||
base_modes:
|
||||
|
||||
# list of mode names that are to be activated by default.
|
||||
# The full set of modes to be activated is base_modes + default_modes.
|
||||
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
|
||||
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
||||
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
||||
default_modes:
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
# override of the corresponding setting in serena_config.yml, see the documentation there.
|
||||
# If null or missing, the value from the global config is used.
|
||||
symbol_info_budget:
|
||||
@@ -36,7 +36,7 @@ composer test
|
||||
|
||||
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
||||
|
||||
Current suite: **739 tests, 2089 assertions**.
|
||||
Current suite: **750 tests, 2114 assertions**.
|
||||
|
||||
### Creating Updates
|
||||
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs.
|
||||
|
||||
@@ -9,7 +9,7 @@ $buildUrl = function(array $params = []) use ($list): string {
|
||||
}
|
||||
}
|
||||
$qs = http_build_query($query);
|
||||
return $list->basePath . ($qs ? ('?' . $qs) : '');
|
||||
return $list->basePath . $qs;
|
||||
};
|
||||
|
||||
$currentSort = $list->sort['column'] ?? '';
|
||||
@@ -92,7 +92,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="js-table-filters-wrapper table-filters-wrapper<?= $hasActiveFilters ? ' open' : ''; ?>">
|
||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
|
||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" data-path-submit="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
|
||||
<?php foreach ($list->filters as $filter): ?>
|
||||
<?php
|
||||
$filterKey = (string)($filter['key'] ?? '');
|
||||
@@ -292,7 +292,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-6 text-right">
|
||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="form-inline table-list-per-page-form">
|
||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" data-path-submit="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="form-inline table-list-per-page-form">
|
||||
<?php foreach ($list->query as $key => $value): ?>
|
||||
<?php if ($key !== 'per_page' && $key !== 'page'): ?>
|
||||
<input type="hidden" name="<?= htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); ?>" value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); ?>" />
|
||||
@@ -300,7 +300,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
<?php endforeach; ?>
|
||||
<input type="hidden" name="page" value="1" />
|
||||
Wyświetlaj
|
||||
<select name="per_page" class="form-control input-sm" onchange="this.form.submit()">
|
||||
<select name="per_page" class="form-control input-sm js-per-page-select">
|
||||
<?php foreach ($list->perPageOptions as $opt): ?>
|
||||
<option value="<?= (int)$opt; ?>"<?= ((int)$opt === $perPage) ? ' selected="selected"' : ''; ?>><?= (int)$opt; ?></option>
|
||||
<?php endforeach; ?>
|
||||
@@ -529,5 +529,26 @@ $isCompactColumn = function(array $column): bool {
|
||||
saveFilterState(true);
|
||||
}
|
||||
});
|
||||
|
||||
// --- Path-based form submission (admin URL routing) ---
|
||||
$(document).off('submit.tablePathSubmit', 'form[data-path-submit]');
|
||||
$(document).on('submit.tablePathSubmit', 'form[data-path-submit]', function(e) {
|
||||
e.preventDefault();
|
||||
var basePath = $(this).attr('data-path-submit');
|
||||
var data = $(this).serializeArray();
|
||||
var parts = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (String(data[i].value) !== '') {
|
||||
parts.push(encodeURIComponent(data[i].name) + '=' + encodeURIComponent(data[i].value));
|
||||
}
|
||||
}
|
||||
window.location.href = basePath + (parts.length ? parts.join('&') : '');
|
||||
});
|
||||
|
||||
// Per-page select auto-submit
|
||||
$(document).off('change.tablePerPage', '.js-per-page-select');
|
||||
$(document).on('change.tablePerPage', '.js-per-page-select', function() {
|
||||
$(this).closest('form').trigger('submit');
|
||||
});
|
||||
})(window.jQuery);
|
||||
</script>
|
||||
|
||||
@@ -30,6 +30,14 @@ class OrderAdminService
|
||||
return $this->orders->orderStatuses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{names: array<int, string>, colors: array<int, string>}
|
||||
*/
|
||||
public function statusData(): array
|
||||
{
|
||||
return $this->orders->orderStatusData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
|
||||
@@ -245,25 +245,43 @@ class OrderRepository
|
||||
|
||||
public function orderStatuses(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_statuses', ['id', 'status'], [
|
||||
$data = $this->orderStatusData();
|
||||
return $data['names'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca nazwy i kolory statusów w jednym zapytaniu.
|
||||
*
|
||||
* @return array{names: array<int, string>, colors: array<int, string>}
|
||||
*/
|
||||
public function orderStatusData(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_statuses', ['id', 'status', 'color'], [
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
$names = [];
|
||||
$colors = [];
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
return ['names' => $names, 'colors' => $colors];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$id = (int)($row['id'] ?? 0);
|
||||
if ($id < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$id] = (string)($row['status'] ?? '');
|
||||
$names[$id] = (string)($row['status'] ?? '');
|
||||
|
||||
$color = trim((string)($row['color'] ?? ''));
|
||||
if ($color !== '' && preg_match('/^#[0-9a-fA-F]{3,6}$/', $color)) {
|
||||
$colors[$id] = $color;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
return ['names' => $names, 'colors' => $colors];
|
||||
}
|
||||
|
||||
public function nextOrderId(int $orderId): ?int
|
||||
|
||||
@@ -69,7 +69,9 @@ class ShopOrderController
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$statusesMap = $this->service->statuses();
|
||||
$statusData = $this->service->statusData();
|
||||
$statusesMap = $statusData['names'];
|
||||
$statusColorsMap = $statusData['colors'];
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
|
||||
@@ -77,7 +79,15 @@ class ShopOrderController
|
||||
$orderId = (int)($item['id'] ?? 0);
|
||||
$orderNumber = (string)($item['number'] ?? '');
|
||||
$statusId = (int)($item['status'] ?? 0);
|
||||
$statusLabel = (string)($statusesMap[$statusId] ?? ('Status #' . $statusId));
|
||||
$statusLabel = htmlspecialchars((string)($statusesMap[$statusId] ?? ('Status #' . $statusId)), ENT_QUOTES, 'UTF-8');
|
||||
$statusColor = isset($statusColorsMap[$statusId]) ? $statusColorsMap[$statusId] : '';
|
||||
|
||||
if ($statusColor !== '') {
|
||||
$textColor = $this->contrastTextColor($statusColor);
|
||||
$statusHtml = '<span class="label" style="background-color:' . htmlspecialchars($statusColor, ENT_QUOTES, 'UTF-8') . ';color:' . $textColor . '">' . $statusLabel . '</span>';
|
||||
} else {
|
||||
$statusHtml = $statusLabel;
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
@@ -86,13 +96,13 @@ class ShopOrderController
|
||||
'paid' => ((int)($item['paid'] ?? 0) === 1)
|
||||
? '<i class="fa fa-check text-success"></i>'
|
||||
: '<i class="fa fa-times text-dark"></i>',
|
||||
'status' => htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'status' => $statusHtml,
|
||||
'summary' => number_format((float)($item['summary'] ?? 0), 2, '.', ' ') . ' zł',
|
||||
'client' => htmlspecialchars((string)($item['client'] ?? ''), ENT_QUOTES, 'UTF-8') . ' | zamówienia: <strong>' . (int)($item['total_orders'] ?? 0) . '</strong>',
|
||||
'address' => (string)($item['address'] ?? ''),
|
||||
'order_email' => (string)($item['order_email'] ?? ''),
|
||||
'client_phone' => (string)($item['client_phone'] ?? ''),
|
||||
'transport' => (string)($item['transport'] ?? ''),
|
||||
'transport' => $this->sanitizeInlineHtml((string)($item['transport'] ?? '')),
|
||||
'payment_method' => (string)($item['payment_method'] ?? ''),
|
||||
'_actions' => [
|
||||
[
|
||||
@@ -127,7 +137,7 @@ class ShopOrderController
|
||||
['key' => 'address', 'label' => 'Adres', 'sortable' => false],
|
||||
['key' => 'order_email', 'sort_key' => 'order_email', 'label' => 'Email', 'sortable' => true],
|
||||
['key' => 'client_phone', 'sort_key' => 'client_phone', 'label' => 'Telefon', 'sortable' => true],
|
||||
['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true],
|
||||
['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'payment_method', 'sort_key' => 'payment_method', 'label' => 'Płatność', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
@@ -361,4 +371,26 @@ class ShopOrderController
|
||||
|
||||
return date('Y-m-d H:i', $ts);
|
||||
}
|
||||
}
|
||||
|
||||
private function contrastTextColor(string $hex): string
|
||||
{
|
||||
$hex = ltrim($hex, '#');
|
||||
if (strlen($hex) === 3) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
if (strlen($hex) !== 6) {
|
||||
return '#fff';
|
||||
}
|
||||
$r = hexdec(substr($hex, 0, 2));
|
||||
$g = hexdec(substr($hex, 2, 2));
|
||||
$b = hexdec(substr($hex, 4, 2));
|
||||
$luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
|
||||
return $luminance > 0.5 ? '#000' : '#fff';
|
||||
}
|
||||
|
||||
private function sanitizeInlineHtml(string $html): string
|
||||
{
|
||||
$html = strip_tags($html, '<b><strong><i><em>');
|
||||
return preg_replace('/<(b|strong|i|em)\s[^>]*>/i', '<$1>', $html);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$database['host'] = 'localhost';
|
||||
$database['remote_host'] = 'host700513.hostido.net.pl';
|
||||
$database['user'] = 'host117523_shoppro';
|
||||
$database['password'] = 'mhA9WCEXEnRfTtbN33hL';
|
||||
$database['name'] = 'host117523_shoppro';
|
||||
|
||||
@@ -4,6 +4,17 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.308 (2026-02-22) - Kolory statusow zamowien + poprawki bezpieczenstwa
|
||||
|
||||
- **NEW**: Kolorowe badge statusow na liscie zamowien w admin panelu — kolory pobierane z `pp_shop_statuses.color`, kontrast tekstu obliczany automatycznie
|
||||
- **FIX**: Walidacja formatu hex koloru z bazy (`/^#[0-9a-fA-F]{3,6}$/`) — odrzucanie nieprawidlowych wartosci
|
||||
- **FIX**: Sanityzacja HTML w kolumnie "Dostawa" — `strip_tags()` + regex usuwajacy atrybuty z dozwolonych tagow (zapobieganie XSS via `onclick` itp.)
|
||||
- **OPTYMALIZACJA**: Polaczenie dwoch zapytan SQL do `pp_shop_statuses` (nazwy + kolory) w jedno `orderStatusData()`
|
||||
- **ZMIANA**: Path-based form submit w `table-list.php` — formularze filtrow i per-page uzywaja JS interceptora z `data-path-submit` zamiast natywnego GET, kompatybilne z admin URL routing
|
||||
- **NEW**: 11 nowych testow jednostkowych (750 total, 2114 assertions)
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.307 (2026-02-22) - Przycisk sprawdzania aktualizacji + auto-changelog
|
||||
|
||||
- **NEW**: Przycisk "Sprawdz aktualizacje" w panelu admina — ikona odswiezenia obok numeru wersji, klik odpytuje serwer AJAX-em i pokazuje/ukrywa badge "aktualizacja" bez przeladowania strony
|
||||
|
||||
@@ -23,7 +23,7 @@ composer test # standard
|
||||
## Aktualny stan
|
||||
|
||||
```text
|
||||
OK (739 tests, 2089 assertions)
|
||||
OK (750 tests, 2114 assertions)
|
||||
```
|
||||
|
||||
Zweryfikowano: 2026-02-22 (ver. 0.304)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
|
||||
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
|
||||
ALTER TABLE pp_shop_payment_methods ADD COLUMN max_order_amount DECIMAL(10,2) DEFAULT NULL;
|
||||
|
||||
@@ -29,6 +29,66 @@ class OrderRepositoryTest extends TestCase
|
||||
$this->assertSame('W realizacji', $statuses[4]);
|
||||
}
|
||||
|
||||
public function testOrderStatusDataReturnsBothNamesAndColors(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('select')
|
||||
->willReturnCallback(function ($table, $columns, $where) {
|
||||
if ($table === 'pp_shop_statuses') {
|
||||
return [
|
||||
['id' => 0, 'status' => 'Nowe', 'color' => '#ff0000'],
|
||||
['id' => 4, 'status' => 'W realizacji', 'color' => '#00ff00'],
|
||||
['id' => 5, 'status' => 'Wysłane', 'color' => ''],
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
$repository = new OrderRepository($mockDb);
|
||||
$data = $repository->orderStatusData();
|
||||
|
||||
$this->assertArrayHasKey('names', $data);
|
||||
$this->assertArrayHasKey('colors', $data);
|
||||
$this->assertSame('Nowe', $data['names'][0]);
|
||||
$this->assertSame('W realizacji', $data['names'][4]);
|
||||
$this->assertSame('Wysłane', $data['names'][5]);
|
||||
$this->assertSame('#ff0000', $data['colors'][0]);
|
||||
$this->assertSame('#00ff00', $data['colors'][4]);
|
||||
$this->assertArrayNotHasKey(5, $data['colors']);
|
||||
}
|
||||
|
||||
public function testOrderStatusDataFiltersInvalidHexColors(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('select')
|
||||
->willReturn([
|
||||
['id' => 1, 'status' => 'OK', 'color' => '#abc'],
|
||||
['id' => 2, 'status' => 'Bad', 'color' => 'red'],
|
||||
['id' => 3, 'status' => 'XSS', 'color' => '#000" onclick="alert(1)'],
|
||||
['id' => 4, 'status' => 'Valid', 'color' => '#AABBCC'],
|
||||
]);
|
||||
|
||||
$repository = new OrderRepository($mockDb);
|
||||
$data = $repository->orderStatusData();
|
||||
|
||||
$this->assertSame('#abc', $data['colors'][1]);
|
||||
$this->assertArrayNotHasKey(2, $data['colors']);
|
||||
$this->assertArrayNotHasKey(3, $data['colors']);
|
||||
$this->assertSame('#AABBCC', $data['colors'][4]);
|
||||
}
|
||||
|
||||
public function testOrderStatusDataReturnsEmptyOnDbFailure(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('select')->willReturn(false);
|
||||
|
||||
$repository = new OrderRepository($mockDb);
|
||||
$data = $repository->orderStatusData();
|
||||
|
||||
$this->assertSame([], $data['names']);
|
||||
$this->assertSame([], $data['colors']);
|
||||
}
|
||||
|
||||
public function testNextAndPrevOrderIdReturnNullForInvalidInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
@@ -85,4 +85,72 @@ class ShopOrderControllerTest extends TestCase
|
||||
$this->assertEquals('Domain\\Product\\ProductRepository', $params[1]->getType()->getName());
|
||||
$this->assertTrue($params[1]->isOptional());
|
||||
}
|
||||
|
||||
// --- contrastTextColor tests (via reflection) ---
|
||||
|
||||
public function testContrastTextColorReturnsBlackForLightColor(): void
|
||||
{
|
||||
$result = $this->invokePrivate('contrastTextColor', ['#ffffff']);
|
||||
$this->assertSame('#000', $result);
|
||||
}
|
||||
|
||||
public function testContrastTextColorReturnsWhiteForDarkColor(): void
|
||||
{
|
||||
$result = $this->invokePrivate('contrastTextColor', ['#000000']);
|
||||
$this->assertSame('#fff', $result);
|
||||
}
|
||||
|
||||
public function testContrastTextColorHandlesShortHex(): void
|
||||
{
|
||||
$result = $this->invokePrivate('contrastTextColor', ['#fff']);
|
||||
$this->assertSame('#000', $result);
|
||||
|
||||
$result = $this->invokePrivate('contrastTextColor', ['#000']);
|
||||
$this->assertSame('#fff', $result);
|
||||
}
|
||||
|
||||
public function testContrastTextColorDefaultsToWhiteForInvalidHex(): void
|
||||
{
|
||||
$result = $this->invokePrivate('contrastTextColor', ['invalid']);
|
||||
$this->assertSame('#fff', $result);
|
||||
|
||||
$result = $this->invokePrivate('contrastTextColor', ['#zz']);
|
||||
$this->assertSame('#fff', $result);
|
||||
}
|
||||
|
||||
// --- sanitizeInlineHtml tests (via reflection) ---
|
||||
|
||||
public function testSanitizeInlineHtmlStripsDisallowedTags(): void
|
||||
{
|
||||
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b>Bold</b> <script>alert(1)</script> <em>Italic</em>']);
|
||||
$this->assertSame('<b>Bold</b> alert(1) <em>Italic</em>', $result);
|
||||
}
|
||||
|
||||
public function testSanitizeInlineHtmlStripsAttributesFromAllowedTags(): void
|
||||
{
|
||||
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b onclick="alert(1)">Bold</b>']);
|
||||
$this->assertSame('<b>Bold</b>', $result);
|
||||
|
||||
$result = $this->invokePrivate('sanitizeInlineHtml', ['<strong style="color:red" class="x">text</strong>']);
|
||||
$this->assertSame('<strong>text</strong>', $result);
|
||||
}
|
||||
|
||||
public function testSanitizeInlineHtmlPreservesCleanTags(): void
|
||||
{
|
||||
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b>Bold</b> <i>Italic</i> <strong>Strong</strong> <em>Em</em>']);
|
||||
$this->assertSame('<b>Bold</b> <i>Italic</i> <strong>Strong</strong> <em>Em</em>', $result);
|
||||
}
|
||||
|
||||
public function testSanitizeInlineHtmlHandlesPlainText(): void
|
||||
{
|
||||
$result = $this->invokePrivate('sanitizeInlineHtml', ['Kurier DPD']);
|
||||
$this->assertSame('Kurier DPD', $result);
|
||||
}
|
||||
|
||||
private function invokePrivate(string $method, array $args)
|
||||
{
|
||||
$reflection = new \ReflectionMethod($this->controller, $method);
|
||||
$reflection->setAccessible(true);
|
||||
return $reflection->invokeArgs($this->controller, $args);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
updates/0.30/ver_0.307.zip
Normal file
BIN
updates/0.30/ver_0.307.zip
Normal file
Binary file not shown.
24
updates/0.30/ver_0.307_manifest.json
Normal file
24
updates/0.30/ver_0.307_manifest.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"changelog": "NEW - przycisk Sprawdz aktualizacje w panelu admina, NEW - auto-generowany changelog z manifestow",
|
||||
"version": "0.307",
|
||||
"files": {
|
||||
"added": [
|
||||
|
||||
],
|
||||
"deleted": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"admin/templates/site/main-layout.php",
|
||||
"autoload/admin/Controllers/UpdateController.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:175c64537f0238a03903758c2d8ab154277b3889d35551ede17d158e3f62de7d",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-02-22",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?
|
||||
$current_ver = 307;
|
||||
$current_ver = 308;
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user