mirror of
https://github.com/we-promise/sure.git
synced 2026-05-24 13:04:56 +00:00
feat(settings): add pagination to imports and exports pages (#598)
* feat(settings): split imports and exports * feat(security): sanitize pagination params to prevent abuse * fix(settings): fix syntax in settings nav * feat(settings): internationalize family_exports and imports UI strings * fix(settings): fix coderabbit review * fix(settings): fix coderabbit review * fix(settings): fix coderabbit review * Change default per_page value from 20 to 10 Signed-off-by: Juan José Mata <jjmata@jjmata.com> * Add `/family_export` to navigation * Consistency with old defaults * Align `safe_per_page` even if not DRY --------- Signed-off-by: Julien Orain <julien.orain@gmail.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: JulienOrain <your-github-email@example.com> Co-authored-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -38,7 +38,7 @@ class AccountsController < ApplicationController
|
||||
@q = params.fetch(:q, {}).permit(:search, status: [])
|
||||
entries = @account.entries.where(excluded: false).search(@q).reverse_chronological
|
||||
|
||||
@pagy, @entries = pagy(entries, limit: params[:per_page] || "10")
|
||||
@pagy, @entries = pagy(entries, limit: safe_per_page)
|
||||
|
||||
@activity_feed_data = Account::ActivityFeedData.new(@account, @entries)
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
include RestoreLayoutPreferences, Onboardable, Localize, AutoSync, Authentication, Invitable,
|
||||
SelfHostable, StoreLocation, Impersonatable, Breadcrumbable,
|
||||
FeatureGuardable, Notifiable
|
||||
FeatureGuardable, Notifiable, SafePagination
|
||||
include Pundit::Authorization
|
||||
|
||||
include Pagy::Backend
|
||||
|
||||
@@ -27,7 +27,7 @@ module AccountableResource
|
||||
@q = params.fetch(:q, {}).permit(:search)
|
||||
entries = @account.entries.search(@q).reverse_chronological
|
||||
|
||||
@pagy, @entries = pagy(entries, limit: params[:per_page] || "10")
|
||||
@pagy, @entries = pagy(entries, limit: safe_per_page(10))
|
||||
end
|
||||
|
||||
def edit
|
||||
|
||||
15
app/controllers/concerns/safe_pagination.rb
Normal file
15
app/controllers/concerns/safe_pagination.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SafePagination
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
def safe_per_page(default = 10)
|
||||
allowed_values = [ 10, 20, 30, 50, 100 ]
|
||||
per_page = params[:per_page].to_i
|
||||
|
||||
return default if per_page <= 0
|
||||
|
||||
allowed_values.include?(per_page) ? per_page : allowed_values.min_by { |v| (v - per_page).abs }
|
||||
end
|
||||
end
|
||||
@@ -13,29 +13,33 @@ class FamilyExportsController < ApplicationController
|
||||
FamilyDataExportJob.perform_later(@export)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to imports_path, notice: "Export started. You'll be able to download it shortly." }
|
||||
format.html { redirect_to family_exports_path, notice: t("family_exports.create.success") }
|
||||
format.turbo_stream {
|
||||
stream_redirect_to imports_path, notice: "Export started. You'll be able to download it shortly."
|
||||
stream_redirect_to family_exports_path, notice: t("family_exports.create.success")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@exports = Current.family.family_exports.ordered.limit(10)
|
||||
render layout: false # For turbo frame
|
||||
@pagy, @exports = pagy(Current.family.family_exports.ordered, limit: safe_per_page)
|
||||
@breadcrumbs = [
|
||||
[ t("breadcrumbs.home"), root_path ],
|
||||
[ t("breadcrumbs.exports"), family_exports_path ]
|
||||
]
|
||||
render layout: "settings"
|
||||
end
|
||||
|
||||
def download
|
||||
if @export.downloadable?
|
||||
redirect_to @export.export_file, allow_other_host: true
|
||||
else
|
||||
redirect_to imports_path, alert: "Export not ready for download"
|
||||
redirect_to family_exports_path, alert: t("family_exports.export_not_ready")
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@export.destroy
|
||||
redirect_to imports_path, notice: "Export deleted successfully"
|
||||
redirect_to family_exports_path, notice: t("family_exports.destroy.success")
|
||||
end
|
||||
|
||||
private
|
||||
@@ -46,7 +50,7 @@ class FamilyExportsController < ApplicationController
|
||||
|
||||
def require_admin
|
||||
unless Current.user.admin?
|
||||
redirect_to root_path, alert: "Access denied"
|
||||
redirect_to root_path, alert: t("family_exports.access_denied")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,11 +12,10 @@ class ImportsController < ApplicationController
|
||||
end
|
||||
|
||||
def index
|
||||
@imports = Current.family.imports
|
||||
@exports = Current.user.admin? ? Current.family.family_exports.ordered.limit(10) : nil
|
||||
@pagy, @imports = pagy(Current.family.imports.where(type: Import::TYPES).ordered, limit: safe_per_page)
|
||||
@breadcrumbs = [
|
||||
[ "Home", root_path ],
|
||||
[ "Import/Export", imports_path ]
|
||||
[ t("breadcrumbs.home"), root_path ],
|
||||
[ t("breadcrumbs.imports"), imports_path ]
|
||||
]
|
||||
render layout: "settings"
|
||||
end
|
||||
@@ -90,7 +89,7 @@ class ImportsController < ApplicationController
|
||||
|
||||
private
|
||||
def set_import
|
||||
@import = Current.family.imports.find(params[:id])
|
||||
@import = Current.family.imports.includes(:account).find(params[:id])
|
||||
end
|
||||
|
||||
def import_params
|
||||
|
||||
@@ -20,7 +20,7 @@ class RulesController < ApplicationController
|
||||
.recent
|
||||
.includes(:rule)
|
||||
|
||||
@pagy, @recent_runs = pagy(recent_runs_scope, limit: params[:per_page] || 20, page_param: :runs_page)
|
||||
@pagy, @recent_runs = pagy(recent_runs_scope, limit: safe_per_page, page_param: :runs_page)
|
||||
|
||||
render layout: "settings"
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ class TransactionsController < ApplicationController
|
||||
:transfer_as_inflow, :transfer_as_outflow
|
||||
)
|
||||
|
||||
@pagy, @transactions = pagy(base_scope, limit: per_page)
|
||||
@pagy, @transactions = pagy(base_scope, limit: safe_per_page)
|
||||
|
||||
# Load projected recurring transactions for next month
|
||||
@projected_recurring = Current.family.recurring_transactions
|
||||
@@ -281,10 +281,6 @@ class TransactionsController < ApplicationController
|
||||
end
|
||||
|
||||
private
|
||||
def per_page
|
||||
params[:per_page].to_i.positive? ? params[:per_page].to_i : 20
|
||||
end
|
||||
|
||||
def needs_rule_notification?(transaction)
|
||||
return false if Current.user.rule_prompts_disabled
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ module SettingsHelper
|
||||
{ name: "Self-Hosting", path: :settings_hosting_path, condition: :self_hosted_and_admin? },
|
||||
{ name: "Providers", path: :settings_providers_path, condition: :admin_user? },
|
||||
{ name: "Imports", path: :imports_path, condition: :admin_user? },
|
||||
{ name: "Exports", path: :family_exports_path, condition: :admin_user? },
|
||||
# More section
|
||||
{ name: "Guides", path: :settings_guides_path },
|
||||
{ name: "What's new", path: :changelog_path },
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="flex items-center justify-between mx-4 py-4">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-primary">Export from <%= export.created_at.strftime("%B %d, %Y at %I:%M %p") %></p>
|
||||
<p class="text-sm font-medium text-primary"><%= t("family_exports.list.export_from", date: l(export.created_at, format: :long)) %></p>
|
||||
<p class="text-xs text-secondary"><%= export.filename %></p>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<% if export.processing? || export.pending? %>
|
||||
<div class="flex items-center gap-2 text-secondary">
|
||||
<div class="animate-spin h-4 w-4 border-2 border-secondary border-t-transparent rounded-full"></div>
|
||||
<span class="text-sm">Exporting...</span>
|
||||
<span class="text-sm"><%= t("family_exports.exporting") %></span>
|
||||
</div>
|
||||
<% elsif export.completed? %>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -39,7 +39,7 @@
|
||||
method: :delete,
|
||||
class: "flex items-center gap-2 text-destructive hover:text-destructive-hover",
|
||||
data: {
|
||||
turbo_confirm: "Are you sure you want to delete this export? This action cannot be undone.",
|
||||
turbo_confirm: t("family_exports.delete_confirmation"),
|
||||
turbo_frame: "_top"
|
||||
} do %>
|
||||
<%= icon "trash-2", class: "w-5 h-5 text-destructive" %>
|
||||
@@ -61,7 +61,7 @@
|
||||
method: :delete,
|
||||
class: "flex items-center gap-2 text-destructive hover:text-destructive-hover",
|
||||
data: {
|
||||
turbo_confirm: "Are you sure you want to delete this failed export?",
|
||||
turbo_confirm: t("family_exports.delete_failed_confirmation"),
|
||||
turbo_frame: "_top"
|
||||
} do %>
|
||||
<%= icon "trash-2", class: "w-5 h-5 text-destructive" %>
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<p class="text-sm text-primary text-center py-4 mb-1 font-medium">No exports yet.</p>
|
||||
<p class="text-sm text-primary text-center py-4 mb-1 font-medium"><%= t("family_exports.index.no_exports") %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1 +1,85 @@
|
||||
<%= render "list", exports: @exports %>
|
||||
<%= settings_section title: t(".title") do %>
|
||||
<div class="space-y-4">
|
||||
<% if @exports.empty? %>
|
||||
<p class="text-sm text-primary text-center py-4 mb-1 font-medium"><%= t(".no_exports") %></p>
|
||||
<% else %>
|
||||
<div class="bg-container rounded-lg shadow-border-xs">
|
||||
<% @exports.each do |export| %>
|
||||
<div class="flex items-center justify-between mx-4 py-4">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-primary"><%= t("family_exports.list.export_from", date: l(export.created_at, format: :long)) %></p>
|
||||
<p class="text-xs text-secondary"><%= export.filename %></p>
|
||||
</div>
|
||||
|
||||
<% if export.processing? || export.pending? %>
|
||||
<span class="px-1 py text-xs rounded-full bg-gray-500/5 text-secondary border border-alpha-black-50">
|
||||
<%= t("family_exports.list.in_progress") %>
|
||||
</span>
|
||||
<% elsif export.completed? %>
|
||||
<span class="px-1 py text-xs rounded-full bg-green-500/5 text-green-500 border border-alpha-black-50">
|
||||
<%= t("family_exports.list.complete") %>
|
||||
</span>
|
||||
<% elsif export.failed? %>
|
||||
<span class="px-1 py text-xs rounded-full bg-red-500/5 text-red-500 border border-alpha-black-50">
|
||||
<%= t("family_exports.list.failed") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if export.processing? || export.pending? %>
|
||||
<div class="flex items-center gap-2 text-secondary">
|
||||
<div class="animate-spin h-4 w-4 border-2 border-secondary border-t-transparent rounded-full"></div>
|
||||
<span class="text-sm"><%= t("family_exports.exporting") %></span>
|
||||
</div>
|
||||
<% elsif export.completed? %>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= button_to family_export_path(export),
|
||||
method: :delete,
|
||||
class: "flex items-center gap-2 text-destructive hover:text-destructive-hover",
|
||||
data: {
|
||||
turbo_confirm: t("family_exports.delete_confirmation")
|
||||
} do %>
|
||||
<%= icon "trash-2", class: "w-5 h-5 text-destructive" %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to download_family_export_path(export),
|
||||
class: "flex items-center gap-2 text-primary hover:text-primary-hover" do %>
|
||||
<%= icon "download", class: "w-5 h-5" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% elsif export.failed? %>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 text-destructive">
|
||||
<%= icon "alert-circle", class: "w-4 h-4" %>
|
||||
</div>
|
||||
|
||||
<%= button_to family_export_path(export),
|
||||
method: :delete,
|
||||
class: "flex items-center gap-2 text-destructive hover:text-destructive-hover",
|
||||
data: {
|
||||
turbo_confirm: t("family_exports.delete_failed_confirmation")
|
||||
} do %>
|
||||
<%= icon "trash-2", class: "w-5 h-5 text-destructive" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @pagy.pages > 1 %>
|
||||
<div class="mt-4">
|
||||
<%= render "shared/pagination", pagy: @pagy %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to new_family_export_path,
|
||||
class: "bg-container-inset flex items-center justify-center gap-2 text-secondary mt-1 hover:bg-container-inset-hover rounded-lg px-4 py-2 w-full text-center",
|
||||
data: { turbo_frame: :modal } do %>
|
||||
<%= icon("plus") %>
|
||||
<%= t(".new") %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
<%= settings_section title: t(".imports") do %>
|
||||
<%= settings_section title: t(".title") do %>
|
||||
<div class="space-y-4">
|
||||
<% if @imports.empty? %>
|
||||
<%= render partial: "imports/empty" %>
|
||||
<% else %>
|
||||
<div class="bg-container rounded-lg shadow-border-xs">
|
||||
<%= render partial: "imports/import", collection: @imports.ordered %>
|
||||
<%= render partial: "imports/import", collection: @imports %>
|
||||
</div>
|
||||
|
||||
<% if @pagy.pages > 1 %>
|
||||
<div class="mt-4">
|
||||
<%= render "shared/pagination", pagy: @pagy %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to new_import_path,
|
||||
@@ -16,24 +22,3 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if Current.user.admin? %>
|
||||
<%= settings_section title: t(".exports") do %>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-container rounded-lg shadow-border-xs">
|
||||
<%= turbo_frame_tag "family_exports", src: family_exports_path, loading: :lazy do %>
|
||||
<div class="mt-4 text-center text-secondary py-8">
|
||||
<div class="animate-spin inline-block h-4 w-4 border-2 border-secondary border-t-transparent rounded-full"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= link_to new_family_export_path,
|
||||
class: "bg-container-inset flex items-center justify-center gap-2 text-secondary mt-1 hover:bg-container-inset-hover rounded-lg px-4 py-2 w-full text-center",
|
||||
data: { turbo_frame: :modal } do %>
|
||||
<%= icon("plus") %>
|
||||
<%= t(".new_export") %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -31,6 +31,7 @@ nav_sections = [
|
||||
{ label: t(".self_hosting_label"), path: settings_hosting_path, icon: "database", if: self_hosted? },
|
||||
{ label: "Providers", path: settings_providers_path, icon: "plug" },
|
||||
{ label: t(".imports_label"), path: imports_path, icon: "download" },
|
||||
{ label: t(".exports_label"), path: family_exports_path, icon: "upload" },
|
||||
{ label: "SSO Providers", path: admin_sso_providers_path, icon: "key-round", if: Current.user&.super_admin? },
|
||||
{ label: "Users", path: admin_users_path, icon: "users", if: Current.user&.super_admin? }
|
||||
]
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<%= select_tag :per_page,
|
||||
options_for_select(["10", "20", "30", "50"], pagy.limit),
|
||||
options_for_select(["10", "20", "30", "50", "100"], pagy.limit),
|
||||
data: { controller: "selectable-link" },
|
||||
class: "py-1.5 pr-8 text-sm text-primary font-medium bg-container-inset border border-secondary rounded-lg focus:border-secondary focus:ring-secondary focus-visible:ring-secondary" %>
|
||||
</div>
|
||||
|
||||
6
config/locales/breadcrumbs/ca.yml
Normal file
6
config/locales/breadcrumbs/ca.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
ca:
|
||||
breadcrumbs:
|
||||
exports: Exportacions
|
||||
home: Inici
|
||||
imports: Importacions
|
||||
6
config/locales/breadcrumbs/de.yml
Normal file
6
config/locales/breadcrumbs/de.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
de:
|
||||
breadcrumbs:
|
||||
exports: Exporte
|
||||
home: Startseite
|
||||
imports: Importe
|
||||
6
config/locales/breadcrumbs/en.yml
Normal file
6
config/locales/breadcrumbs/en.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
en:
|
||||
breadcrumbs:
|
||||
exports: Exports
|
||||
home: Home
|
||||
imports: Imports
|
||||
6
config/locales/breadcrumbs/es.yml
Normal file
6
config/locales/breadcrumbs/es.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
es:
|
||||
breadcrumbs:
|
||||
exports: Exportaciones
|
||||
home: Inicio
|
||||
imports: Importaciones
|
||||
6
config/locales/breadcrumbs/nb.yml
Normal file
6
config/locales/breadcrumbs/nb.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
nb:
|
||||
breadcrumbs:
|
||||
exports: Eksporter
|
||||
home: Hjem
|
||||
imports: Importer
|
||||
6
config/locales/breadcrumbs/pt-BR.yml
Normal file
6
config/locales/breadcrumbs/pt-BR.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
pt-BR:
|
||||
breadcrumbs:
|
||||
exports: Exportações
|
||||
home: Início
|
||||
imports: Importações
|
||||
6
config/locales/breadcrumbs/ro.yml
Normal file
6
config/locales/breadcrumbs/ro.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
ro:
|
||||
breadcrumbs:
|
||||
exports: Exporturi
|
||||
home: Acasă
|
||||
imports: Importuri
|
||||
6
config/locales/breadcrumbs/tr.yml
Normal file
6
config/locales/breadcrumbs/tr.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
tr:
|
||||
breadcrumbs:
|
||||
exports: Dışa Aktarmalar
|
||||
home: Ana Sayfa
|
||||
imports: İçe Aktarmalar
|
||||
6
config/locales/breadcrumbs/zh-CN.yml
Normal file
6
config/locales/breadcrumbs/zh-CN.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
zh-CN:
|
||||
breadcrumbs:
|
||||
exports: 导出
|
||||
home: 主页
|
||||
imports: 导入
|
||||
@@ -1,7 +1,22 @@
|
||||
---
|
||||
ca:
|
||||
family_exports:
|
||||
access_denied: Accés denegat
|
||||
create:
|
||||
success: Exportació iniciada. Podràs descarregar-la aviat.
|
||||
delete_confirmation: Estàs segur que vols eliminar aquesta exportació? Aquesta acció no es pot desfer.
|
||||
delete_failed_confirmation: Estàs segur que vols eliminar aquesta exportació fallida?
|
||||
destroy:
|
||||
success: Exportació eliminada amb èxit
|
||||
export_not_ready: L'exportació no està llesta per descarregar
|
||||
exporting: Exportant...
|
||||
index:
|
||||
title: Exportacions
|
||||
new: Nova exportació
|
||||
no_exports: Encara no hi ha exportacions.
|
||||
list:
|
||||
export_from: "Exportació del %{date}"
|
||||
in_progress: En curs
|
||||
complete: Completat
|
||||
failed: Error
|
||||
in_progress: En curs
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
de:
|
||||
family_exports:
|
||||
access_denied: Zugriff verweigert
|
||||
create:
|
||||
success: Export gestartet. Sie können ihn in Kürze herunterladen.
|
||||
delete_confirmation: Möchten Sie diesen Export wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.
|
||||
delete_failed_confirmation: Möchten Sie diesen fehlgeschlagenen Export wirklich löschen?
|
||||
destroy:
|
||||
success: Export erfolgreich gelöscht
|
||||
export_not_ready: Export noch nicht zum Download bereit
|
||||
exporting: Wird exportiert...
|
||||
index:
|
||||
title: Exporte
|
||||
new: Neuer Export
|
||||
no_exports: Noch keine Exporte vorhanden.
|
||||
list:
|
||||
export_from: "Export vom %{date}"
|
||||
in_progress: Wird ausgeführt
|
||||
complete: Abgeschlossen
|
||||
failed: Fehlgeschlagen
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
en:
|
||||
family_exports:
|
||||
access_denied: Access denied
|
||||
create:
|
||||
success: Export started. You'll be able to download it shortly.
|
||||
delete_confirmation: Are you sure you want to delete this export? This action cannot be undone.
|
||||
delete_failed_confirmation: Are you sure you want to delete this failed export?
|
||||
destroy:
|
||||
success: Export deleted successfully
|
||||
export_not_ready: Export not ready for download
|
||||
exporting: Exporting...
|
||||
index:
|
||||
title: Exports
|
||||
new: New Export
|
||||
no_exports: No exports yet.
|
||||
list:
|
||||
export_from: "Export from %{date}"
|
||||
in_progress: In progress
|
||||
complete: Complete
|
||||
failed: Failed
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
es:
|
||||
family_exports:
|
||||
access_denied: Acceso denegado
|
||||
create:
|
||||
success: Exportación iniciada. Podrás descargarla en breve.
|
||||
delete_confirmation: ¿Estás seguro de que quieres eliminar esta exportación? Esta acción no se puede deshacer.
|
||||
delete_failed_confirmation: ¿Estás seguro de que quieres eliminar esta exportación fallida?
|
||||
destroy:
|
||||
success: Exportación eliminada con éxito
|
||||
export_not_ready: La exportación no está lista para descargar
|
||||
exporting: Exportando...
|
||||
index:
|
||||
title: Exportaciones
|
||||
new: Nueva exportación
|
||||
no_exports: Aún no hay exportaciones.
|
||||
list:
|
||||
export_from: "Exportación del %{date}"
|
||||
in_progress: En progreso
|
||||
complete: Completo
|
||||
failed: Fallido
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
nb:
|
||||
family_exports:
|
||||
access_denied: Tilgang nektet
|
||||
create:
|
||||
success: Eksport startet. Du vil kunne laste den ned snart.
|
||||
delete_confirmation: Er du sikker på at du vil slette denne eksporten? Denne handlingen kan ikke angres.
|
||||
delete_failed_confirmation: Er du sikker på at du vil slette denne mislykkede eksporten?
|
||||
destroy:
|
||||
success: Eksport slettet
|
||||
export_not_ready: Eksport ikke klar for nedlasting
|
||||
exporting: Eksporterer...
|
||||
index:
|
||||
title: Eksporter
|
||||
new: Ny Eksport
|
||||
no_exports: Ingen eksporter ennå.
|
||||
list:
|
||||
export_from: "Eksport fra %{date}"
|
||||
in_progress: Pågår
|
||||
complete: Fullført
|
||||
failed: Mislykket
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
pt-BR:
|
||||
family_exports:
|
||||
access_denied: Acesso negado
|
||||
create:
|
||||
success: Exportação iniciada. Você poderá baixá-la em breve.
|
||||
delete_confirmation: Tem certeza de que deseja excluir esta exportação? Esta ação não pode ser desfeita.
|
||||
delete_failed_confirmation: Tem certeza de que deseja excluir esta exportação falhada?
|
||||
destroy:
|
||||
success: Exportação excluída com sucesso
|
||||
export_not_ready: Exportação não está pronta para download
|
||||
exporting: Exportando...
|
||||
index:
|
||||
title: Exportações
|
||||
new: Nova Exportação
|
||||
no_exports: Nenhuma exportação ainda.
|
||||
list:
|
||||
in_progress: Em andamento
|
||||
export_from: "Exportação de %{date}"
|
||||
in_progress: Em andamento
|
||||
complete: Concluído
|
||||
failed: Falhou
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
ro:
|
||||
family_exports:
|
||||
access_denied: Acces interzis
|
||||
create:
|
||||
success: Export început. Veți putea să-l descărcați în curând.
|
||||
delete_confirmation: Sigur doriți să ștergeți acest export? Această acțiune nu poate fi anulată.
|
||||
delete_failed_confirmation: Sigur doriți să ștergeți acest export eșuat?
|
||||
destroy:
|
||||
success: Export șters cu succes
|
||||
export_not_ready: Exportul nu este gata pentru descărcare
|
||||
exporting: Se exportă...
|
||||
index:
|
||||
title: Exporturi
|
||||
new: Export nou
|
||||
no_exports: Nu există încă exporturi.
|
||||
list:
|
||||
export_from: "Export din %{date}"
|
||||
in_progress: În curs de desfășurare
|
||||
complete: Finalizat
|
||||
failed: Eșuat
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
tr:
|
||||
family_exports:
|
||||
access_denied: Erişim reddedildi
|
||||
create:
|
||||
success: Dışa aktarma başladı. Kısa süre içinde indirebileceksiniz.
|
||||
delete_confirmation: Bu dışa aktarma işlemini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.
|
||||
delete_failed_confirmation: Bu başarısız dışa aktarma işlemini silmek istediğinizden emin misiniz?
|
||||
destroy:
|
||||
success: Dışa aktarma başarıyla silindi
|
||||
export_not_ready: Dışa aktarma henüz indirmeye hazır değil
|
||||
exporting: Dışa aktarılıyor...
|
||||
index:
|
||||
title: Dışa aktarmalar
|
||||
new: Yeni Dışa Aktarma
|
||||
no_exports: Henüz hiç dışa aktarma yok.
|
||||
list:
|
||||
export_from: "%{date} tarihli dışa aktarma"
|
||||
in_progress: Devam ediyor
|
||||
complete: Tamamlandı
|
||||
failed: Başarısız
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
zh-CN:
|
||||
family_exports:
|
||||
access_denied: 访问被拒绝
|
||||
create:
|
||||
success: 导出已开始。您很快就能下载它。
|
||||
delete_confirmation: 确定要删除此导出吗?此操作无法撤销。
|
||||
delete_failed_confirmation: 确定要删除此失败的导出吗?
|
||||
destroy:
|
||||
success: 导出已成功删除
|
||||
export_not_ready: 导出尚未准备好下载
|
||||
exporting: 正在导出...
|
||||
index:
|
||||
title: 导出
|
||||
new: 新建导出
|
||||
no_exports: 暂无导出记录
|
||||
list:
|
||||
export_from: "导出自 %{date}"
|
||||
complete: 已完成
|
||||
failed: 已失败
|
||||
in_progress: 进行中
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
zh-TW:
|
||||
family_exports:
|
||||
access_denied: 存取被拒絕
|
||||
create:
|
||||
success: 匯出已開始。您很快就能下載它。
|
||||
destroy:
|
||||
success: 匯出已成功刪除
|
||||
export_not_ready: 匯出尚未準備好下載
|
||||
list:
|
||||
complete: 已完成
|
||||
failed: 已失敗
|
||||
|
||||
@@ -84,12 +84,12 @@ ca:
|
||||
uploading: Processant files
|
||||
view: Veure
|
||||
index:
|
||||
title: Importacions
|
||||
new: Nova importació
|
||||
exports: Exportacions
|
||||
imports: Importacions
|
||||
new: Nova importació
|
||||
new_export: Nova exportació
|
||||
no_exports: Encara no hi ha exportacions.
|
||||
title: Importa/Exporta
|
||||
new:
|
||||
description: Pots importar manualment diversos tipus de dades via CSV o utilitzar
|
||||
una de les nostres plantilles d'importació com Mint.
|
||||
|
||||
@@ -58,12 +58,8 @@ de:
|
||||
uploading: Zeilen werden verarbeitet
|
||||
view: Anzeigen
|
||||
index:
|
||||
imports: Importe
|
||||
title: Importe
|
||||
new: Neuer Import
|
||||
title: Import/Export
|
||||
exports: Exporte
|
||||
new_export: Neuer Export
|
||||
no_exports: Noch keine Exporte vorhanden.
|
||||
new:
|
||||
description: Du kannst verschiedene Datentypen manuell über CSV importieren oder eine unserer Importvorlagen wie Mint verwenden.
|
||||
import_accounts: Konten importieren
|
||||
|
||||
@@ -83,12 +83,8 @@ en:
|
||||
uploading: Processing rows
|
||||
view: View
|
||||
index:
|
||||
imports: Imports
|
||||
title: Imports
|
||||
new: New Import
|
||||
title: Import/Export
|
||||
exports: Exports
|
||||
new_export: New Export
|
||||
no_exports: No exports yet.
|
||||
new:
|
||||
description: You can manually import various types of data via CSV or use one
|
||||
of our import templates like Mint.
|
||||
|
||||
@@ -62,12 +62,8 @@ es:
|
||||
uploading: Procesando filas
|
||||
view: Ver
|
||||
index:
|
||||
imports: Importaciones
|
||||
title: Importaciones
|
||||
new: Nueva importación
|
||||
title: Importar/Exportar
|
||||
exports: Exportaciones
|
||||
new_export: Nueva exportación
|
||||
no_exports: Aún no hay exportaciones.
|
||||
new:
|
||||
description: Puedes importar manualmente varios tipos de datos mediante CSV o usar una de nuestras plantillas de importación como Mint.
|
||||
import_accounts: Importar cuentas
|
||||
|
||||
@@ -71,12 +71,8 @@ nb:
|
||||
uploading: Behandler rader
|
||||
view: Vis
|
||||
index:
|
||||
imports: Importer
|
||||
new: Ny Import
|
||||
title: Importer
|
||||
exports: Eksporter
|
||||
new_export: Ny Eksport
|
||||
no_exports: Ingen eksporter ennå.
|
||||
new: Ny Import
|
||||
new:
|
||||
description: Du kan manuelt importere ulike typer data via CSV eller bruke en av
|
||||
våre importmaler som Mint.
|
||||
|
||||
@@ -76,12 +76,8 @@ pt-BR:
|
||||
uploading: Processando linhas
|
||||
view: Visualizar
|
||||
index:
|
||||
imports: Importações
|
||||
title: Importações
|
||||
new: Nova Importação
|
||||
title: Importar/Exportar
|
||||
exports: Exportações
|
||||
new_export: Nova Exportação
|
||||
no_exports: Nenhuma exportação ainda.
|
||||
new:
|
||||
description: Você pode importar manualmente vários tipos de dados via CSV ou usar um
|
||||
de nossos modelos de importação, como o do Mint.
|
||||
|
||||
@@ -58,12 +58,8 @@ ro:
|
||||
uploading: Se procesează rândurile
|
||||
view: Vezi
|
||||
index:
|
||||
imports: Importuri
|
||||
title: Importuri
|
||||
new: Import nou
|
||||
title: Import/Export
|
||||
exports: Exporturi
|
||||
new_export: Export nou
|
||||
no_exports: Nu există încă exporturi.
|
||||
new:
|
||||
description: Poți importa manual diverse tipuri de date prin CSV sau poți folosi unul dintre șabloanele noastre de import, cum ar fi Mint.
|
||||
import_accounts: Importă conturi
|
||||
|
||||
@@ -58,12 +58,8 @@ tr:
|
||||
uploading: Satırlar işleniyor
|
||||
view: Görüntüle
|
||||
index:
|
||||
imports: İçe aktarmalar
|
||||
new: Yeni İçe Aktarma
|
||||
title: İçe aktarmalar
|
||||
exports: Dışa aktarmalar
|
||||
new_export: Yeni Dışa Aktarma
|
||||
no_exports: Henüz hiç dışa aktarma yok.
|
||||
new: Yeni İçe Aktarma
|
||||
new:
|
||||
description: Farklı veri türlerini CSV ile manuel olarak içe aktarabilir veya Mint gibi içe aktarma şablonlarımızı kullanabilirsiniz.
|
||||
import_accounts: Hesapları içe aktar
|
||||
|
||||
@@ -66,12 +66,8 @@ zh-CN:
|
||||
uploading: 处理行数据中
|
||||
view: 查看
|
||||
index:
|
||||
exports: 导出记录
|
||||
imports: 导入记录
|
||||
title: 导入记录
|
||||
new: 新建导入
|
||||
new_export: 新建导出
|
||||
no_exports: 暂无导出记录
|
||||
title: 导入/导出管理
|
||||
new:
|
||||
description: 您可以通过 CSV 手动导入多种类型数据,或使用我们的导入模板(如 Mint 格式)。
|
||||
import_accounts: 导入账户
|
||||
|
||||
@@ -127,8 +127,9 @@ ca:
|
||||
categories_label: Categories
|
||||
feedback_label: Feedback
|
||||
general_section_title: General
|
||||
imports_label: Importacions
|
||||
exports_label: Exportacions
|
||||
guides_label: Guies
|
||||
imports_label: Importa/Exporta
|
||||
logout: Tanca la sessió
|
||||
merchants_label: Comerços
|
||||
other_section_title: Més
|
||||
|
||||
@@ -94,7 +94,8 @@ de:
|
||||
categories_label: Kategorien
|
||||
feedback_label: Feedback
|
||||
general_section_title: Allgemein
|
||||
imports_label: Import/Export
|
||||
imports_label: Importe
|
||||
exports_label: Exporte
|
||||
logout: Abmelden
|
||||
merchants_label: Händler
|
||||
guides_label: Anleitungen
|
||||
|
||||
@@ -115,7 +115,8 @@ en:
|
||||
categories_label: Categories
|
||||
feedback_label: Feedback
|
||||
general_section_title: General
|
||||
imports_label: Import/Export
|
||||
imports_label: Imports
|
||||
exports_label: Exports
|
||||
logout: Logout
|
||||
merchants_label: Merchants
|
||||
guides_label: Guides
|
||||
|
||||
@@ -95,7 +95,8 @@ es:
|
||||
categories_label: Categorías
|
||||
feedback_label: Comentarios
|
||||
general_section_title: General
|
||||
imports_label: Importar/Exportar
|
||||
imports_label: Importaciones
|
||||
exports_label: Exportaciones
|
||||
logout: Cerrar sesión
|
||||
merchants_label: Comerciantes
|
||||
guides_label: Guías
|
||||
|
||||
@@ -81,6 +81,7 @@ nb:
|
||||
feedback_label: Tilbakemelding
|
||||
general_section_title: Generelt
|
||||
imports_label: Importer
|
||||
exports_label: Eksporter
|
||||
logout: Logg ut
|
||||
merchants_label: Forhandlere
|
||||
other_section_title: Mer
|
||||
|
||||
@@ -91,6 +91,7 @@ pt-BR:
|
||||
feedback_label: Feedback
|
||||
general_section_title: Geral
|
||||
imports_label: Importações
|
||||
exports_label: Exportações
|
||||
logout: Sair
|
||||
merchants_label: Comerciantes
|
||||
other_section_title: Mais
|
||||
|
||||
@@ -98,7 +98,8 @@ ro:
|
||||
categories_label: Categorii
|
||||
feedback_label: Feedback
|
||||
general_section_title: General
|
||||
imports_label: Import/Export
|
||||
imports_label: Importuri
|
||||
exports_label: Exporturi
|
||||
logout: Logout
|
||||
merchants_label: Comercianți
|
||||
guides_label: Ghiduri
|
||||
|
||||
@@ -101,7 +101,8 @@ zh-CN:
|
||||
feedback_label: 意见反馈
|
||||
general_section_title: 通用设置
|
||||
guides_label: 使用指南
|
||||
imports_label: 导入/导出
|
||||
imports_label: 导入记录
|
||||
exports_label: 导出记录
|
||||
logout: 退出登录
|
||||
merchants_label: 商户管理
|
||||
other_section_title: 更多设置
|
||||
|
||||
@@ -33,7 +33,7 @@ class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
post family_exports_path
|
||||
end
|
||||
|
||||
assert_redirected_to imports_path
|
||||
assert_redirected_to family_exports_path
|
||||
assert_equal "Export started. You'll be able to download it shortly.", flash[:notice]
|
||||
|
||||
export = @family.family_exports.last
|
||||
@@ -67,7 +67,7 @@ class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
export = @family.family_exports.create!(status: "processing")
|
||||
|
||||
get download_family_export_path(export)
|
||||
assert_redirected_to imports_path
|
||||
assert_redirected_to family_exports_path
|
||||
assert_equal "Export not ready for download", flash[:alert]
|
||||
end
|
||||
|
||||
@@ -78,7 +78,7 @@ class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
delete family_export_path(export)
|
||||
end
|
||||
|
||||
assert_redirected_to imports_path
|
||||
assert_redirected_to family_exports_path
|
||||
assert_equal "Export deleted successfully", flash[:notice]
|
||||
end
|
||||
|
||||
@@ -95,7 +95,7 @@ class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
delete family_export_path(export)
|
||||
end
|
||||
|
||||
assert_redirected_to imports_path
|
||||
assert_redirected_to family_exports_path
|
||||
assert_equal "Export deleted successfully", flash[:notice]
|
||||
end
|
||||
|
||||
@@ -112,7 +112,7 @@ class FamilyExportsControllerTest < ActionDispatch::IntegrationTest
|
||||
delete family_export_path(export)
|
||||
end
|
||||
|
||||
assert_redirected_to imports_path
|
||||
assert_redirected_to family_exports_path
|
||||
assert_equal "Export deleted successfully", flash[:notice]
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user