mirror of
https://github.com/mirego/accent.git
synced 2024-09-11 18:58:14 +03:00
Refactor conflicts page to view all languages side-by-side
This commit is contained in:
parent
8bcdf3b1c9
commit
995c2a5859
@ -15,8 +15,8 @@ defmodule Accent.GraphQL.Paginated do
|
||||
@enforce_keys [:entries, :meta]
|
||||
defstruct entries: [], meta: %{}
|
||||
|
||||
def paginate(query, args) do
|
||||
Accent.Repo.paginate(query, page: args[:page], page_size: args[:page_size])
|
||||
def paginate(query, args, options \\ []) do
|
||||
Accent.Repo.paginate(query, page: args[:page], page_size: args[:page_size], options: options)
|
||||
end
|
||||
|
||||
def format(paginated_list) do
|
||||
|
@ -1,5 +1,8 @@
|
||||
defmodule Accent.GraphQL.Resolvers.Translation do
|
||||
@moduledoc false
|
||||
import Absinthe.Resolution.Helpers, only: [batch: 3]
|
||||
|
||||
alias Accent.Document
|
||||
alias Accent.GraphQL.Paginated
|
||||
alias Accent.Plugs.GraphQLContext
|
||||
alias Accent.Project
|
||||
@ -27,6 +30,59 @@ defmodule Accent.GraphQL.Resolvers.Translation do
|
||||
|> then(&{:ok, &1})
|
||||
end
|
||||
|
||||
def batch_translation_ids(grouped_translation, _args, _resolution) do
|
||||
ids = Enum.reject(grouped_translation.translation_ids, &is_nil/1)
|
||||
|
||||
batch(
|
||||
{__MODULE__, :from_translation_ids},
|
||||
ids,
|
||||
fn batch_results ->
|
||||
translations =
|
||||
Map.get(batch_results, {grouped_translation.key, grouped_translation.document_id}, [])
|
||||
|
||||
translations =
|
||||
Enum.sort(translations, fn a, b ->
|
||||
cond do
|
||||
a.revision.master == true -> true
|
||||
DateTime.compare(a.revision.inserted_at, b.revision.inserted_at) === :lt -> true
|
||||
true -> false
|
||||
end
|
||||
end)
|
||||
|
||||
{:ok, translations}
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def from_translation_ids(_, ids) do
|
||||
ids = Enum.map(List.flatten(ids), &Ecto.UUID.cast!(&1))
|
||||
|
||||
query =
|
||||
Query.from(translations in Translation,
|
||||
preload: [:revision],
|
||||
where: translations.id in ^ids
|
||||
)
|
||||
|
||||
query
|
||||
|> Repo.all()
|
||||
|> Enum.group_by(&{&1.key, &1.document_id})
|
||||
end
|
||||
|
||||
def batch_document(grouped_translation, _args, _resolution) do
|
||||
batch({__MODULE__, :from_document_id}, grouped_translation.document_id, fn batch_results ->
|
||||
[document | _] = Map.get(batch_results, grouped_translation.document_id)
|
||||
{:ok, document}
|
||||
end)
|
||||
end
|
||||
|
||||
def from_document_id(_, ids) do
|
||||
query = Query.from(documents in Document, where: documents.id in ^ids)
|
||||
|
||||
query
|
||||
|> Repo.all()
|
||||
|> Enum.group_by(& &1.id)
|
||||
end
|
||||
|
||||
@spec correct(Translation.t(), %{text: String.t()}, GraphQLContext.t()) :: translation_operation
|
||||
def correct(translation, %{text: text}, info) do
|
||||
%Context{}
|
||||
@ -124,12 +180,29 @@ defmodule Accent.GraphQL.Resolvers.Translation do
|
||||
translations =
|
||||
Translation
|
||||
|> list(args, project.id)
|
||||
|> TranslationScope.from_project(project.id)
|
||||
|> Paginated.paginate(args)
|
||||
|
||||
{:ok, Paginated.format(translations)}
|
||||
end
|
||||
|
||||
@spec list_grouped_project(Project.t(), map(), GraphQLContext.t()) :: {:ok, Paginated.t(Translation.t())}
|
||||
def list_grouped_project(project, args, _) do
|
||||
total_entries =
|
||||
Translation
|
||||
|> list_grouped_count(args, project.id)
|
||||
|> Repo.all()
|
||||
|> Enum.count()
|
||||
|
||||
translations =
|
||||
Translation
|
||||
|> list_grouped(args, project.id)
|
||||
|> Paginated.paginate(args, total_entries: total_entries)
|
||||
|
||||
revisions = grouped_related_revisions(Map.put(args, :project_id, project.id))
|
||||
|
||||
{:ok, Map.put(Paginated.format(translations), :revisions, revisions)}
|
||||
end
|
||||
|
||||
@spec related_translations(Translation.t(), map(), struct()) :: {:ok, [Translation.t()]}
|
||||
def related_translations(translation, _, _) do
|
||||
translations =
|
||||
@ -179,7 +252,112 @@ defmodule Accent.GraphQL.Resolvers.Translation do
|
||||
end
|
||||
end
|
||||
|
||||
defp grouped_related_revisions(args) do
|
||||
query_revision_ids =
|
||||
if Enum.empty?(args[:related_revisions]) do
|
||||
Query.from(
|
||||
revisions in Revision,
|
||||
where: revisions.project_id == ^args[:project_id],
|
||||
order_by: [asc: :inserted_at],
|
||||
limit: 2
|
||||
)
|
||||
else
|
||||
Query.from(
|
||||
revisions in Revision,
|
||||
where: revisions.id in ^args[:related_revisions],
|
||||
order_by: [asc: :inserted_at],
|
||||
limit: 2
|
||||
)
|
||||
end
|
||||
|
||||
Repo.all(query_revision_ids)
|
||||
end
|
||||
|
||||
defp grouped_related_query(schema, args, project_id) do
|
||||
revision_ids =
|
||||
if Enum.empty?(args[:related_revisions]) do
|
||||
Enum.map(grouped_related_revisions(Map.put(args, :project_id, project_id)), & &1.id)
|
||||
else
|
||||
args[:related_revisions]
|
||||
end
|
||||
|
||||
query =
|
||||
schema
|
||||
|> TranslationScope.from_version(args[:version])
|
||||
|> TranslationScope.from_project(project_id)
|
||||
|> TranslationScope.from_revisions(revision_ids)
|
||||
|> TranslationScope.active()
|
||||
|> TranslationScope.not_locked()
|
||||
|
||||
{query, revision_ids}
|
||||
end
|
||||
|
||||
defp list_grouped_count(schema, args, project_id) do
|
||||
query = list_base_query(schema, args, project_id)
|
||||
{related_query, revision_ids} = grouped_related_query(schema, args, project_id)
|
||||
|
||||
query =
|
||||
Query.from(
|
||||
translations in query,
|
||||
left_join: related_translations in subquery(related_query),
|
||||
as: :related_translations,
|
||||
on:
|
||||
related_translations.revision_id in ^revision_ids and
|
||||
related_translations.key == translations.key and
|
||||
related_translations.document_id == translations.document_id,
|
||||
distinct: [translations.key, translations.document_id],
|
||||
select: translations.key,
|
||||
group_by: [translations.key, translations.document_id]
|
||||
)
|
||||
|
||||
if args[:is_conflicted] do
|
||||
Query.from([related_translations: related_translations] in query,
|
||||
having: fragment("array_agg(distinct(?))", related_translations.conflicted) != [false]
|
||||
)
|
||||
else
|
||||
query
|
||||
end
|
||||
end
|
||||
|
||||
defp list_grouped(schema, args, project_id) do
|
||||
query = list_base_query(schema, args, project_id)
|
||||
{related_query, revision_ids} = grouped_related_query(schema, args, project_id)
|
||||
|
||||
query =
|
||||
Query.from(
|
||||
translations in query,
|
||||
left_join: related_translations in subquery(related_query),
|
||||
as: :related_translations,
|
||||
on:
|
||||
related_translations.revision_id in ^revision_ids and
|
||||
related_translations.key == translations.key and
|
||||
related_translations.document_id == translations.document_id,
|
||||
distinct: translations.key,
|
||||
select: %{
|
||||
key: translations.key,
|
||||
document_id: translations.document_id,
|
||||
translation_ids: fragment("array_agg(distinct(?))", related_translations.id)
|
||||
},
|
||||
group_by: [translations.key, translations.document_id]
|
||||
)
|
||||
|
||||
if args[:is_conflicted] do
|
||||
Query.from([related_translations: related_translations] in query,
|
||||
having: fragment("array_agg(distinct(?))", related_translations.conflicted) != [false]
|
||||
)
|
||||
else
|
||||
query
|
||||
end
|
||||
end
|
||||
|
||||
defp list(schema, args, project_id) do
|
||||
schema
|
||||
|> list_base_query(args, project_id)
|
||||
|> Query.distinct(true)
|
||||
|> Query.preload(:revision)
|
||||
end
|
||||
|
||||
defp list_base_query(schema, args, project_id) do
|
||||
schema
|
||||
|> TranslationScope.active()
|
||||
|> TranslationScope.not_locked()
|
||||
@ -193,7 +371,6 @@ defmodule Accent.GraphQL.Resolvers.Translation do
|
||||
|> TranslationScope.parse_empty(args[:is_text_empty])
|
||||
|> TranslationScope.parse_commented_on(args[:is_commented_on])
|
||||
|> TranslationScope.from_version(args[:version])
|
||||
|> Query.distinct(true)
|
||||
|> Query.preload(:revision)
|
||||
|> TranslationScope.from_project(project_id)
|
||||
end
|
||||
end
|
||||
|
@ -6,6 +6,8 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
import Accent.GraphQL.Helpers.Authorization
|
||||
import Accent.GraphQL.Helpers.Fields
|
||||
|
||||
alias Accent.GraphQL.Resolvers.Translation, as: TranslationResolver
|
||||
|
||||
object :projects do
|
||||
field(:meta, non_null(:pagination_meta))
|
||||
field(:entries, list_of(:project))
|
||||
@ -135,13 +137,14 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
resolve(project_authorize(:index_documents, &Accent.GraphQL.Resolvers.Document.list_project/3))
|
||||
end
|
||||
|
||||
field :translations, :translations do
|
||||
field :grouped_translations, :grouped_translations do
|
||||
arg(:page, :integer)
|
||||
arg(:page_size, :integer)
|
||||
arg(:order, :string)
|
||||
arg(:document, :id)
|
||||
arg(:version, :id)
|
||||
arg(:related_revisions, list_of(non_null(:id)))
|
||||
arg(:query, :string)
|
||||
arg(:is_translated, :boolean)
|
||||
arg(:is_conflicted, :boolean)
|
||||
arg(:is_text_empty, :boolean)
|
||||
arg(:is_text_not_empty, :boolean)
|
||||
@ -151,11 +154,28 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
resolve(
|
||||
project_authorize(
|
||||
:index_translations,
|
||||
&Accent.GraphQL.Resolvers.Translation.list_project/3
|
||||
&TranslationResolver.list_grouped_project/3
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
field :translations, :translations do
|
||||
arg(:page, :integer)
|
||||
arg(:page_size, :integer)
|
||||
arg(:order, :string)
|
||||
arg(:document, :id)
|
||||
arg(:version, :id)
|
||||
arg(:query, :string)
|
||||
arg(:is_translated, :boolean)
|
||||
arg(:is_conflicted, :boolean)
|
||||
arg(:is_text_empty, :boolean)
|
||||
arg(:is_text_not_empty, :boolean)
|
||||
arg(:is_added_last_sync, :boolean)
|
||||
arg(:is_commented_on, :boolean)
|
||||
|
||||
resolve(project_authorize(:index_translations, &TranslationResolver.list_project/3))
|
||||
end
|
||||
|
||||
field :activities, :activities do
|
||||
arg(:page, :integer)
|
||||
arg(:page_size, :integer)
|
||||
@ -182,7 +202,7 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
field :translation, :translation do
|
||||
arg(:id, non_null(:id))
|
||||
|
||||
resolve(project_authorize(:show_translation, &Accent.GraphQL.Resolvers.Translation.show_project/3))
|
||||
resolve(project_authorize(:show_translation, &TranslationResolver.show_project/3))
|
||||
end
|
||||
|
||||
field :activity, :activity do
|
||||
@ -200,6 +220,7 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
|
||||
field :revisions, list_of(:revision) do
|
||||
arg(:version_id, :id)
|
||||
|
||||
resolve(project_authorize(:index_revisions, &Accent.GraphQL.Resolvers.Revision.list_project/3))
|
||||
end
|
||||
|
||||
|
@ -18,6 +18,15 @@ defmodule Accent.GraphQL.Types.Translation do
|
||||
value(:float, as: "float")
|
||||
end
|
||||
|
||||
object :grouped_translation do
|
||||
field(:key, non_null(:string))
|
||||
field(:document, non_null(:document), resolve: &Accent.GraphQL.Resolvers.Translation.batch_document/3)
|
||||
|
||||
field(:translations, non_null(list_of(non_null(:translation))),
|
||||
resolve: &Accent.GraphQL.Resolvers.Translation.batch_translation_ids/3
|
||||
)
|
||||
end
|
||||
|
||||
object :translation do
|
||||
field(:id, non_null(:id))
|
||||
field(:key, non_null(:string), resolve: &Accent.GraphQL.Resolvers.Translation.key/3)
|
||||
@ -119,4 +128,10 @@ defmodule Accent.GraphQL.Types.Translation do
|
||||
field(:meta, :pagination_meta)
|
||||
field(:entries, list_of(:translation))
|
||||
end
|
||||
|
||||
object :grouped_translations do
|
||||
field(:meta, :pagination_meta)
|
||||
field(:entries, list_of(:grouped_translation))
|
||||
field(:revisions, non_null(list_of(non_null(:revision))))
|
||||
end
|
||||
end
|
||||
|
@ -34,6 +34,7 @@ module.exports = {
|
||||
'no-shadowed-elements': true,
|
||||
'no-trailing-spaces': true,
|
||||
'no-triple-curlies': false,
|
||||
'no-inline-styles': false,
|
||||
'no-unused-block-params': true,
|
||||
quotes: false,
|
||||
'require-valid-alt-text': false,
|
||||
@ -41,7 +42,7 @@ module.exports = {
|
||||
'require-button-type': false,
|
||||
'self-closing-void-elements': false,
|
||||
'simple-unless': false,
|
||||
'style-concatenation': true,
|
||||
'style-concatenation': false,
|
||||
'table-groups': true,
|
||||
'template-length': [true, {min: 1, max: 200}],
|
||||
}
|
||||
|
@ -14,12 +14,16 @@ interface Args {
|
||||
conflicts: any;
|
||||
document: any;
|
||||
documents: any;
|
||||
relatedRevisions: any;
|
||||
defaultRelatedRevisions: any[];
|
||||
revisions: any;
|
||||
version: any;
|
||||
versions: any;
|
||||
query: any;
|
||||
withAdvancedFilters: boolean;
|
||||
onChangeDocument: () => void;
|
||||
onChangeReference: () => void;
|
||||
onChangeVersion: () => void;
|
||||
onChangeRevisions: () => void;
|
||||
onChangeQuery: (query: string) => void;
|
||||
}
|
||||
|
||||
@ -30,9 +34,19 @@ export default class ConflictsFilters extends Component<Args> {
|
||||
@gt('args.documents.length', 1)
|
||||
showDocumentsSelect: boolean;
|
||||
|
||||
@gt('args.revisions.length', 1)
|
||||
showRevisionsSelect: boolean;
|
||||
|
||||
@gt('args.versions.length', 0)
|
||||
showVersionsSelect: boolean;
|
||||
|
||||
get showSomeFilters() {
|
||||
return this.showDocumentsSelect || this.showVersionsSelect;
|
||||
}
|
||||
|
||||
@tracked
|
||||
displayAdvancedFilters = this.args.withAdvancedFilters;
|
||||
|
||||
@tracked
|
||||
debouncedQuery = this.args.query;
|
||||
|
||||
@ -64,6 +78,29 @@ export default class ConflictsFilters extends Component<Args> {
|
||||
return documents;
|
||||
}
|
||||
|
||||
get relatedRevisionsValue() {
|
||||
if (this.args.relatedRevisions.length === 0) {
|
||||
const revisionIds = this.args.defaultRelatedRevisions.map(
|
||||
({id}: any) => id
|
||||
);
|
||||
return this.mappedRevisions.filter(({value}: {value: string}) =>
|
||||
revisionIds.includes(value)
|
||||
);
|
||||
}
|
||||
|
||||
return this.mappedRevisions.filter(({value}: {value: string}) =>
|
||||
this.args.relatedRevisions?.includes(value)
|
||||
);
|
||||
}
|
||||
|
||||
get mappedRevisionsOptions() {
|
||||
const values = this.relatedRevisionsValue.map(({value}: any) => value);
|
||||
|
||||
return this.mappedRevisions.filter(
|
||||
({value}: {value: string}) => !values.includes(value)
|
||||
);
|
||||
}
|
||||
|
||||
get documentValue() {
|
||||
return this.mappedDocuments.find(
|
||||
({value}: {value: string}) => value === this.args.document
|
||||
@ -88,6 +125,20 @@ export default class ConflictsFilters extends Component<Args> {
|
||||
return versions;
|
||||
}
|
||||
|
||||
get mappedRevisions() {
|
||||
return this.args.revisions.map(
|
||||
(revision: {
|
||||
id: string;
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
language: {slug: string; name: string};
|
||||
}) => ({
|
||||
label: revision.name || revision.language.name,
|
||||
value: revision.id,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get versionValue() {
|
||||
return this.mappedVersions.find(
|
||||
({value}: {value: string}) => value === this.args.version
|
||||
@ -101,6 +152,11 @@ export default class ConflictsFilters extends Component<Args> {
|
||||
this.debounceQuery.perform(target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleAdvancedFilters() {
|
||||
this.displayAdvancedFilters = !this.displayAdvancedFilters;
|
||||
}
|
||||
|
||||
@action
|
||||
submitForm(event: Event) {
|
||||
event.preventDefault();
|
||||
|
@ -0,0 +1,21 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
revisions: any[];
|
||||
isTextEmptyFilter: boolean;
|
||||
isTextNotEmptyFilter: boolean;
|
||||
isAddedLastSyncFilter: boolean;
|
||||
isNotTranslatedFilter: boolean;
|
||||
isCommentedOnFilter: boolean;
|
||||
onChangeAdvancedFilterBoolean: (
|
||||
key:
|
||||
| 'isTextEmpty'
|
||||
| 'isTextNotEmpty'
|
||||
| 'isAddedLastSync'
|
||||
| 'isCommentedOn'
|
||||
| 'isNotTranslated',
|
||||
event: InputEvent
|
||||
) => void;
|
||||
}
|
||||
|
||||
export default class AdvancedFilters extends Component<Args> {}
|
@ -1,9 +1,11 @@
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
import {action} from '@ember/object';
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
permissions: Record<string, true>;
|
||||
project: any;
|
||||
conflicts: any;
|
||||
groupedTranslations: any;
|
||||
version: any;
|
||||
versions: any[];
|
||||
query: any;
|
||||
@ -15,7 +17,10 @@ interface Args {
|
||||
) => void;
|
||||
}
|
||||
|
||||
export default class ConflictsItems extends Component<Args> {
|
||||
export default class ConflictsList extends Component<Args> {
|
||||
@tracked
|
||||
selectedTranslationId: string | null = null;
|
||||
|
||||
get currentVersion() {
|
||||
if (!this.args.versions) return;
|
||||
if (!this.args.version) return;
|
||||
@ -24,4 +29,34 @@ export default class ConflictsItems extends Component<Args> {
|
||||
(version) => version.id === this.args.version
|
||||
);
|
||||
}
|
||||
|
||||
get revisions() {
|
||||
if (this.args.groupedTranslations.length === 0) return [];
|
||||
|
||||
return this.args.groupedTranslations[0].translations.map(
|
||||
({revision}: any) => revision
|
||||
);
|
||||
}
|
||||
|
||||
get mappedRevisions() {
|
||||
if (this.args.groupedTranslations.length === 0) return [];
|
||||
|
||||
return this.args.groupedTranslations[0].translations.map(
|
||||
({revision}: any) => {
|
||||
return {
|
||||
name: revision.name || revision.language.name,
|
||||
slug: revision.slug || revision.language.slug,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get showRevisionsHeader() {
|
||||
return this.revisions.length > 1;
|
||||
}
|
||||
|
||||
@action
|
||||
handleFocus(id: string) {
|
||||
this.selectedTranslationId = id;
|
||||
}
|
||||
}
|
||||
|
29
webapp/app/components/conflicts-list/group/component.ts
Normal file
29
webapp/app/components/conflicts-list/group/component.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {action} from '@ember/object';
|
||||
import Component from '@glimmer/component';
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
|
||||
interface Args {
|
||||
selectedTranslationId: string | null;
|
||||
groupedTranslation: {
|
||||
key: string;
|
||||
translations: any[];
|
||||
};
|
||||
onFocus: (id: string) => void;
|
||||
}
|
||||
|
||||
export default class ConflictsListGroup extends Component<Args> {
|
||||
translationKey = parsedKeyProperty(this.args.groupedTranslation.key);
|
||||
|
||||
get masterTranslation() {
|
||||
return this.args.groupedTranslation.translations[0];
|
||||
}
|
||||
|
||||
get isFocused() {
|
||||
return this.masterTranslation.id === this.args.selectedTranslationId;
|
||||
}
|
||||
|
||||
@action
|
||||
handleFocus() {
|
||||
this.args.onFocus(this.masterTranslation.id);
|
||||
}
|
||||
}
|
@ -2,15 +2,15 @@ import {action} from '@ember/object';
|
||||
import {empty} from '@ember/object/computed';
|
||||
import Component from '@glimmer/component';
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
import {dropTask} from 'ember-concurrency';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
import {MutationResponse} from 'accent-webapp/services/apollo-mutate';
|
||||
|
||||
interface Conflict {
|
||||
interface Translation {
|
||||
id: string;
|
||||
key: string;
|
||||
conflictedText: string;
|
||||
correctedText: string;
|
||||
isConflicted: boolean;
|
||||
revision: {
|
||||
name: string | null;
|
||||
slug: string | null;
|
||||
@ -22,13 +22,6 @@ interface Conflict {
|
||||
rtl: boolean;
|
||||
};
|
||||
};
|
||||
relatedTranslations: Array<{
|
||||
id: string;
|
||||
correctedText: string;
|
||||
revision: {
|
||||
isMaster: boolean;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
interface Args {
|
||||
@ -36,79 +29,53 @@ interface Args {
|
||||
index: number;
|
||||
project: any;
|
||||
prompts: any[];
|
||||
conflict: Conflict;
|
||||
onCorrect: (conflict: any, textInput: string) => Promise<MutationResponse>;
|
||||
onCopyTranslation: (
|
||||
text: string,
|
||||
sourceLanguageSlug: string | null,
|
||||
targetLanguageSlug: string
|
||||
) => Promise<{text: string | null}>;
|
||||
translation: Translation;
|
||||
onFocus: () => void;
|
||||
onBlur: () => void;
|
||||
onCorrect: (translation: any, textInput: string) => Promise<MutationResponse>;
|
||||
onUpdate: (translation: any, textInput: string) => Promise<MutationResponse>;
|
||||
onUncorrect: (
|
||||
translation: any,
|
||||
textInput: string
|
||||
) => Promise<MutationResponse>;
|
||||
}
|
||||
|
||||
export default class ConflictItem extends Component<Args> {
|
||||
@empty('args.conflict.conflictedText')
|
||||
export default class ConflictsListItem extends Component<Args> {
|
||||
@empty('args.translation.conflictedText')
|
||||
emptyPreviousText: boolean;
|
||||
|
||||
@tracked
|
||||
textInput = this.args.conflict.correctedText;
|
||||
textInput = this.args.translation.correctedText;
|
||||
|
||||
@tracked
|
||||
loading = false;
|
||||
conflictResolved = false;
|
||||
|
||||
@tracked
|
||||
isCorrectLoading = false;
|
||||
|
||||
@tracked
|
||||
isUncorrectLoading = false;
|
||||
|
||||
@tracked
|
||||
isUpdateLoading = false;
|
||||
|
||||
@tracked
|
||||
error = false;
|
||||
|
||||
@tracked
|
||||
resolved = false;
|
||||
|
||||
@tracked
|
||||
inputDisabled = false;
|
||||
|
||||
conflictKey = parsedKeyProperty(this.args.conflict.key);
|
||||
textOriginal = this.args.conflict.correctedText;
|
||||
|
||||
get relatedTranslations() {
|
||||
const masterConflict = this.args.conflict.relatedTranslations.find(
|
||||
(translation) => translation.revision.isMaster
|
||||
);
|
||||
if (!masterConflict) return [];
|
||||
|
||||
return this.args.conflict.relatedTranslations.filter((translation) => {
|
||||
return (
|
||||
translation.id === masterConflict.id ||
|
||||
translation.correctedText !== masterConflict.correctedText
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
get showTextDiff() {
|
||||
if (!this.args.conflict.conflictedText) return false;
|
||||
|
||||
return this.textInput !== this.args.conflict.conflictedText;
|
||||
}
|
||||
translationKey = parsedKeyProperty(this.args.translation.key);
|
||||
textOriginal = this.args.translation.correctedText;
|
||||
|
||||
get showOriginalButton() {
|
||||
return this.textInput !== this.textOriginal;
|
||||
}
|
||||
|
||||
get revisionName() {
|
||||
return (
|
||||
this.args.conflict.revision.name ||
|
||||
this.args.conflict.revision.language.name
|
||||
);
|
||||
}
|
||||
|
||||
get revisionSlug() {
|
||||
return (
|
||||
this.args.conflict.revision.slug ||
|
||||
this.args.conflict.revision.language.slug
|
||||
);
|
||||
}
|
||||
|
||||
get revisionTextDirRtl() {
|
||||
return this.args.conflict.revision.rtl !== null
|
||||
? this.args.conflict.revision.rtl
|
||||
: this.args.conflict.revision.language.rtl;
|
||||
return this.args.translation.revision.rtl !== null
|
||||
? this.args.translation.revision.rtl
|
||||
: this.args.translation.revision.language.rtl;
|
||||
}
|
||||
|
||||
@action
|
||||
@ -132,30 +99,12 @@ export default class ConflictItem extends Component<Args> {
|
||||
this.inputDisabled = false;
|
||||
}
|
||||
|
||||
copyTranslationTask = dropTask(
|
||||
async (text: string, sourceLanguageSlug: string) => {
|
||||
this.inputDisabled = true;
|
||||
|
||||
const copyTranslation = await this.args.onCopyTranslation(
|
||||
text,
|
||||
sourceLanguageSlug,
|
||||
this.revisionSlug
|
||||
);
|
||||
|
||||
this.inputDisabled = false;
|
||||
|
||||
if (copyTranslation.text) {
|
||||
this.textInput = copyTranslation.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@action
|
||||
async correct() {
|
||||
this.onLoading();
|
||||
async correctConflict() {
|
||||
this.onCorrectLoading();
|
||||
|
||||
const response = await this.args.onCorrect(
|
||||
this.args.conflict,
|
||||
this.args.translation,
|
||||
this.textInput
|
||||
);
|
||||
|
||||
@ -166,18 +115,71 @@ export default class ConflictItem extends Component<Args> {
|
||||
}
|
||||
}
|
||||
|
||||
private onLoading() {
|
||||
@action
|
||||
async uncorrectConflict() {
|
||||
this.onUncorrectLoading();
|
||||
|
||||
const response = await this.args.onUncorrect(
|
||||
this.args.translation,
|
||||
this.textInput
|
||||
);
|
||||
|
||||
if (response.errors) {
|
||||
this.onError();
|
||||
} else {
|
||||
this.onUncorrectSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async updateConflict() {
|
||||
this.onUpdateLoading();
|
||||
|
||||
const response = await this.args.onUpdate(
|
||||
this.args.translation,
|
||||
this.textInput
|
||||
);
|
||||
|
||||
if (response.errors) {
|
||||
this.onError();
|
||||
} else {
|
||||
this.onUpdateSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
private onCorrectLoading() {
|
||||
this.error = false;
|
||||
this.loading = true;
|
||||
this.isCorrectLoading = true;
|
||||
}
|
||||
|
||||
private onUncorrectLoading() {
|
||||
this.error = false;
|
||||
this.isUncorrectLoading = true;
|
||||
}
|
||||
|
||||
private onUpdateLoading() {
|
||||
this.error = false;
|
||||
this.isUpdateLoading = true;
|
||||
}
|
||||
|
||||
private onError() {
|
||||
this.error = true;
|
||||
this.loading = false;
|
||||
this.isUpdateLoading = false;
|
||||
this.isCorrectLoading = false;
|
||||
this.isUncorrectLoading = false;
|
||||
}
|
||||
|
||||
private onCorrectSuccess() {
|
||||
this.resolved = true;
|
||||
this.loading = false;
|
||||
this.conflictResolved = true;
|
||||
this.isCorrectLoading = false;
|
||||
}
|
||||
|
||||
private onUncorrectSuccess() {
|
||||
this.conflictResolved = false;
|
||||
this.isCorrectLoading = false;
|
||||
}
|
||||
|
||||
private onUpdateSuccess() {
|
||||
this.isUpdateLoading = false;
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
interface Args {
|
||||
project: any;
|
||||
translation: any;
|
||||
onCopyTranslation: (text: string, languageSlug: string) => void;
|
||||
}
|
||||
|
||||
const MAX_TEXT_LENGTH = 600;
|
||||
|
||||
export default class ConflictItemRelatedTranslation extends Component<Args> {
|
||||
get text() {
|
||||
const text = this.args.translation.correctedText;
|
||||
|
||||
if (text.length < MAX_TEXT_LENGTH) return text;
|
||||
|
||||
return `${text.substring(0, MAX_TEXT_LENGTH - 1)}…`;
|
||||
}
|
||||
|
||||
get revisionName() {
|
||||
return (
|
||||
this.args.translation.revision.slug ||
|
||||
this.args.translation.revision.language.slug
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
translate() {
|
||||
this.args.onCopyTranslation(
|
||||
this.args.translation.correctedText,
|
||||
this.revisionSlug
|
||||
);
|
||||
}
|
||||
|
||||
private get revisionSlug() {
|
||||
return (
|
||||
this.args.translation.revision.slug ||
|
||||
this.args.translation.revision.language.slug
|
||||
);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
project: any;
|
||||
revision: any;
|
||||
translations: any;
|
||||
isLoading: boolean;
|
||||
showLoading: boolean;
|
||||
document: any;
|
||||
version: any;
|
||||
permissions: Record<string, true>;
|
||||
documents: any;
|
||||
versions: any;
|
||||
showSkeleton: boolean;
|
||||
query: any;
|
||||
reference: any;
|
||||
referenceRevision: any;
|
||||
referenceRevisions: any;
|
||||
onCorrect: () => void;
|
||||
onCopyTranslation: () => void;
|
||||
onCorrectAll: () => void;
|
||||
onSelectPage: () => void;
|
||||
onChangeDocument: () => void;
|
||||
onChangeVersion: () => void;
|
||||
onChangeReference: () => void;
|
||||
onChangeQuery: () => void;
|
||||
}
|
||||
|
||||
export default class ConflictsPage extends Component<Args> {}
|
@ -1,6 +1,5 @@
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {gt} from '@ember/object/computed';
|
||||
import Component from '@glimmer/component';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
import GlobalState from 'accent-webapp/services/global-state';
|
||||
@ -28,11 +27,17 @@ export default class RevisionExportOptions extends Component<Args> {
|
||||
@service('global-state')
|
||||
globalState: GlobalState;
|
||||
|
||||
@gt('mappedDocuments.length', 1)
|
||||
showDocuments: boolean;
|
||||
get showDocuments() {
|
||||
return this.mappedDocuments.length > 1;
|
||||
}
|
||||
|
||||
@gt('mappedVersions.length', 1)
|
||||
showVersions: boolean;
|
||||
get showVersions() {
|
||||
return this.mappedVersions.length > 1;
|
||||
}
|
||||
|
||||
get showSomeFilters() {
|
||||
return this.showDocuments || this.showVersions;
|
||||
}
|
||||
|
||||
@tracked
|
||||
debouncedQuery = this.args.query;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import {equal} from '@ember/object/computed';
|
||||
import {next} from '@ember/runloop';
|
||||
import {action} from '@ember/object';
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
@ -105,6 +106,21 @@ export default class TranslationEditForm extends Component<Args> {
|
||||
this.args.onKeyUp?.(value);
|
||||
}
|
||||
|
||||
@action
|
||||
handleFocus() {
|
||||
this.args.onFocus?.();
|
||||
}
|
||||
|
||||
@action
|
||||
handleBlur() {
|
||||
next(this, () => this.args.onBlur?.());
|
||||
}
|
||||
|
||||
@action
|
||||
handleSubmit() {
|
||||
this.args.onSubmit();
|
||||
}
|
||||
|
||||
@action
|
||||
changeText(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
212
webapp/app/controllers/logged-in/project/conflicts.ts
Normal file
212
webapp/app/controllers/logged-in/project/conflicts.ts
Normal file
@ -0,0 +1,212 @@
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {readOnly, equal, and} from '@ember/object/computed';
|
||||
import Controller from '@ember/controller';
|
||||
import translationCorrectQuery from 'accent-webapp/queries/correct-translation';
|
||||
import translationUncorrectQuery from 'accent-webapp/queries/uncorrect-translation';
|
||||
import translationUpdateQuery from 'accent-webapp/queries/update-translation';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
import FlashMessages from 'ember-cli-flash/services/flash-messages';
|
||||
import Apollo from 'accent-webapp/services/apollo';
|
||||
import ApolloMutate from 'accent-webapp/services/apollo-mutate';
|
||||
import GlobalState from 'accent-webapp/services/global-state';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
const FLASH_MESSAGE_CORRECT_SUCCESS =
|
||||
'pods.project.conflicts.flash_messages.correct_success';
|
||||
const FLASH_MESSAGE_CORRECT_ERROR =
|
||||
'pods.project.conflicts.flash_messages.correct_error';
|
||||
const FLASH_MESSAGE_UNCORRECT_SUCCESS =
|
||||
'pods.project.conflicts.flash_messages.uncorrect_success';
|
||||
const FLASH_MESSAGE_UNCORRECT_ERROR =
|
||||
'pods.project.conflicts.flash_messages.uncorrect_error';
|
||||
const FLASH_MESSAGE_UPDATE_SUCCESS =
|
||||
'pods.project.conflicts.flash_messages.update_success';
|
||||
const FLASH_MESSAGE_UPDATE_ERROR =
|
||||
'pods.project.conflicts.flash_messages.update_error';
|
||||
|
||||
export default class ConflictsController extends Controller {
|
||||
@tracked
|
||||
model: any;
|
||||
|
||||
@service('intl')
|
||||
intl: IntlService;
|
||||
|
||||
@service('flash-messages')
|
||||
flashMessages: FlashMessages;
|
||||
|
||||
@service('apollo')
|
||||
apollo: Apollo;
|
||||
|
||||
@service('apollo-mutate')
|
||||
apolloMutate: ApolloMutate;
|
||||
|
||||
@service('global-state')
|
||||
globalState: GlobalState;
|
||||
|
||||
queryParams = [
|
||||
'page',
|
||||
'query',
|
||||
'document',
|
||||
'version',
|
||||
'relatedRevisions',
|
||||
'isTextEmpty',
|
||||
'isTextNotEmpty',
|
||||
'isAddedLastSync',
|
||||
'isCommentedOn',
|
||||
'isTranslatedFilter',
|
||||
];
|
||||
|
||||
@tracked
|
||||
query = '';
|
||||
|
||||
@tracked
|
||||
document: string | null = null;
|
||||
|
||||
@tracked
|
||||
version: string | null = null;
|
||||
|
||||
@tracked
|
||||
relatedRevisions: string[] = [];
|
||||
|
||||
@tracked
|
||||
isTextEmpty: 'true' | null = null;
|
||||
|
||||
@tracked
|
||||
isTextNotEmpty: 'true' | null = null;
|
||||
|
||||
@tracked
|
||||
isAddedLastSync: 'true' | null = null;
|
||||
|
||||
@tracked
|
||||
isTranslated: 'true' | null = null;
|
||||
|
||||
@tracked
|
||||
isCommentedOn: 'true' | null = null;
|
||||
|
||||
@tracked
|
||||
page = 1;
|
||||
|
||||
@readOnly('globalState.permissions')
|
||||
permissions: any;
|
||||
|
||||
@equal('model.groupedTranslations.entries', undefined)
|
||||
emptyEntries: boolean;
|
||||
|
||||
@and('emptyEntries', 'model.loading')
|
||||
showLoading: boolean;
|
||||
|
||||
@and('emptyEntries', 'model.loading')
|
||||
showSkeleton: boolean;
|
||||
|
||||
get withAdvancedFilters() {
|
||||
return [
|
||||
this.isTextEmpty,
|
||||
this.isTextNotEmpty,
|
||||
this.isAddedLastSync,
|
||||
this.isCommentedOn,
|
||||
this.isTranslated,
|
||||
].filter((filter) => filter === 'true').length;
|
||||
}
|
||||
|
||||
@action
|
||||
changeAdvancedFilterBoolean(
|
||||
key:
|
||||
| 'isTextEmpty'
|
||||
| 'isTextNotEmpty'
|
||||
| 'isAddedLastSync'
|
||||
| 'isCommentedOn'
|
||||
| 'isTranslated',
|
||||
event: InputEvent
|
||||
) {
|
||||
this[key] = (event.target as HTMLInputElement).checked ? 'true' : null;
|
||||
}
|
||||
|
||||
@action
|
||||
async correctConflict(conflict: any, text: string) {
|
||||
const response = await this.apolloMutate.mutate({
|
||||
mutation: translationCorrectQuery,
|
||||
variables: {
|
||||
translationId: conflict.id,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.errors) {
|
||||
this.flashMessages.error(this.intl.t(FLASH_MESSAGE_CORRECT_ERROR));
|
||||
} else {
|
||||
this.flashMessages.success(this.intl.t(FLASH_MESSAGE_CORRECT_SUCCESS));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@action
|
||||
async uncorrectConflict(conflict: any, text: string) {
|
||||
const response = await this.apolloMutate.mutate({
|
||||
mutation: translationUncorrectQuery,
|
||||
variables: {
|
||||
translationId: conflict.id,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.errors) {
|
||||
this.flashMessages.error(this.intl.t(FLASH_MESSAGE_UNCORRECT_ERROR));
|
||||
} else {
|
||||
this.flashMessages.success(this.intl.t(FLASH_MESSAGE_UNCORRECT_SUCCESS));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@action
|
||||
async updateConflict(conflict: any, text: string) {
|
||||
const response = await this.apolloMutate.mutate({
|
||||
mutation: translationUpdateQuery,
|
||||
variables: {
|
||||
translationId: conflict.id,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.errors) {
|
||||
this.flashMessages.error(this.intl.t(FLASH_MESSAGE_UPDATE_ERROR));
|
||||
} else {
|
||||
this.flashMessages.success(this.intl.t(FLASH_MESSAGE_UPDATE_SUCCESS));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@action
|
||||
changeQuery(query: string) {
|
||||
this.page = 1;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@action
|
||||
changeDocument(select: HTMLSelectElement) {
|
||||
this.page = 1;
|
||||
this.document = select.value ? select.value : null;
|
||||
}
|
||||
|
||||
@action
|
||||
changeVersion(select: HTMLSelectElement) {
|
||||
this.page = 1;
|
||||
this.version = select.value ? select.value : null;
|
||||
}
|
||||
|
||||
@action
|
||||
changeRelatedRevisions(choices: Array<{value: string}>) {
|
||||
this.page = 1;
|
||||
this.relatedRevisions = choices.map(({value}) => value);
|
||||
}
|
||||
|
||||
@action
|
||||
selectPage(page: number) {
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
this.page = page;
|
||||
}
|
||||
}
|
@ -12,9 +12,7 @@ import promptConfigSaveQuery, {
|
||||
} from 'accent-webapp/queries/save-project-prompt-config';
|
||||
import promptConfigDeleteQuery from 'accent-webapp/queries/delete-project-prompt-config';
|
||||
import promptDeleteQuery from 'accent-webapp/queries/delete-project-prompt';
|
||||
import projectPromptConfigQuery, {
|
||||
ProjectPromptConfigResponse,
|
||||
} from 'accent-webapp/queries/project-prompt-config';
|
||||
import projectPromptConfigQuery from 'accent-webapp/queries/project-prompt-config';
|
||||
import {InMemoryCache} from '@apollo/client/cache';
|
||||
|
||||
const FLASH_MESSAGE_PREFIX = 'pods.project.edit.flash_messages.';
|
||||
@ -125,21 +123,25 @@ export default class PromptsController extends Controller {
|
||||
variables,
|
||||
refetchQueries: ['Project'],
|
||||
update: (cache: InMemoryCache) => {
|
||||
const data = cache.readQuery({
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
}) as ProjectPromptConfigResponse;
|
||||
|
||||
const prompts = data.viewer.project.prompts.filter(
|
||||
(prompt) => prompt.id !== variables.promptId
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
},
|
||||
(data) => {
|
||||
return {
|
||||
viewer: {
|
||||
...data.viewer,
|
||||
project: {
|
||||
...data.viewer.project,
|
||||
prompts: data.viewer.project.prompts.filter(
|
||||
(prompt: any) => prompt.id !== variables.promptId
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
data.viewer.project.prompts = prompts;
|
||||
|
||||
cache.writeQuery({
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
data,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -12,9 +12,7 @@ import RouterService from '@ember/routing/router-service';
|
||||
import promptCreateQuery, {
|
||||
CreatePromptResponse,
|
||||
} from 'accent-webapp/queries/create-project-prompt';
|
||||
import projectPromptConfigQuery, {
|
||||
ProjectPromptConfigResponse,
|
||||
} from 'accent-webapp/queries/project-prompt-config';
|
||||
import projectPromptConfigQuery from 'accent-webapp/queries/project-prompt-config';
|
||||
import {InMemoryCache} from '@apollo/client/cache';
|
||||
|
||||
const FLASH_MESSAGE_PREFIX = 'pods.project.edit.flash_messages.';
|
||||
@ -87,20 +85,25 @@ export default class PromptsNewController extends Controller {
|
||||
data: {createProjectPrompt},
|
||||
}: {data: {createProjectPrompt: CreatePromptResponse}}
|
||||
) => {
|
||||
const data = cache.readQuery({
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
}) as ProjectPromptConfigResponse;
|
||||
const prompts = data.viewer.project.prompts.concat([
|
||||
createProjectPrompt.prompt,
|
||||
]);
|
||||
data.viewer.project.prompts = prompts;
|
||||
|
||||
cache.writeQuery({
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
data,
|
||||
});
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: projectPromptConfigQuery,
|
||||
variables: {projectId: this.project.id},
|
||||
},
|
||||
(data) => {
|
||||
return {
|
||||
viewer: {
|
||||
...data.viewer,
|
||||
project: {
|
||||
...data.viewer.project,
|
||||
prompts: data.viewer.project.prompts.concat([
|
||||
createProjectPrompt.prompt,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,159 +0,0 @@
|
||||
import {camelize} from '@ember/string';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {readOnly, equal, empty, and} from '@ember/object/computed';
|
||||
import Controller from '@ember/controller';
|
||||
import translationCorrectQuery from 'accent-webapp/queries/correct-translation';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
import FlashMessages from 'ember-cli-flash/services/flash-messages';
|
||||
import Apollo from 'accent-webapp/services/apollo';
|
||||
import ApolloMutate from 'accent-webapp/services/apollo-mutate';
|
||||
import GlobalState from 'accent-webapp/services/global-state';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
import projectTranslateTextQuery from 'accent-webapp/queries/translate-text-project';
|
||||
|
||||
const FLASH_MESSAGE_CORRECT_SUCCESS =
|
||||
'pods.project.conflicts.flash_messages.correct_success';
|
||||
const FLASH_MESSAGE_CORRECT_ERROR =
|
||||
'pods.project.conflicts.flash_messages.correct_error';
|
||||
const FLASH_MESSAGE_TRANSLATE_ERROR_PREFIX =
|
||||
'pods.project.conflicts.flash_messages.translate_error';
|
||||
const FLASH_MESSAGE_TRANSLATE_PROVIDER_ERROR =
|
||||
'pods.project.conflicts.flash_messages.translate_provider_error';
|
||||
|
||||
export default class ConflictsController extends Controller {
|
||||
@tracked
|
||||
model: any;
|
||||
|
||||
@service('intl')
|
||||
intl: IntlService;
|
||||
|
||||
@service('flash-messages')
|
||||
flashMessages: FlashMessages;
|
||||
|
||||
@service('apollo')
|
||||
apollo: Apollo;
|
||||
|
||||
@service('apollo-mutate')
|
||||
apolloMutate: ApolloMutate;
|
||||
|
||||
@service('global-state')
|
||||
globalState: GlobalState;
|
||||
|
||||
queryParams = ['page', 'query', 'document', 'version'];
|
||||
|
||||
@tracked
|
||||
query = '';
|
||||
|
||||
@tracked
|
||||
document: string | null = null;
|
||||
|
||||
@tracked
|
||||
version: string | null = null;
|
||||
|
||||
@tracked
|
||||
page = 1;
|
||||
|
||||
@readOnly('globalState.permissions')
|
||||
permissions: any;
|
||||
|
||||
@equal('model.translations.entries', undefined)
|
||||
emptyEntries: boolean;
|
||||
|
||||
@readOnly('model.revisionModel.project.revisions')
|
||||
revisions: any;
|
||||
|
||||
@empty('document')
|
||||
emptyDocument: boolean;
|
||||
|
||||
@equal('query', '')
|
||||
emptyQuery: boolean;
|
||||
|
||||
@and('emptyEntries', 'model.loading')
|
||||
showLoading: boolean;
|
||||
|
||||
@and('emptyEntries', 'model.loading', 'emptyQuery', 'emptyDocument')
|
||||
showSkeleton: boolean;
|
||||
|
||||
@action
|
||||
async copyTranslation(
|
||||
text: string,
|
||||
sourceLanguageSlug: string,
|
||||
targetLanguageSlug: string
|
||||
) {
|
||||
const {data} = await this.apollo.client.query({
|
||||
fetchPolicy: 'network-only',
|
||||
query: projectTranslateTextQuery,
|
||||
variables: {
|
||||
text,
|
||||
sourceLanguageSlug,
|
||||
targetLanguageSlug,
|
||||
projectId: this.model.project.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (data.viewer?.project?.translatedText?.text) {
|
||||
return data.viewer.project.translatedText;
|
||||
} else if (data.viewer?.project?.translatedText?.error) {
|
||||
const error = data.viewer?.project?.translatedText?.error;
|
||||
const source = sourceLanguageSlug;
|
||||
const target = targetLanguageSlug;
|
||||
|
||||
this.flashMessages.error(
|
||||
this.intl.t(`${FLASH_MESSAGE_TRANSLATE_ERROR_PREFIX}.${error}`, {
|
||||
source,
|
||||
target,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const provider = camelize(data.viewer?.project?.translatedText?.provider);
|
||||
this.flashMessages.error(
|
||||
this.intl.t(FLASH_MESSAGE_TRANSLATE_PROVIDER_ERROR, {provider})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async correctConflict(conflict: any, text: string) {
|
||||
const response = await this.apolloMutate.mutate({
|
||||
mutation: translationCorrectQuery,
|
||||
variables: {
|
||||
translationId: conflict.id,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.errors) {
|
||||
this.flashMessages.error(this.intl.t(FLASH_MESSAGE_CORRECT_ERROR));
|
||||
} else {
|
||||
this.flashMessages.success(this.intl.t(FLASH_MESSAGE_CORRECT_SUCCESS));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@action
|
||||
changeQuery(query: string) {
|
||||
this.page = 1;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@action
|
||||
changeDocument(select: HTMLSelectElement) {
|
||||
this.page = 1;
|
||||
this.document = select.value ? select.value : null;
|
||||
}
|
||||
|
||||
@action
|
||||
changeVersion(select: HTMLSelectElement) {
|
||||
this.page = 1;
|
||||
this.version = select.value ? select.value : null;
|
||||
}
|
||||
|
||||
@action
|
||||
selectPage(page: number) {
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
this.page = page;
|
||||
}
|
||||
}
|
@ -141,16 +141,25 @@
|
||||
"reload_button": "Reload"
|
||||
},
|
||||
"conflicts_filters": {
|
||||
"advanced_filters_button": "Advanced filters",
|
||||
"total_entries_count": "{count, plural, =0 {No strings to review} =1 {1 string to review} other {# strings to review}}",
|
||||
"reference_default_option_text": "No reference language",
|
||||
"version_default_option_text": "Latest version",
|
||||
"document_default_option_text": "All documents",
|
||||
"input_placeholder_text": "Search for a string"
|
||||
"input_placeholder_text": "Search for a string",
|
||||
"advanced_filters_title": "Advanced filters",
|
||||
"advanced_filters": {
|
||||
"empty": "Text is empty (\"\" or null value)",
|
||||
"not_empty": "Text is not empty",
|
||||
"added_last_sync": "Strings added last sync",
|
||||
"commented_on": "Strings with comments",
|
||||
"translated": "To translate"
|
||||
}
|
||||
},
|
||||
"conflicts_items": {
|
||||
"translations_version_notice": "You are viewing strings to review for the version",
|
||||
"conflicts_list": {
|
||||
"translations_version_notice": "You are viewing the strings to be reviewed in the version",
|
||||
"correct_all_button": "Mark all strings as reviewed for this language",
|
||||
"no_translations": "No strings to review for: {query}",
|
||||
"no_translations": "No strings found for: {query}",
|
||||
"all_reviewed_title": "All reviewed",
|
||||
"all_reviewed_subtitle": "All strings have been marked as reviewed"
|
||||
},
|
||||
|
@ -157,13 +157,23 @@
|
||||
"reload_button": "Recharger"
|
||||
},
|
||||
"conflicts_filters": {
|
||||
"advanced_filters_button": "Filtres avancés",
|
||||
"total_entries_count": "{count, plural, =0 {Aucune chaîne à vérifier} =1 {1 chaîne à vérifier} other {# chaînes à vérifier}}",
|
||||
"reference_default_option_text": "Pas de langue de référence",
|
||||
"version_default_option_text": "Dernière version",
|
||||
"document_default_option_text": "Tous les documents",
|
||||
"input_placeholder_text": "Rechercher une chaîne"
|
||||
"input_placeholder_text": "Rechercher une chaîne",
|
||||
"advanced_filters_title": "Filtres avancés",
|
||||
"advanced_filters": {
|
||||
"empty": "Le texte est vide (\"\" ou valeur nulle)",
|
||||
"not_empty": "Le texte n’est pas vide",
|
||||
"added_last_sync": "Ajoutées à la dernière synchronisation",
|
||||
"commented_on": "Chaînes avec commentaires",
|
||||
"conflicted": "À réviser",
|
||||
"translated": "À traduire"
|
||||
}
|
||||
},
|
||||
"conflicts_items": {
|
||||
"conflicts_list": {
|
||||
"translations_version_notice": "Vous consultez les textes à réviser de la version",
|
||||
"correct_all_button": "Marquer toutes les chaînes comme révisées pour cette langue",
|
||||
"no_translations": "Aucune chaîne à examiner pour : {query}",
|
||||
|
@ -3,11 +3,16 @@ import {gql} from '@apollo/client/core';
|
||||
export default gql`
|
||||
query Conflicts(
|
||||
$projectId: ID!
|
||||
$revisionId: ID!
|
||||
$query: String
|
||||
$page: Int
|
||||
$document: ID
|
||||
$relatedRevisions: [ID!]
|
||||
$version: ID
|
||||
$isTextEmpty: Boolean
|
||||
$isTextNotEmpty: Boolean
|
||||
$isAddedLastSync: Boolean
|
||||
$isCommentedOn: Boolean
|
||||
$isTranslated: Boolean
|
||||
) {
|
||||
viewer {
|
||||
project(id: $projectId) {
|
||||
@ -34,30 +39,60 @@ export default gql`
|
||||
name
|
||||
}
|
||||
|
||||
revision(id: $revisionId) {
|
||||
revisions {
|
||||
id
|
||||
isMaster
|
||||
slug
|
||||
name
|
||||
|
||||
translations(
|
||||
query: $query
|
||||
page: $page
|
||||
pageSize: 20
|
||||
document: $document
|
||||
version: $version
|
||||
isConflicted: true
|
||||
) {
|
||||
meta {
|
||||
totalEntries
|
||||
totalPages
|
||||
currentPage
|
||||
nextPage
|
||||
previousPage
|
||||
language {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
groupedTranslations(
|
||||
query: $query
|
||||
page: $page
|
||||
pageSize: 20
|
||||
document: $document
|
||||
version: $version
|
||||
relatedRevisions: $relatedRevisions
|
||||
isConflicted: true
|
||||
isTextEmpty: $isTextEmpty
|
||||
isTextNotEmpty: $isTextNotEmpty
|
||||
isAddedLastSync: $isAddedLastSync
|
||||
isTranslated: $isTranslated
|
||||
isCommentedOn: $isCommentedOn
|
||||
) {
|
||||
meta {
|
||||
totalEntries
|
||||
totalPages
|
||||
currentPage
|
||||
nextPage
|
||||
previousPage
|
||||
}
|
||||
|
||||
revisions {
|
||||
id
|
||||
}
|
||||
|
||||
entries {
|
||||
key
|
||||
document {
|
||||
id
|
||||
path
|
||||
}
|
||||
entries {
|
||||
|
||||
translations {
|
||||
id
|
||||
key
|
||||
conflictedText
|
||||
correctedText
|
||||
valueType
|
||||
isConflicted
|
||||
isTranslated
|
||||
|
||||
lintMessages {
|
||||
text
|
||||
@ -83,31 +118,6 @@ export default gql`
|
||||
rtl
|
||||
}
|
||||
}
|
||||
|
||||
relatedTranslations {
|
||||
id
|
||||
correctedText
|
||||
isConflicted
|
||||
revision {
|
||||
id
|
||||
isMaster
|
||||
name
|
||||
slug
|
||||
rtl
|
||||
|
||||
language {
|
||||
id
|
||||
name
|
||||
slug
|
||||
rtl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document {
|
||||
id
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ export default gql`
|
||||
id
|
||||
translations(
|
||||
query: $query
|
||||
pageSize: 50
|
||||
page: $page
|
||||
document: $document
|
||||
version: $version
|
||||
|
@ -64,9 +64,10 @@ export default Router.map(function () {
|
||||
});
|
||||
this.route('comments', {path: 'conversation'});
|
||||
|
||||
this.route('conflicts');
|
||||
|
||||
this.route('revision', {path: 'revisions/:revisionId'}, function () {
|
||||
this.route('translations');
|
||||
this.route('conflicts');
|
||||
this.route('lint-translations');
|
||||
});
|
||||
|
||||
|
@ -42,6 +42,9 @@ export default class IndexRoute extends Route {
|
||||
}: {query: any; page: number; document: any; version: any},
|
||||
transition: Transition
|
||||
) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationsQuery,
|
||||
|
@ -18,6 +18,9 @@ export default class TranslationRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({translationId}: {translationId: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationQuery,
|
||||
|
@ -53,6 +53,9 @@ export default class ActivitiesRoute extends Route {
|
||||
},
|
||||
transition: Transition
|
||||
) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectActivitiesQuery,
|
||||
|
@ -19,6 +19,9 @@ export default class Activity extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({activityId}: {activityId: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectActivityQuery,
|
||||
|
@ -19,6 +19,9 @@ export default class CollaboratorsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectCollaboratorsQuery,
|
||||
|
@ -25,6 +25,9 @@ export default class CommentsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({page}: {page: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
const pageNumber = page ? parseInt(page, 10) : null;
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
|
@ -8,7 +8,7 @@ import ApolloSubscription, {
|
||||
} from 'accent-webapp/services/apollo-subscription';
|
||||
import RouteParams from 'accent-webapp/services/route-params';
|
||||
import Transition from '@ember/routing/transition';
|
||||
import ConflictsController from 'accent-webapp/controllers/logged-in/project/revision/conflicts';
|
||||
import ConflictsController from 'accent-webapp/controllers/logged-in/project/conflicts';
|
||||
import RouterService from '@ember/routing/router-service';
|
||||
|
||||
export default class ConflictsRoute extends Route {
|
||||
@ -34,44 +34,58 @@ export default class ConflictsRoute extends Route {
|
||||
version: {
|
||||
refreshModel: true,
|
||||
},
|
||||
relatedRevisions: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isTextEmpty: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isTextNotEmpty: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isAddedLastSync: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isCommentedOn: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isConflicted: {
|
||||
refreshModel: true,
|
||||
},
|
||||
isTranslated: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
subscription: Subscription;
|
||||
|
||||
model(
|
||||
{
|
||||
query,
|
||||
page,
|
||||
document,
|
||||
version,
|
||||
}: {query: any; page: number; document: any; version: any},
|
||||
transition: Transition
|
||||
) {
|
||||
model(params: any, transition: Transition) {
|
||||
params.isTextEmpty = params.isTextEmpty === 'true' ? true : null;
|
||||
params.isTextNotEmpty = params.isTextNotEmpty === 'true' ? true : null;
|
||||
params.isAddedLastSync = params.isAddedLastSync === 'true' ? true : null;
|
||||
params.isCommentedOn = params.isCommentedOn === 'true' ? true : null;
|
||||
params.isTranslated = params.isTranslated === 'true' ? false : null;
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationsQuery,
|
||||
{
|
||||
props: (data) => ({
|
||||
revisionModel: this.modelFor('logged-in.project.revision'),
|
||||
projectModel: this.modelFor('logged-in.project'),
|
||||
prompts: data.viewer.project.prompts,
|
||||
documents: data.viewer.project.documents.entries,
|
||||
versions: data.viewer.project.versions.entries,
|
||||
prompts: data.viewer.project.prompts,
|
||||
revisions: data.viewer.project.revisions,
|
||||
relatedRevisions: data.viewer.project.groupedTranslations.revisions,
|
||||
project: data.viewer.project,
|
||||
translations: data.viewer.project.revision.translations,
|
||||
groupedTranslations: data.viewer.project.groupedTranslations,
|
||||
}),
|
||||
options: {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: {
|
||||
projectId: this.routeParams.fetch(transition, 'logged-in.project')
|
||||
.projectId,
|
||||
revisionId: this.routeParams.fetch(
|
||||
transition,
|
||||
'logged-in.project.revision'
|
||||
).revisionId,
|
||||
query,
|
||||
page,
|
||||
document,
|
||||
version,
|
||||
...params,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -98,30 +112,4 @@ export default class ConflictsRoute extends Route {
|
||||
onRefresh() {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
@action
|
||||
onRevisionChange({revisionId}: {revisionId: string}) {
|
||||
const {project} = this.modelFor('logged-in.project') as {project: any};
|
||||
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.router.transitionTo(
|
||||
'logged-in.project.revision.conflicts',
|
||||
project.id,
|
||||
revisionId,
|
||||
{
|
||||
queryParams: this.fetchQueryParams(
|
||||
this.controller as ConflictsController
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private fetchQueryParams(controller: ConflictsController) {
|
||||
const query = controller.query;
|
||||
|
||||
return {
|
||||
query,
|
||||
};
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@ export default class APITokenRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectApiTokenQuery,
|
||||
|
@ -19,6 +19,9 @@ export default class BadgesRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectEditQuery,
|
||||
|
@ -19,6 +19,9 @@ export default class ProjectEditIndexRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectEditQuery,
|
||||
|
@ -18,6 +18,9 @@ export default class MachineTranslationsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectMachineTranslationsConfigQuery,
|
||||
|
@ -18,6 +18,9 @@ export default class PomptsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectPromptConfigQuery,
|
||||
|
@ -19,6 +19,9 @@ export default class ServiceIntegrationsController extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectServiceIntegrationsQuery,
|
||||
|
@ -34,6 +34,9 @@ export default class FilesRoute extends Route {
|
||||
}: {page: string; excludeEmptyTranslations: boolean},
|
||||
transition: Transition
|
||||
) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
const pageNumber = page ? parseInt(page, 10) : null;
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
|
@ -18,6 +18,9 @@ export default class ProjectIndexRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: object, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
const props = (data: any) => ({project: data.viewer.project});
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
|
@ -20,6 +20,9 @@ export default class ManageLanguagesRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
projectNewLanguageQuery,
|
||||
|
@ -43,6 +43,9 @@ export default class LintRoute extends Route {
|
||||
};
|
||||
|
||||
model(params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
lintQuery,
|
||||
|
@ -57,6 +57,9 @@ export default class TranslationsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
params.isTextEmpty = params.isTextEmpty === 'true' ? true : null;
|
||||
params.isTextNotEmpty = params.isTextNotEmpty === 'true' ? true : null;
|
||||
params.isAddedLastSync = params.isAddedLastSync === 'true' ? true : null;
|
||||
|
@ -18,6 +18,9 @@ export default class TranslationsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({translationId}: {translationId: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationQuery,
|
||||
|
@ -26,6 +26,9 @@ export default class ActivitiesRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({page}: {page: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationActivitiesQuery,
|
||||
|
@ -26,6 +26,9 @@ export default class CommentsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({page}: {page: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
const pageNumber = page ? parseInt(page, 10) : null;
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
|
@ -23,6 +23,9 @@ export default class TranslationEditionsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model(_params: any, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
translationEditionsQuery,
|
||||
|
@ -25,6 +25,9 @@ export default class VersionsRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model({page}: {page: string}, transition: Transition) {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
const pageNumber = page ? parseInt(page, 10) : null;
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
|
@ -21,6 +21,9 @@ export default class LoginRoute extends Route {
|
||||
subscription: Subscription;
|
||||
|
||||
model() {
|
||||
if (this.subscription)
|
||||
this.apolloSubscription.clearSubscription(this.subscription);
|
||||
|
||||
this.subscription = this.apolloSubscription.graphql(
|
||||
() => this.modelFor(this.routeName),
|
||||
authenticationProvidersQuery,
|
||||
|
@ -15,9 +15,11 @@ export default class ApolloMutate extends Service {
|
||||
const operationName = Object.keys(data)[0];
|
||||
|
||||
if (!data[operationName]?.errors?.length) {
|
||||
data[operationName].errors = null;
|
||||
|
||||
return data[operationName];
|
||||
const updatedData = {
|
||||
data,
|
||||
[operationName]: {...data[operationName], errors: null},
|
||||
};
|
||||
return updatedData;
|
||||
}
|
||||
|
||||
return data[operationName];
|
||||
|
@ -1,23 +1,16 @@
|
||||
.filters-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.conflicts-filters {
|
||||
:global(.filters) {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.filters-content {
|
||||
flex-grow: 1;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.queryForm {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.totalEntries {
|
||||
flex-shrink: 0;
|
||||
margin-top: 10px;
|
||||
color: var(--color-grey);
|
||||
font-size: 12px;
|
||||
:global(.ember-power-select-multiple-trigger) {
|
||||
width: 100%;
|
||||
border-width: 2px;
|
||||
margin-bottom: 10px;
|
||||
&:focus {
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
@ -47,10 +40,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
.totalEntries {
|
||||
margin-top: 10px;
|
||||
color: var(--color-grey);
|
||||
font-size: 12px;
|
||||
button.advancedFilters {
|
||||
position: relative;
|
||||
box-shadow: none;
|
||||
flex-shrink: 0;
|
||||
&:focus,
|
||||
&:hover {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.advancedFilters-badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -7px;
|
||||
background: var(--color-primary);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 0 4px 1px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 440px) {
|
||||
|
@ -1,9 +1,17 @@
|
||||
.conflicts-items {
|
||||
position: relative;
|
||||
margin-top: 20px;
|
||||
.conflicts-header {
|
||||
position: sticky;
|
||||
padding: 14px 0 0 15px;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--group-columns-count, 1), 1fr);
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 0 0 10px -14px;
|
||||
background: var(--content-background);
|
||||
}
|
||||
|
||||
.conflicts-items-version {
|
||||
.conflicts-list-version {
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
border-radius: var(--border-radius);
|
||||
@ -16,7 +24,7 @@
|
||||
color: var(--color-blue);
|
||||
}
|
||||
|
||||
.conflicts-items-version-tag {
|
||||
.conflicts-list-version-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 2px;
|
||||
@ -26,6 +34,17 @@
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.conflicts-header-item {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.conflicts-header-item-slug {
|
||||
font-weight: normal;
|
||||
opacity: 0.6;
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.all-reviewed {
|
||||
max-width: 400px;
|
||||
margin: 100px auto 20px;
|
||||
@ -43,7 +62,3 @@
|
||||
font-size: 12px;
|
||||
color: var(--color-grey);
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
.conflicts-list-advanced-filters {
|
||||
margin: 15px 0 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
color: var(--text-color-normal);
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
|
||||
input {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.labels {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-width: 960px;
|
||||
}
|
110
webapp/app/styles/components/conflicts-list/group.scss
Normal file
110
webapp/app/styles/components/conflicts-list/group.scss
Normal file
@ -0,0 +1,110 @@
|
||||
.item {
|
||||
--grid-item-reviewed-opacity: 0.5;
|
||||
--grid-item-actions-opacity: 0;
|
||||
border-bottom: 1px solid var(--background-light-highlight);
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: border-color;
|
||||
background: var(--content-background);
|
||||
|
||||
:global(.lint-translations-item) {
|
||||
display: none;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--border-radius);
|
||||
margin: 0 15px 10px;
|
||||
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
&.item--focus {
|
||||
background: var(--background-light);
|
||||
|
||||
:global(.lint-translations-item) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item-key {
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
--grid-item-reviewed-opacity: 1;
|
||||
--grid-item-actions-opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.item-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--group-columns-count, 1), 1fr);
|
||||
}
|
||||
|
||||
.item-grid-item {
|
||||
--border-color: var(--background-light-highlight);
|
||||
position: relative;
|
||||
padding: 0;
|
||||
border-left: 2px solid var(--border-color);
|
||||
opacity: 1;
|
||||
|
||||
&.item-grid-item--translated {
|
||||
--border-color: var(--color-warning);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.item-grid-item--reviewed {
|
||||
--border-color: var(--color-green);
|
||||
opacity: var(--grid-item-reviewed-opacity);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: -24px;
|
||||
left: -2px;
|
||||
height: 32px;
|
||||
width: 2px;
|
||||
background: var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.item-key-prefix {
|
||||
display: inline-flex;
|
||||
font-size: 11px;
|
||||
color: #959595;
|
||||
gap: 6px;
|
||||
font-weight: 300;
|
||||
|
||||
&::before {
|
||||
content: '/';
|
||||
}
|
||||
}
|
||||
|
||||
.item-key {
|
||||
position: sticky;
|
||||
top: 46px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: var(--content-background);
|
||||
padding: 2px 6px;
|
||||
text-decoration: none;
|
||||
font-family: var(--font-monospace);
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
border-radius: var(--border-radius);
|
||||
font-weight: 600;
|
||||
z-index: 2;
|
||||
word-break: break-all;
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: color;
|
||||
margin-left: 4px;
|
||||
line-height: 1.5;
|
||||
font-size: 11px;
|
||||
color: var(--text-color-normal);
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: transform;
|
||||
}
|
||||
|
||||
.key {
|
||||
text-decoration: none;
|
||||
}
|
@ -1,19 +1,13 @@
|
||||
.root {
|
||||
&:nth-child(even) {
|
||||
background: var(--background-light);
|
||||
}
|
||||
}
|
||||
|
||||
.conflict-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.translation-item {
|
||||
&:hover {
|
||||
.form-helpers {
|
||||
pointer-events: all;
|
||||
opacity: 1;
|
||||
}
|
||||
.button-submit {
|
||||
pointer-events: all;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +30,7 @@
|
||||
.item-details__column {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.item-details__column:first-of-type {
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
@ -67,105 +62,11 @@
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.item-detail-conflict {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
.translation-item.resolved {
|
||||
background: var(--color-primary-opacity-10);
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions {
|
||||
margin-left: 6px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-flag,
|
||||
.item-detail-conflict-actions-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 3px;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: transparent;
|
||||
color: var(--color-black);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-link--loading {
|
||||
pointer-events: none;
|
||||
background: transparent;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-flag--link,
|
||||
.item-detail-conflict-actions-link {
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: var(--background-light-highlight);
|
||||
}
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-flag {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
padding: 0 5px;
|
||||
margin-left: 0;
|
||||
margin-right: 5px;
|
||||
width: auto;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--background-light-highlight);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-flag--link {
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: var(--content-background);
|
||||
border-color: var(--color-primary);
|
||||
background: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.item-detail-conflict-actions-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.conflict-item.resolved {
|
||||
padding: 5px 10px;
|
||||
margin: 5px 0;
|
||||
background: hsl(
|
||||
var(--color-green-hue),
|
||||
var(--color-green-saturation),
|
||||
var(--color-highlight-lighteness)
|
||||
);
|
||||
|
||||
.item-key-prefix {
|
||||
opacity: 0.7;
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
.item-key {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
color: var(--color-green);
|
||||
}
|
||||
}
|
||||
|
||||
.conflict-item.errored {
|
||||
.translation-item.errored {
|
||||
.textInput {
|
||||
border-color: var(--color-error);
|
||||
}
|
||||
@ -177,70 +78,18 @@
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.item-key-prefix {
|
||||
display: inline-flex;
|
||||
font-size: 11px;
|
||||
color: #959595;
|
||||
gap: 6px;
|
||||
font-weight: 300;
|
||||
|
||||
&::before {
|
||||
content: '/';
|
||||
}
|
||||
}
|
||||
|
||||
.item-key {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-bottom: 12px;
|
||||
font-family: var(--font-monospace);
|
||||
word-break: break-all;
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: color;
|
||||
margin-right: 15px;
|
||||
line-height: 1.5;
|
||||
font-size: 11px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.key {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.textResolved {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.textResolved-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.textResolved-text {
|
||||
margin-bottom: 5px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.textDiff {
|
||||
padding: 8px 0 3px;
|
||||
color: var(--color-grey);
|
||||
}
|
||||
|
||||
.textDiff {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.button-submit {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
position: absolute;
|
||||
gap: 10px;
|
||||
top: 10px;
|
||||
pointer-events: none;
|
||||
opacity: var(--grid-item-actions-opacity);
|
||||
gap: 0;
|
||||
bottom: 15px;
|
||||
right: 10px;
|
||||
z-index: 3;
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: opacity;
|
||||
|
||||
&[data-dir='rtl'] {
|
||||
right: auto;
|
||||
@ -249,34 +98,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item-revision {
|
||||
flex-shrink: 0;
|
||||
margin-right: 4px;
|
||||
color: var(--color-black);
|
||||
opacity: 0.6;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.item-revision {
|
||||
opacity: 1;
|
||||
padding: 4px 0 3px;
|
||||
margin: 0 9px 0 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.textInput {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
@ -297,19 +123,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.conflictedText-references {
|
||||
width: 100%;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.conflictedText-references-conflicted {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 7px;
|
||||
font-size: 12px;
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
.form-helpers {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
@ -318,24 +131,3 @@
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: opacity;
|
||||
}
|
||||
|
||||
.conflictedText-references-conflicted-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
margin-top: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.conflictedText-references-conflicted-value {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.conflictedText-references-conflicted-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -1,107 +0,0 @@
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
padding: 5px 0 3px;
|
||||
color: var(--color-grey);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.item-link {
|
||||
display: inline-flex;
|
||||
place-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 2px;
|
||||
color: var(--color-black);
|
||||
opacity: 0.8;
|
||||
text-decoration: none;
|
||||
padding: 1px 5px;
|
||||
background: var(--background-light-highlight);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 11px;
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: opacity;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.item-text {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item-text-content {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
margin: 0 0 0 5px;
|
||||
color: var(--color-black);
|
||||
opacity: 0.8;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
margin-left: 6px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.item-actions-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
padding: 3px;
|
||||
background: transparent;
|
||||
color: var(--color-black);
|
||||
background: var(--color-primary-opacity-20);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.item-actions-link--loading {
|
||||
pointer-events: none;
|
||||
background: transparent;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions-flag--link,
|
||||
.item-actions-link {
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: var(--background-light-highlight);
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions-flag {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
padding: 0 5px;
|
||||
margin-left: 0;
|
||||
margin-right: 5px;
|
||||
width: auto;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--background-light-highlight);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.conflicts-page {
|
||||
background: var(--content-background);
|
||||
}
|
@ -37,7 +37,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin: 20px auto 40px;
|
||||
margin: 20px auto 10px;
|
||||
}
|
||||
|
||||
.numberStat-reviewPercentage {
|
||||
@ -92,11 +92,10 @@
|
||||
|
||||
.stats-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 2px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
.slaves {
|
||||
@ -146,7 +145,7 @@
|
||||
}
|
||||
|
||||
.master {
|
||||
margin: 0 0 10px;
|
||||
margin: 25px 0 5px;
|
||||
}
|
||||
|
||||
.stats-title-links {
|
||||
|
@ -8,7 +8,7 @@
|
||||
margin-bottom: 20px;
|
||||
margin-right: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--background-light-highlight);
|
||||
box-shadow: 0 6px 25px var(--shadow-color), 0 2px 7px var(--shadow-color);
|
||||
|
||||
&:nth-child(even) {
|
||||
flex: 1 1 50%;
|
||||
|
@ -64,13 +64,13 @@ button.button {
|
||||
opacity: 1;
|
||||
}
|
||||
.prompt-button-quick-access[data-rtl] {
|
||||
left: 36px;
|
||||
transform: translateX(36px);
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.prompt-button-quick-access {
|
||||
opacity: 1;
|
||||
right: 36px;
|
||||
transform: translateX(-36px);
|
||||
padding: 0 7px;
|
||||
top: -1px;
|
||||
pointer-events: all;
|
||||
@ -87,6 +87,7 @@ button.button {
|
||||
background: var(--input-background);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(0);
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
|
@ -68,12 +68,6 @@
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.details-associations-overflow {
|
||||
max-height: 400px;
|
||||
overflow-y: scroll;
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
.details-associations-pagination {
|
||||
border-top: 1px solid var(--content-background-border);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
}
|
||||
|
||||
:global([data-theme='dark']) .list-item-link:global(.active) {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.list-item-link {
|
||||
|
@ -25,6 +25,7 @@
|
||||
font-size: 13px;
|
||||
transition: 0.2s ease-in-out;
|
||||
transition-property: background, box-shadow, color, transform;
|
||||
overflow: hidden;
|
||||
|
||||
strong {
|
||||
padding: 10px 20px 5px;
|
||||
|
@ -1,11 +1,11 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
padding-right: 30px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.list {
|
||||
@ -13,10 +13,10 @@
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.config {
|
||||
flex: 0 1 auto;
|
||||
max-width: 450px;
|
||||
width: 100%;
|
||||
}
|
||||
@ -30,5 +30,5 @@
|
||||
.text {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
padding-bottom: 22px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
.form {
|
||||
margin-top: 25px;
|
||||
padding: 16px;
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--background-light);
|
||||
@ -31,7 +30,6 @@
|
||||
}
|
||||
|
||||
.select select {
|
||||
background: transparent;
|
||||
border: 1px solid var(--background-light-highlight);
|
||||
padding: 7px 10px;
|
||||
font-weight: bold;
|
||||
@ -71,5 +69,5 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
div.filters {
|
||||
.filters:global(.filters) {
|
||||
margin: 20px auto 0;
|
||||
padding: 0 20px;
|
||||
max-width: var(--screen-lg);
|
||||
|
@ -7,6 +7,7 @@
|
||||
.exportOptions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.exportOptions-advancedFilters {
|
||||
|
@ -74,12 +74,21 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px 130px 10px 10px;
|
||||
font-size: 12px;
|
||||
|
||||
&[dir='rtl'] {
|
||||
padding: 10px 10px 10px 130px;
|
||||
}
|
||||
|
||||
&.inputText--borderless {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
opacity: 0.2;
|
||||
font-style: italic;
|
||||
|
@ -4,11 +4,6 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.filters-content {
|
||||
flex-grow: 1;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.queryForm {
|
||||
position: relative;
|
||||
}
|
||||
@ -43,6 +38,7 @@
|
||||
button.advancedFilters {
|
||||
position: relative;
|
||||
box-shadow: none;
|
||||
flex-shrink: 0;
|
||||
&:focus,
|
||||
&:hover {
|
||||
transform: translate3d(0, 0, 0);
|
||||
@ -60,13 +56,6 @@ button.advancedFilters {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.totalEntries {
|
||||
flex-shrink: 0;
|
||||
margin-top: 10px;
|
||||
color: var(--color-grey);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 440px) {
|
||||
.filters-wrapper {
|
||||
flex-direction: column;
|
||||
|
@ -160,6 +160,7 @@
|
||||
.item-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
|
@ -235,6 +235,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.button--borderLess {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.button--grey {
|
||||
--button-text-color: var(--color-grey);
|
||||
--button-text-hover-color: var(--color-grey);
|
||||
|
@ -10,10 +10,11 @@
|
||||
background: none;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ember-power-select-trigger {
|
||||
padding: 5px 10px;
|
||||
padding: 5px 27px 5px 5px;
|
||||
background: var(--content-background);
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
@ -30,13 +31,28 @@
|
||||
|
||||
.filters-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.filters-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.queryForm {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.queryForm-filters {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.queryForm-filters-column {
|
||||
|
@ -70,6 +70,65 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ember-power-select-multiple-options
|
||||
.ember-power-select-multiple-option:first-of-type {
|
||||
span[role='button'] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ember-power-select-multiple-options {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
|
||||
& li.ember-power-select-trigger-multiple-input-container {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
||||
& input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ember-power-select-trigger-multiple-input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
border: none;
|
||||
line-height: inherit;
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
text-indent: 2px;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .ember-power-select-multiple-option {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ember-power-select-multiple-option {
|
||||
display: inline-block;
|
||||
background: var(--color-primary-opacity-25);
|
||||
color: var(--color-primary-darken-50);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
padding: 3px 9px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ember-power-select-multiple-remove-btn {
|
||||
cursor: pointer;
|
||||
&:not(:hover) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.ember-power-select-status-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
@ -17,6 +17,10 @@
|
||||
<PowerSelect @options={{@options}} @selected={{@selected}} @placeholder={{@placeholder}} @renderInPlace={{@renderInPlace}} @onChange={{@onchange}} as |option|>
|
||||
{{option.label}}
|
||||
</PowerSelect>
|
||||
{{else if @multi}}
|
||||
<PowerSelectMultiple @options={{@options}} @selected={{@selected}} @placeholder={{@placeholder}} @renderInPlace={{@renderInPlace}} @onChange={{@onchange}} as |option|>
|
||||
{{option.label}}
|
||||
</PowerSelectMultiple>
|
||||
{{else}}
|
||||
<div local-class='root' ...attributes>
|
||||
<select {{on 'change' this.selectChange}}>
|
||||
|
@ -2,6 +2,18 @@
|
||||
<div class='filters'>
|
||||
<form class='filters-wrapper' local-class='filters-wrapper' {{on 'submit' (fn this.submitForm)}}>
|
||||
<div class='filters-content' local-class='filters-content'>
|
||||
|
||||
{{#if this.showRevisionsSelect}}
|
||||
<AccSelect
|
||||
@matchTriggerWidth={{false}}
|
||||
@searchEnabled={{false}}
|
||||
@multi={{true}}
|
||||
@selected={{this.relatedRevisionsValue}}
|
||||
@options={{this.mappedRevisionsOptions}}
|
||||
@onchange={{fn @onChangeRevisions}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<div class='queryForm' local-class='queryForm'>
|
||||
{{inline-svg '/assets/search.svg' local-class='search-icon'}}
|
||||
|
||||
@ -13,46 +25,66 @@
|
||||
{{on-key 'Enter' (fn this.submitForm)}}
|
||||
{{on 'keyup' this.setDebouncedQuery}}
|
||||
/>
|
||||
|
||||
{{#if @onChangeAdvancedFilterBoolean}}
|
||||
<button {{on 'click' (fn this.toggleAdvancedFilters)}} local-class='advancedFilters' class='button button--filled button--white'>
|
||||
{{inline-svg 'assets/filter.svg' class='button-icon'}}
|
||||
{{t 'components.conflicts_filters.advanced_filters_button'}}
|
||||
|
||||
{{#if @withAdvancedFilters}}
|
||||
<span local-class='advancedFilters-badge'>
|
||||
{{@withAdvancedFilters}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class='queryForm-filters'>
|
||||
<div class='queryForm-filters-column'>
|
||||
{{#if this.showDocumentsSelect}}
|
||||
<div class='queryForm-filter'>
|
||||
<div class='queryForm-filter-select'>
|
||||
<AccSelect
|
||||
@matchTriggerWidth={{false}}
|
||||
@searchEnabled={{false}}
|
||||
@selected={{this.documentValue}}
|
||||
@options={{this.mappedDocuments}}
|
||||
@onchange={{fn @onChangeDocument}}
|
||||
/>
|
||||
{{#if this.showSomeFilters}}
|
||||
<div class='queryForm-filters'>
|
||||
<div class='queryForm-filters-column'>
|
||||
{{#if this.showDocumentsSelect}}
|
||||
<div class='queryForm-filter'>
|
||||
<div class='queryForm-filter-select'>
|
||||
<AccSelect
|
||||
@matchTriggerWidth={{false}}
|
||||
@searchEnabled={{false}}
|
||||
@selected={{this.documentValue}}
|
||||
@options={{this.mappedDocuments}}
|
||||
@onchange={{fn @onChangeDocument}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showVersionsSelect}}
|
||||
<div class='queryForm-filter'>
|
||||
<div class='queryForm-filter-select'>
|
||||
<AccSelect
|
||||
@matchTriggerWidth={{false}}
|
||||
@searchEnabled={{false}}
|
||||
@selected={{this.versionValue}}
|
||||
@options={{this.mappedVersions}}
|
||||
@onchange={{fn @onChangeVersion}}
|
||||
/>
|
||||
{{#if this.showVersionsSelect}}
|
||||
<div class='queryForm-filter'>
|
||||
<div class='queryForm-filter-select'>
|
||||
<AccSelect
|
||||
@matchTriggerWidth={{false}}
|
||||
@searchEnabled={{false}}
|
||||
@selected={{this.versionValue}}
|
||||
@options={{this.mappedVersions}}
|
||||
@onchange={{fn @onChangeVersion}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if @meta.totalEntries}}
|
||||
<span local-class='totalEntries'>
|
||||
{{t 'components.conflicts_filters.total_entries_count' count=@meta.totalEntries}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if this.displayAdvancedFilters}}
|
||||
<ConflictsList::AdvancedFilters
|
||||
@isTextEmptyFilter={{@isTextEmptyFilter}}
|
||||
@isTextNotEmptyFilter={{@isTextNotEmptyFilter}}
|
||||
@isAddedLastSyncFilter={{@isAddedLastSyncFilter}}
|
||||
@isCommentedOnFilter={{@isCommentedOnFilter}}
|
||||
@isTranslatedFilter={{@isTranslatedFilter}}
|
||||
@onChangeAdvancedFilterBoolean={{@onChangeAdvancedFilterBoolean}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -1,34 +1,49 @@
|
||||
<ul local-class='conflicts-items'>
|
||||
{{#if this.currentVersion}}
|
||||
<div local-class='conflicts-items-version'>
|
||||
{{#if this.currentVersion}}
|
||||
<div local-class='conflicts-list-version'>
|
||||
|
||||
{{t 'components.conflicts_items.translations_version_notice'}}
|
||||
<span local-class='conflicts-items-version-tag'>{{this.currentVersion.tag}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#each @conflicts key='id' as |conflict index|}}
|
||||
<ConflictsList::Item
|
||||
{{t 'components.conflicts_list.translations_version_notice'}}
|
||||
<span local-class='conflicts-list-version-tag'>{{this.currentVersion.tag}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showRevisionsHeader}}
|
||||
<ul local-class='conflicts-header' style='--group-columns-count: {{this.revisions.length}};'>
|
||||
{{#each this.mappedRevisions as |revision|}}
|
||||
<li local-class='conflicts-header-item'>
|
||||
{{revision.name}}
|
||||
<span local-class='conflicts-header-item-slug'>
|
||||
{{revision.slug}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
<ul local-class='conflicts-items' style='--group-columns-count: {{this.revisions.length}};'>
|
||||
{{#each @groupedTranslations key='id' as |groupedTranslation index|}}
|
||||
<ConflictsList::Group
|
||||
@index={{index}}
|
||||
@permissions={{@permissions}}
|
||||
@revisions={{@revisions}}
|
||||
@revisions={{this.revisions}}
|
||||
@project={{@project}}
|
||||
@prompts={{@prompts}}
|
||||
@conflict={{conflict}}
|
||||
@groupedTranslation={{groupedTranslation}}
|
||||
@onCorrect={{@onCorrect}}
|
||||
@onCopyTranslation={{@onCopyTranslation}}
|
||||
@onUncorrect={{@onUncorrect}}
|
||||
@onUpdate={{@onUpdate}}
|
||||
@selectedTranslationId={{this.selectedTranslationId}}
|
||||
@onFocus={{this.handleFocus}}
|
||||
/>
|
||||
{{else if @query}}
|
||||
<EmptyContent local-class='empty-content' @center={{true}} @iconPath='assets/empty.svg' @text={{t 'components.conflicts_items.no_translations' query=@query}} />
|
||||
{{else}}
|
||||
<div local-class='all-reviewed'>
|
||||
<img local-class='all-reviewed-image' src='/assets/all-reviewed-splash.svg' />
|
||||
|
||||
<div local-class='all-reviewed-title'>
|
||||
{{t 'components.conflicts_items.all_reviewed_title'}}
|
||||
{{t 'components.conflicts_list.all_reviewed_title'}}
|
||||
</div>
|
||||
|
||||
<div local-class='all-reviewed-subtitle'>
|
||||
{{t 'components.conflicts_items.all_reviewed_subtitle'}}
|
||||
{{t 'components.conflicts_list.all_reviewed_subtitle'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
@ -0,0 +1,42 @@
|
||||
<div local-class='conflicts-list-advanced-filters'>
|
||||
<span local-class='title'>
|
||||
{{t 'components.conflicts_filters.advanced_filters_title'}}
|
||||
</span>
|
||||
|
||||
<div local-class='labels'>
|
||||
<label local-class='label'>
|
||||
<input type='checkbox' checked={{@isTextEmptyFilter}} {{on 'change' (fn @onChangeAdvancedFilterBoolean 'isTextEmpty')}} />
|
||||
<span class='label-text'>
|
||||
{{t 'components.conflicts_filters.advanced_filters.empty'}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label local-class='label'>
|
||||
<input type='checkbox' checked={{@isTextNotEmptyFilter}} {{on 'change' (fn @onChangeAdvancedFilterBoolean 'isTextNotEmpty')}} />
|
||||
<span>
|
||||
{{t 'components.conflicts_filters.advanced_filters.not_empty'}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label local-class='label'>
|
||||
<input type='checkbox' checked={{@isAddedLastSyncFilter}} {{on 'change' (fn @onChangeAdvancedFilterBoolean 'isAddedLastSync')}} />
|
||||
<span>
|
||||
{{t 'components.conflicts_filters.advanced_filters.added_last_sync'}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label local-class='label'>
|
||||
<input type='checkbox' checked={{@isCommentedOnFilter}} {{on 'change' (fn @onChangeAdvancedFilterBoolean 'isCommentedOn')}} />
|
||||
<span>
|
||||
{{t 'components.conflicts_filters.advanced_filters.commented_on'}}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label local-class='label'>
|
||||
<input type='checkbox' checked={{@isTranslatedFilter}} {{on 'change' (fn @onChangeAdvancedFilterBoolean 'isTranslated')}} />
|
||||
<span>
|
||||
{{t 'components.conflicts_filters.advanced_filters.translated'}}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
38
webapp/app/templates/components/conflicts-list/group.hbs
Normal file
38
webapp/app/templates/components/conflicts-list/group.hbs
Normal file
@ -0,0 +1,38 @@
|
||||
<li local-class='item {{if this.isFocused "item--focus"}}'>
|
||||
<LinkTo @route='logged-in.project.translation' @models={{array @project.id this.masterTranslation.id}} local-class='item-key' tabindex='-1'>
|
||||
{{this.translationKey.value}}
|
||||
|
||||
<small local-class='item-key-prefix'>
|
||||
{{#if this.translationKey.prefix}}
|
||||
{{this.translationKey.prefix}}
|
||||
{{else}}
|
||||
{{@groupedTranslation.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
</LinkTo>
|
||||
|
||||
<ul local-class='item-grid'>
|
||||
{{#each @groupedTranslation.translations as |translation|}}
|
||||
<li
|
||||
local-class='item-grid-item {{if translation.isTranslated "item-grid-item--translated"}} {{if
|
||||
translation.isConflicted
|
||||
"item-grid-item--conflicted"
|
||||
"item-grid-item--reviewed"
|
||||
}}'
|
||||
>
|
||||
<ConflictsList::Item
|
||||
@permissions={{@permissions}}
|
||||
@project={{@project}}
|
||||
@revisions={{@revisions}}
|
||||
@prompts={{@prompts}}
|
||||
@translation={{translation}}
|
||||
@onCorrect={{@onCorrect}}
|
||||
@onUncorrect={{@onUncorrect}}
|
||||
@onUpdate={{@onUpdate}}
|
||||
@isFocused={{this.isFocused}}
|
||||
@onFocus={{this.handleFocus}}
|
||||
/>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</li>
|
@ -1,123 +1,80 @@
|
||||
<div local-class='root'>
|
||||
<div local-class='conflict-item {{if this.resolved "resolved"}} {{if this.error "errored"}}'>
|
||||
<li>
|
||||
{{#if this.resolved}}
|
||||
<div local-class='textResolved'>
|
||||
<LinkTo @route='logged-in.project.translation' @models={{array @project.id @conflict.id}} local-class='key'>
|
||||
<strong local-class='item-key'>
|
||||
{{this.conflictKey.value}}
|
||||
<small local-class='item-key-prefix'>
|
||||
{{#if this.conflictKey.prefix}}
|
||||
{{this.conflictKey.prefix}}
|
||||
{{else}}
|
||||
{{@conflict.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
</strong>
|
||||
</LinkTo>
|
||||
|
||||
<div local-class='textResolved-content'>
|
||||
{{#if this.error}}
|
||||
<div local-class='error'>
|
||||
{{t 'components.conflict_item.uncorrect_error_text'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div local-class='item-details' data-dir={{if this.revisionTextDirRtl 'rtl'}}>
|
||||
<div local-class='item-details__column'>
|
||||
<LinkTo @route='logged-in.project.translation' @models={{array @project.id @conflict.id}} local-class='key'>
|
||||
<strong local-class='item-key'>
|
||||
{{this.conflictKey.value}}
|
||||
<small local-class='item-key-prefix'>
|
||||
{{#if this.conflictKey.prefix}}
|
||||
{{this.conflictKey.prefix}}
|
||||
{{else}}
|
||||
{{@conflict.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
</strong>
|
||||
</LinkTo>
|
||||
|
||||
{{#if this.error}}
|
||||
<div local-class='error'>
|
||||
{{t 'components.conflict_item.correct_error_text'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div local-class='item-details__column'>
|
||||
<div local-class='textInput'>
|
||||
<TranslationEdit::Form
|
||||
@projectId={{@project.id}}
|
||||
@translationId={{@conflict.id}}
|
||||
@lintMessages={{@conflict.lintMessages}}
|
||||
@valueType={{@conflict.valueType}}
|
||||
@value={{this.textInput}}
|
||||
@inputDisabled={{this.inputDisabled}}
|
||||
@showTypeHints={{false}}
|
||||
@onKeyUp={{fn this.changeTranslationText}}
|
||||
@onSubmit={{fn this.correct}}
|
||||
@rtl={{this.revisionTextDirRtl}}
|
||||
lang={{this.revisionSlug}}
|
||||
as |form|
|
||||
>
|
||||
{{#component form.submit}}
|
||||
<div local-class='button-submit' data-dir={{form.dir}}>
|
||||
{{#if this.showOriginalButton}}
|
||||
<AsyncButton @onClick={{fn this.setOriginalText}} local-class='revert-button' class='button button--iconOnly button--white'>
|
||||
{{inline-svg '/assets/revert.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
|
||||
<div local-class='form-helpers'>
|
||||
<TranslationEdit::Helpers
|
||||
@permissions={{@permissions}}
|
||||
@project={{@project}}
|
||||
@revisions={{@revisions}}
|
||||
@prompts={{@prompts}}
|
||||
@rtl={{this.revisionTextDirRtl}}
|
||||
@text={{this.textInput}}
|
||||
@onUpdatingText={{fn this.onUpdatingText}}
|
||||
@onUpdateText={{fn this.onUpdateText}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if (get @permissions 'correct_translation')}}
|
||||
<AsyncButton @onClick={{fn this.correct}} @loading={{this.loading}} class='button button--iconOnly button--filled button--green'>
|
||||
{{inline-svg '/assets/check.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/component}}
|
||||
</TranslationEdit::Form>
|
||||
</div>
|
||||
|
||||
<div local-class='conflictedText-references'>
|
||||
{{#if this.showTextDiff}}
|
||||
<div local-class='conflictedText-references-conflicted'>
|
||||
<span local-class='conflictedText-references-conflicted-label'>
|
||||
{{inline-svg '/assets/diff.svg' local-class='conflictedText-references-conflicted-icon'}}
|
||||
</span>
|
||||
|
||||
<div local-class='conflictedText-references-conflicted-value'>{{string-diff this.textInput @conflict.conflictedText}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if @conflict.relatedTranslations}}
|
||||
{{#each this.relatedTranslations key='id' as |relatedTranslation|}}
|
||||
<ConflictsList::Item::RelatedTranslation
|
||||
@project={{@project}}
|
||||
@translation={{relatedTranslation}}
|
||||
@permissions={{@permissions}}
|
||||
@onCopyTranslation={{perform this.copyTranslationTask}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<li local-class='translation-item {{if @isFocused "focused"}} {{if this.error "errored"}} {{if this.conflictResolved "resolved"}}'>
|
||||
<div local-class='item-details' data-dir={{if this.revisionTextDirRtl 'rtl'}}>
|
||||
<div local-class='item-details__column'>
|
||||
{{#if this.error}}
|
||||
<div local-class='error'>
|
||||
{{t 'components.translation_item.correct_error_text'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
</div>
|
||||
<div local-class='item-details__column'>
|
||||
<div local-class='textInput'>
|
||||
<TranslationEdit::Form
|
||||
@borderless={{true}}
|
||||
@projectId={{@project.id}}
|
||||
@translationId={{@translation.id}}
|
||||
@lintMessages={{@translation.lintMessages}}
|
||||
@valueType={{@translation.valueType}}
|
||||
@value={{this.textInput}}
|
||||
@inputDisabled={{this.inputDisabled}}
|
||||
@showTypeHints={{false}}
|
||||
@onKeyUp={{fn this.changeTranslationText}}
|
||||
@onSubmit={{fn this.correctConflict}}
|
||||
@onFocus={{@onFocus}}
|
||||
@onBlur={{@onBlur}}
|
||||
@rtl={{this.revisionTextDirRtl}}
|
||||
lang={{this.revisionSlug}}
|
||||
as |form|
|
||||
>
|
||||
{{#component form.submit}}
|
||||
<div local-class='button-submit' data-dir={{form.dir}}>
|
||||
{{#if this.showOriginalButton}}
|
||||
<AsyncButton @onClick={{fn this.setOriginalText}} local-class='revert-button' class='button button--iconOnly button--white'>
|
||||
{{inline-svg '/assets/revert.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
|
||||
<div local-class='form-helpers'>
|
||||
<TranslationEdit::Helpers
|
||||
@permissions={{@permissions}}
|
||||
@project={{@project}}
|
||||
@revisions={{@revisions}}
|
||||
@prompts={{@prompts}}
|
||||
@rtl={{this.revisionTextDirRtl}}
|
||||
@text={{this.textInput}}
|
||||
@onUpdatingText={{fn this.onUpdatingText}}
|
||||
@onUpdateText={{fn this.onUpdateText}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if @translation.isConflicted}}
|
||||
{{#if (get @permissions 'update_translation')}}
|
||||
<AsyncButton @loading={{this.isUpdateLoading}} tabindex='-1' class='button button--borderLess button--iconOnly button--grey' @onClick={{fn this.updateConflict}}>
|
||||
{{inline-svg '/assets/pencil.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
{{#if (get @permissions 'correct_translation')}}
|
||||
<AsyncButton @loading={{this.isCorrectLoading}} class='button button--iconOnly button--borderLess button--green' @onClick={{fn this.correctConflict}}>
|
||||
{{inline-svg '/assets/check.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if (get @permissions 'update_translation')}}
|
||||
<AsyncButton @loading={{this.isUpdateLoading}} tabindex='-1' class='button button--borderLess button--iconOnly button--grey' @onClick={{fn this.updateConflict}}>
|
||||
{{inline-svg '/assets/pencil.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
|
||||
{{#if (get @permissions 'uncorrect_translation')}}
|
||||
<AsyncButton @loading={{this.isUncorrectLoading}} class='button button--borderLess button--iconOnly button--red' @onClick={{fn this.uncorrectConflict}}>
|
||||
{{inline-svg '/assets/revert.svg' class='button-icon'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/component}}
|
||||
</TranslationEdit::Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
@ -1,17 +0,0 @@
|
||||
<div local-class='item'>
|
||||
<LinkTo @route='logged-in.project.translation' @models={{array @project.id @translation.id}} local-class='item-link'>
|
||||
{{this.revisionName}}
|
||||
</LinkTo>
|
||||
|
||||
<div local-class='item-text'>
|
||||
<span local-class='item-text-content'>{{this.text}}</span>
|
||||
|
||||
<div local-class='item-actions'>
|
||||
{{#if (get @permissions 'machine_translations_translate')}}
|
||||
<button title={{t 'components.conflict_item.translate_tooltip'}} class='tooltip tooltip--top' local-class='item-actions-link' {{on 'click' this.translate}}>
|
||||
{{inline-svg 'assets/language.svg' local-class='item-actions-icon'}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,38 +0,0 @@
|
||||
<div local-class='conflicts-page'>
|
||||
<ConflictsFilters
|
||||
@meta={{@translations.meta}}
|
||||
@conflicts={{@translations.entries}}
|
||||
@document={{@document}}
|
||||
@documents={{@documents}}
|
||||
@version={{@version}}
|
||||
@versions={{@versions}}
|
||||
@query={{@query}}
|
||||
@onChangeDocument={{@onChangeDocument}}
|
||||
@onChangeQuery={{@onChangeQuery}}
|
||||
@onChangeVersion={{@onChangeVersion}}
|
||||
/>
|
||||
|
||||
{{#if @isLoading}}
|
||||
<SkeletonUi::ProgressLine />
|
||||
{{/if}}
|
||||
|
||||
{{#if @showSkeleton}}
|
||||
<SkeletonUi::ConflictsItems />
|
||||
{{else if @showLoading}}
|
||||
<LoadingContent @label={{t 'pods.project.conflicts.loading_content'}} />
|
||||
{{else}}
|
||||
<ConflictsList
|
||||
@version={{@version}}
|
||||
@versions={{@versions}}
|
||||
@permissions={{@permissions}}
|
||||
@project={{@project}}
|
||||
@revisions={{@revisions}}
|
||||
@prompts={{@prompts}}
|
||||
@conflicts={{@translations.entries}}
|
||||
@query={{@query}}
|
||||
@onCorrect={{@onCorrect}}
|
||||
@onCopyTranslation={{@onCopyTranslation}}
|
||||
/>
|
||||
<ResourcePagination @meta={{@translations.meta}} @onSelectPage={{@onSelectPage}} />
|
||||
{{/if}}
|
||||
</div>
|
@ -30,7 +30,7 @@
|
||||
<h2 local-class='stats-title'>
|
||||
<div local-class='stats-title-links'>
|
||||
{{#if (get @permissions 'sync')}}
|
||||
<LinkTo @route='logged-in.project.files.sync' @models={{array @project.id @document.id}} class='button button--borderLess button--filled button--white'>
|
||||
<LinkTo @route='logged-in.project.files.sync' @models={{array @project.id @document.id}} class='button button--filled button--white'>
|
||||
{{inline-svg '/assets/sync.svg' class='button-icon'}}
|
||||
{{t 'components.documents_list.sync'}}
|
||||
</LinkTo>
|
||||
@ -59,17 +59,6 @@
|
||||
</div>
|
||||
|
||||
{{#if this.slaveRevisions}}
|
||||
<h2 local-class='stats-title'>
|
||||
<div local-class='stats-title-links'>
|
||||
{{#if (get @permissions 'create_slave')}}
|
||||
<LinkTo @route='logged-in.project.manage-languages' @model={{@project.id}} class='button button--filled button--white button--borderLess'>
|
||||
{{inline-svg 'assets/language.svg' class='button-icon'}}
|
||||
{{t 'components.dashboard_revisions.manage_languages_link_title'}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div local-class='slaves'>
|
||||
{{#each this.slaveRevisions key='id' as |revision|}}
|
||||
<DashboardRevisions::Item
|
||||
|
@ -81,45 +81,28 @@
|
||||
{{#unless this.empty}}
|
||||
<div local-class='links'>
|
||||
{{#if (get @permissions 'sync')}}
|
||||
<LinkTo
|
||||
@route='logged-in.project.files.sync'
|
||||
@models={{array @project.id @document.id}}
|
||||
local-class='button-sync'
|
||||
title={{t 'components.documents_list.sync'}}
|
||||
class='tooltip tooltip--top button button--filled button--iconOnly'
|
||||
>
|
||||
<LinkTo @route='logged-in.project.files.sync' @models={{array @project.id @document.id}} local-class='button-sync' class='button button--filled'>
|
||||
{{inline-svg '/assets/sync.svg' class='button-icon'}}
|
||||
{{t 'components.documents_list.sync'}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{#if this.multipleRevisions}}
|
||||
{{#if (get @permissions 'merge')}}
|
||||
<LinkTo
|
||||
@route='logged-in.project.files.add-translations'
|
||||
@models={{array @project.id @document.id}}
|
||||
title={{t 'components.documents_list.merge'}}
|
||||
class='tooltip tooltip--top button button--filled button--white button--iconOnly'
|
||||
>
|
||||
<LinkTo @route='logged-in.project.files.add-translations' @models={{array @project.id @document.id}} class='button button--filled button--white'>
|
||||
{{inline-svg '/assets/merge.svg' class='button-icon'}}
|
||||
{{t 'components.documents_list.merge'}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (get @permissions 'machine_translations_translate')}}
|
||||
<LinkTo
|
||||
@route='logged-in.project.files.machine-translations'
|
||||
@models={{array @project.id @document.id}}
|
||||
title={{t 'components.documents_list.machine_translations'}}
|
||||
class='tooltip tooltip--top button button--filled button--white button--iconOnly'
|
||||
>
|
||||
<LinkTo @route='logged-in.project.files.machine-translations' @models={{array @project.id @document.id}} class='button button--filled button--white'>
|
||||
{{inline-svg '/assets/language.svg' class='button-icon'}}
|
||||
{{t 'components.documents_list.machine_translations'}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
<LinkTo
|
||||
@route='logged-in.project.files.export'
|
||||
@models={{array @project.id @document.id}}
|
||||
title={{t 'components.documents_list.export'}}
|
||||
class='tooltip tooltip--top button button--filled button--white button--iconOnly'
|
||||
>
|
||||
<LinkTo @route='logged-in.project.files.export' @models={{array @project.id @document.id}} class='button button--filled button--white'>
|
||||
{{inline-svg '/assets/export.svg' class='button-icon'}}
|
||||
{{t 'components.documents_list.export'}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div local-class='lint-options'>
|
||||
<div class='filters' local-class='filters'>
|
||||
<div class='filters-wrapper'>
|
||||
<form class='filters-wrapper' local-class='filters-wrapper' {{on 'submit' (fn this.submitForm)}}>
|
||||
<form class='filters-wrapper' local-class='filters-wrapper' {{on 'submit' (fn this.submitForm)}}>
|
||||
<div class='filters-content' local-class='filters-content'>
|
||||
<div class='queryForm' local-class='queryForm'>
|
||||
{{inline-svg '/assets/search.svg' local-class='search-icon'}}
|
||||
|
||||
@ -15,24 +15,26 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class='queryForm-filters'>
|
||||
{{#if this.showDocuments}}
|
||||
<div class='queryForm-filters-column'>
|
||||
<div local-class='exportOptions-item'>
|
||||
<AccSelect @searchEnabled={{false}} @selected={{this.documentValue}} @options={{this.mappedDocuments}} @onchange={{fn this.documentChanged}} />
|
||||
{{#if this.showSomeFilters}}
|
||||
<div class='queryForm-filters'>
|
||||
{{#if this.showDocuments}}
|
||||
<div class='queryForm-filters-column'>
|
||||
<div local-class='exportOptions-item'>
|
||||
<AccSelect @searchEnabled={{false}} @selected={{this.documentValue}} @options={{this.mappedDocuments}} @onchange={{fn this.documentChanged}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showVersions}}
|
||||
<div class='queryForm-filters-column'>
|
||||
<div local-class='exportOptions-item'>
|
||||
<AccSelect @searchEnabled={{false}} @selected={{this.versionValue}} @options={{this.mappedVersions}} @onchange={{fn this.versionChanged}} />
|
||||
{{#if this.showVersions}}
|
||||
<div class='queryForm-filters-column'>
|
||||
<div local-class='exportOptions-item'>
|
||||
<AccSelect @searchEnabled={{false}} @selected={{this.versionValue}} @options={{this.mappedVersions}} @onchange={{fn this.versionChanged}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,5 @@
|
||||
{{#if @lintTranslation.messages}}
|
||||
<div local-class='wrapper'>
|
||||
<div local-class='wrapper' class='lint-translations-item'>
|
||||
<ul local-class='messages'>
|
||||
{{#each this.messages as |message|}}
|
||||
<span local-class='description'>
|
||||
|
@ -208,18 +208,16 @@
|
||||
{{t 'components.project_activity.operations_label'}}
|
||||
</span>
|
||||
|
||||
<div local-class='details-associations-overflow'>
|
||||
{{#each this.operations.entries key='id' as |activity|}}
|
||||
<ActivityItem
|
||||
@compact={{true}}
|
||||
@permissions={{@permissions}}
|
||||
@showTranslationLink={{true}}
|
||||
@componentTranslationPrefix='project_activities_list_item'
|
||||
@project={{@project}}
|
||||
@activity={{activity}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#each this.operations.entries key='id' as |activity|}}
|
||||
<ActivityItem
|
||||
@compact={{true}}
|
||||
@permissions={{@permissions}}
|
||||
@showTranslationLink={{true}}
|
||||
@componentTranslationPrefix='project_activities_list_item'
|
||||
@project={{@project}}
|
||||
@activity={{activity}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{#if this.operations.meta.nextPage}}
|
||||
<div local-class='details-associations-pagination'>
|
||||
<ResourcePagination @meta={{this.operations.meta}} @onSelectPage={{fn this.refreshActivities}} />
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
{{#if (get @permissions 'correct_translation')}}
|
||||
<li local-class='list-item'>
|
||||
<LinkTo @route='logged-in.project.revision.conflicts' @models={{array @project.id @selectedRevision}} local-class='list-item-link'>
|
||||
<LinkTo @route='logged-in.project.conflicts' @model={{@project.id}} local-class='list-item-link'>
|
||||
{{inline-svg '/assets/check.svg' local-class='list-item-link-icon'}}
|
||||
<span local-class='list-item-link-text'>
|
||||
{{t 'components.project_navigation.conflicts_link_title'}}
|
||||
|
@ -1,28 +1,31 @@
|
||||
<div local-class='wrapper'>
|
||||
<div local-class='content'>
|
||||
<h2 local-class='title'>
|
||||
{{t 'components.project_settings.prompts.title'}}
|
||||
</h2>
|
||||
<p local-class='text'>
|
||||
|
||||
{{t 'components.project_settings.prompts.help'}}
|
||||
</p>
|
||||
|
||||
<ul local-class='list'>
|
||||
{{#each @prompts as |prompt|}}
|
||||
<li>
|
||||
<ProjectSettings::Prompts::Item @project={{@project}} @prompt={{prompt}} @onDelete={{@onDeletePrompt}} />
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<LinkTo @route='logged-in.project.edit.prompts.new' @models={{array @project.id}} class='button button--xl button--primary button--highlight'>
|
||||
{{inline-svg '/assets/add.svg' class='button-icon'}}
|
||||
{{t 'components.project_settings.prompts.new_button'}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
<h2 local-class='title'>
|
||||
{{t 'components.project_settings.prompts.title'}}
|
||||
</h2>
|
||||
<p local-class='text'>
|
||||
{{t 'components.project_settings.prompts.help'}}
|
||||
</p>
|
||||
|
||||
<div local-class='config'>
|
||||
<ProjectSettings::Prompts::Config @project={{@project}} @onSave={{@onSaveConfig}} @onDelete={{@onDeleteConfig}} />
|
||||
</div>
|
||||
|
||||
{{#if @project.promptConfig}}
|
||||
<div local-class='content'>
|
||||
{{#if @prompts}}
|
||||
<ul local-class='list'>
|
||||
{{#each @prompts as |prompt|}}
|
||||
<li>
|
||||
<ProjectSettings::Prompts::Item @project={{@project}} @prompt={{prompt}} @onDelete={{@onDeletePrompt}} />
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
<LinkTo @route='logged-in.project.edit.prompts.new' @models={{array @project.id}} class='button button--xl button--primary button--highlight'>
|
||||
{{inline-svg '/assets/add.svg' class='button-icon'}}
|
||||
{{t 'components.project_settings.prompts.new_button'}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
@ -24,16 +24,16 @@
|
||||
</p>
|
||||
|
||||
<textarea placeholder={{this.configKeyPlaceholder}} local-class='textInput' rows='1' {{on 'change' (fn this.onConfigKeyChange)}}>{{this.configKey}}</textarea>
|
||||
</div>
|
||||
|
||||
<div local-class='actions'>
|
||||
{{#if @project.promptConfig}}
|
||||
<AsyncButton @onClick={{perform this.remove}} @loading={{this.isRemoving}} class='button button--red button--borderless'>
|
||||
{{t 'components.project_settings.integrations.delete'}}
|
||||
<div local-class='actions'>
|
||||
{{#if @project.promptConfig}}
|
||||
<AsyncButton @onClick={{perform this.remove}} @loading={{this.isRemoving}} class='button button--red button--borderless'>
|
||||
{{t 'components.project_settings.integrations.delete'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
|
||||
<AsyncButton @onClick={{perform this.submit}} @loading={{this.isSubmitting}} class='button button--filled'>
|
||||
{{t 'components.project_settings.integrations.save'}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
|
||||
<AsyncButton @onClick={{perform this.submit}} @loading={{this.isSubmitting}} class='button button--filled'>
|
||||
{{t 'components.project_settings.integrations.save'}}
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
@ -23,7 +23,7 @@
|
||||
|
||||
{{#if @translation.isConflicted}}
|
||||
<AccBadge @link={{true}}>
|
||||
<LinkTo tabindex='-1' @route='logged-in.project.revision.conflicts' @models={{array @project.id @translation.revision.id}} @query={{hash query=@translation.id}}>
|
||||
<LinkTo tabindex='-1' @route='logged-in.project.conflicts' @model={{@project.id}} @query={{hash query=@translation.id}}>
|
||||
{{t 'components.related_translations_list.conflicted_label'}}
|
||||
</LinkTo>
|
||||
</AccBadge>
|
||||
|
@ -95,9 +95,11 @@
|
||||
<textarea
|
||||
{{autoresize @value}}
|
||||
{{on-key 'Escape' (fn this.cancel)}}
|
||||
{{on-key 'cmd+Enter' @onSubmit}}
|
||||
{{on-key 'cmd+Enter' (fn this.handleSubmit)}}
|
||||
{{on 'input' (fn this.changeText)}}
|
||||
local-class='inputText'
|
||||
{{on 'focus' (fn this.handleFocus)}}
|
||||
{{on 'blur' (fn this.handleBlur)}}
|
||||
local-class='inputText {{if @borderless "inputText--borderless"}}'
|
||||
disabled={{@inputDisabled}}
|
||||
value={{@value}}
|
||||
dir={{if @rtl 'rtl'}}
|
||||
|
@ -19,4 +19,4 @@
|
||||
</div>
|
||||
</EmptyContent>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</ul>
|
@ -15,7 +15,7 @@
|
||||
<span local-class='item-meta'>
|
||||
{{#if @translation.isConflicted}}
|
||||
<AccBadge title={{t 'components.translations_list.in_review_tooltip'}} class='tooltip tooltip--top' @link={{true}}>
|
||||
<LinkTo @route='logged-in.project.revision.conflicts' @models={{array @project.id @translation.revision.id}} @query={{hash query=@translation.id}}>
|
||||
<LinkTo @route='logged-in.project.conflicts' @model={{@project.id}} @query={{hash query=@translation.id}}>
|
||||
{{t 'components.translations_list.in_review_label'}}
|
||||
</LinkTo>
|
||||
</AccBadge>
|
||||
|
@ -39,7 +39,7 @@
|
||||
{{else if @translation.isConflicted}}
|
||||
<AccBadge @link={{this.withRevisionLink}} @primary={{true}}>
|
||||
{{#if this.withRevisionLink}}
|
||||
<LinkTo @route='logged-in.project.revision.conflicts' @models={{array @project.id @translation.revision.id}} @query={{hash query=@translation.id}}>
|
||||
<LinkTo @route='logged-in.project.conflicts' @model={{@project.id}} @query={{hash query=@translation.id}}>
|
||||
{{t 'components.translation_splash_title.conflicted_label'}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
|
@ -1,9 +1,10 @@
|
||||
<div local-class='translations-filter'>
|
||||
<div class='filters'>
|
||||
<form local-class='filters-wrapper' class='filters-wrapper' {{on 'submit' (fn this.submitForm)}}>
|
||||
<div local-class='filters-content'>
|
||||
<div local-class='queryForm'>
|
||||
<form class='filters-wrapper' local-class='filters-wrapper' {{on 'submit' (fn this.submitForm)}}>
|
||||
<div class='filters-content' local-class='filters-content'>
|
||||
<div class='queryForm' local-class='queryForm'>
|
||||
{{inline-svg '/assets/search.svg' local-class='search-icon'}}
|
||||
|
||||
<input
|
||||
local-class='input'
|
||||
type='text'
|
||||
@ -12,6 +13,19 @@
|
||||
{{on-key 'Enter' (fn this.submitForm)}}
|
||||
{{on 'keyup' (fn this.setDebouncedQuery)}}
|
||||
/>
|
||||
|
||||
{{#if @onChangeAdvancedFilterBoolean}}
|
||||
<button {{on 'click' (fn this.toggleAdvancedFilters)}} local-class='advancedFilters' class='button button--filled button--white'>
|
||||
{{inline-svg 'assets/filter.svg' class='button-icon'}}
|
||||
{{t 'components.translations_filter.advanced_filters_button'}}
|
||||
|
||||
{{#if @withAdvancedFilters}}
|
||||
<span local-class='advancedFilters-badge'>
|
||||
{{@withAdvancedFilters}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class='queryForm-filters'>
|
||||
@ -44,20 +58,6 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='queryForm-filters-column queryForm-filters-column--end'>
|
||||
{{#if @onChangeAdvancedFilterBoolean}}
|
||||
<button {{on 'click' (fn this.toggleAdvancedFilters)}} local-class='advancedFilters' class='button button--filled button--white'>
|
||||
{{inline-svg 'assets/filter.svg' class='button-icon'}}
|
||||
{{t 'components.translations_filter.advanced_filters_button'}}
|
||||
|
||||
{{#if @withAdvancedFilters}}
|
||||
<span local-class='advancedFilters-badge'>
|
||||
{{@withAdvancedFilters}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if this.displayAdvancedFilters}}
|
||||
@ -73,12 +73,6 @@
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if @meta.totalEntries}}
|
||||
<span local-class='totalEntries'>
|
||||
{{t 'components.translations_filter.total_entries_count' count=@meta.totalEntries}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -53,7 +53,7 @@
|
||||
|
||||
{{#if @translation.isConflicted}}
|
||||
<AccBadge title={{t 'components.translations_list.in_review_tooltip'}} class='tooltip tooltip--top' @link={{true}}>
|
||||
<LinkTo @route='logged-in.project.revision.conflicts' @models={{array @project.id @translation.revision.id}} @query={{hash query=@translation.id}}>
|
||||
<LinkTo @route='logged-in.project.conflicts' @model={{@project.id}} @query={{hash query=@translation.id}}>
|
||||
{{t 'components.translations_list.in_review_label'}}
|
||||
</LinkTo>
|
||||
</AccBadge>
|
||||
|
48
webapp/app/templates/logged-in/project/conflicts.hbs
Normal file
48
webapp/app/templates/logged-in/project/conflicts.hbs
Normal file
@ -0,0 +1,48 @@
|
||||
<ConflictsFilters
|
||||
@meta={{this.model.groupedTranslations.meta}}
|
||||
@conflicts={{this.model.groupedTranslations.entries}}
|
||||
@document={{this.document}}
|
||||
@documents={{this.model.documents}}
|
||||
@version={{this.version}}
|
||||
@versions={{this.model.versions}}
|
||||
@relatedRevisions={{this.relatedRevisions}}
|
||||
@defaultRelatedRevisions={{this.model.relatedRevisions}}
|
||||
@revisions={{this.model.revisions}}
|
||||
@withAdvancedFilters={{this.withAdvancedFilters}}
|
||||
@isTextEmptyFilter={{this.isTextEmpty}}
|
||||
@isTextNotEmptyFilter={{this.isTextNotEmpty}}
|
||||
@isAddedLastSyncFilter={{this.isAddedLastSync}}
|
||||
@isCommentedOnFilter={{this.isCommentedOn}}
|
||||
@isConflictedFilter={{this.isConflicted}}
|
||||
@isTranslatedFilter={{this.isTranslated}}
|
||||
@query={{this.query}}
|
||||
@onChangeDocument={{this.changeDocument}}
|
||||
@onChangeQuery={{this.changeQuery}}
|
||||
@onChangeVersion={{this.changeVersion}}
|
||||
@onChangeRevisions={{this.changeRelatedRevisions}}
|
||||
@onChangeAdvancedFilterBoolean={{fn this.changeAdvancedFilterBoolean}}
|
||||
/>
|
||||
|
||||
{{#if this.model.loading}}
|
||||
<SkeletonUi::ProgressLine />
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showSkeleton}}
|
||||
<SkeletonUi::TranslationsList />
|
||||
{{else if this.showLoading}}
|
||||
<LoadingContent @label={{t 'pods.project.translations.loading_content'}} />
|
||||
{{else}}
|
||||
<ConflictsList
|
||||
@permissions={{this.permissions}}
|
||||
@version={{this.version}}
|
||||
@versions={{this.model.versions}}
|
||||
@project={{this.model.project}}
|
||||
@revisions={{this.revisions}}
|
||||
@prompts={{this.model.prompts}}
|
||||
@groupedTranslations={{this.model.groupedTranslations.entries}}
|
||||
@onCorrect={{this.correctConflict}}
|
||||
@onUncorrect={{this.uncorrectConflict}}
|
||||
@onUpdate={{this.updateConflict}}
|
||||
/>
|
||||
<ResourcePagination @meta={{this.model.groupedTranslations.meta}} @onSelectPage={{fn this.selectPage}} />
|
||||
{{/if}}
|
@ -1,21 +0,0 @@
|
||||
<ConflictsPage
|
||||
@project={{this.model.project}}
|
||||
@revisions={{this.revisions}}
|
||||
@translations={{this.model.translations}}
|
||||
@isLoading={{this.model.loading}}
|
||||
@showLoading={{this.showLoading}}
|
||||
@version={{this.version}}
|
||||
@document={{this.document}}
|
||||
@permissions={{this.permissions}}
|
||||
@documents={{this.model.documents}}
|
||||
@prompts={{this.model.prompts}}
|
||||
@versions={{this.model.versions}}
|
||||
@showSkeleton={{this.showSkeleton}}
|
||||
@query={{this.query}}
|
||||
@onCorrect={{fn this.correctConflict}}
|
||||
@onCopyTranslation={{fn this.copyTranslation}}
|
||||
@onSelectPage={{fn this.selectPage}}
|
||||
@onChangeDocument={{fn this.changeDocument}}
|
||||
@onChangeVersion={{fn this.changeVersion}}
|
||||
@onChangeQuery={{fn this.changeQuery}}
|
||||
/>
|
Loading…
Reference in New Issue
Block a user