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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions public/locales/en/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
"set_default": "Set default"
},
"versions_list": "Versions List",
"version_diff": {
"changes": "Changes",
"changed_files": "Files",
"initial_version": "Initial version",
"no_file_changes": "No file changes",
"no_line_changes": "No line changes",
"compared_to_previous": "Line changes from the previous version",
"added": "Added",
"removed": "Removed",
"modified": "Modified",
"unchanged": "Unchanged",
"compare": "Compare",
"compare_from": "From (base)",
"compare_to": "To",
"compare_caption": "Line changes between the selected versions",
"same_version": "Select two different versions",
"restore_preview_title": "Restore this version?",
"restore_preview_caption": "Line changes versus the current version",
"confirm_restore": "Restore",
"unknown_user": "Unknown user",
"restored_from": "Restored from {{time}}"
},
"alerts": {
"no_changes": "There is no changes.",
"branch_removed": "Branch removed",
Expand Down
22 changes: 22 additions & 0 deletions public/locales/ru/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
"set_default": "Установить по умолчанию"
},
"versions_list": "Список версий",
"version_diff": {
"changes": "Изменения",
"changed_files": "Файлы",
"initial_version": "Начальная версия",
"no_file_changes": "Нет изменений в файлах",
"no_line_changes": "Нет изменений строк",
"compared_to_previous": "Изменения строк относительно предыдущей версии",
"added": "Добавлен",
"removed": "Удалён",
"modified": "Изменён",
"unchanged": "Без изменений",
"compare": "Сравнить",
"compare_from": "От (база)",
"compare_to": "До",
"compare_caption": "Изменения строк между выбранными версиями",
"same_version": "Выберите две разные версии",
"restore_preview_title": "Восстановить эту версию?",
"restore_preview_caption": "Изменения строк относительно текущей версии",
"confirm_restore": "Восстановить",
"unknown_user": "Неизвестный пользователь",
"restored_from": "Восстановлено из {{time}}"
},
"alerts": {
"no_changes": "Нет изменений.",
"branch_removed": "Ветка удалена",
Expand Down
22 changes: 22 additions & 0 deletions public/locales/zh/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
"set_default": "设为默认"
},
"versions_list": "版本列表",
"version_diff": {
"changes": "变更",
"changed_files": "文件",
"initial_version": "初始版本",
"no_file_changes": "文件无变更",
"no_line_changes": "无行级变更",
"compared_to_previous": "相对上一版本的行变更",
"added": "新增",
"removed": "删除",
"modified": "修改",
"unchanged": "未变更",
"compare": "比较",
"compare_from": "从(基准)",
"compare_to": "到",
"compare_caption": "所选版本之间的行变更",
"same_version": "请选择两个不同的版本",
"restore_preview_title": "恢复此版本?",
"restore_preview_caption": "相对当前版本的行变更",
"confirm_restore": "恢复",
"unknown_user": "未知用户",
"restored_from": "恢复自 {{time}}"
},
"alerts": {
"no_changes": "没有更改。",
"branch_removed": "分支已移除",
Expand Down
73 changes: 73 additions & 0 deletions src/components/VersionDiff/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.wrapper {
overflow: auto;
max-height: 420px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
background: #fff;
}

.empty {
color: rgba(0, 0, 0, 0.45);
font-weight: 600;
font-size: 13px;
}

.hunkHeader {
padding: 4px 8px;
color: #3f6587;
font-weight: 600;
font-size: 12px;
font-family: monospace;
background: #f0f4f8;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.line {
display: grid;
grid-template-columns: 46px 46px 16px minmax(0, 1fr);
align-items: stretch;
min-height: 20px;
font-size: 12px;
font-family: monospace;
line-height: 20px;
}

.oldNo,
.newNo {
padding: 0 6px;
color: rgba(0, 0, 0, 0.35);
text-align: right;
user-select: none;
}

.sign {
text-align: center;
user-select: none;
}

.text {
min-width: 0;
padding-right: 8px;
white-space: pre-wrap;
word-break: break-word;
}

.equal {
background: #fff;
}

.add {
background: #e6ffec;

.sign {
color: #22863a;
}
}

.remove {
background: #ffebe9;

.sign {
color: #cb2431;
}
}
28 changes: 28 additions & 0 deletions src/components/VersionDiff/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import RootLayout from "@/layouts/RootLayout";
import { diffLines, toHunks } from "@/utils/helpers/versionDiff";

import VersionDiff from ".";

import type { StoryFn, Meta } from "@storybook/react";

export default {
title: "Components/Models/VersionDiff",
component: VersionDiff,
} as Meta<typeof VersionDiff>;

const Template: StoryFn<typeof VersionDiff> = (args) => (
<RootLayout>
<VersionDiff {...args} />
</RootLayout>
);

export const Default = Template.bind({});

Default.args = {
hunks: toHunks(
diffLines(
"cubes:\n - name: Orders\n sql: SELECT * FROM old_orders\n joins: []\n",
"cubes:\n - name: Orders\n sql: SELECT * FROM orders\n joins: []\n"
)
),
};
25 changes: 25 additions & 0 deletions src/components/VersionDiff/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { render, screen } from "@testing-library/react";
import { expect, test, describe } from "vitest";

import { diffLines, toHunks } from "@/utils/helpers/versionDiff";

import VersionDiff from "./";

describe("VersionDiff Component", () => {
test("renders an empty state when there are no hunks", () => {
render(<VersionDiff hunks={[]} />);
expect(screen.getByText("version_diff.no_line_changes")).toBeDefined();
});

test("renders changed lines with line numbers", () => {
const hunks = toHunks(
diffLines("cubes:\n sql: SELECT 1", "cubes:\n sql: SELECT 2")
);

render(<VersionDiff hunks={hunks} />);

expect(screen.getByText(" sql: SELECT 1")).toBeDefined();
expect(screen.getByText(" sql: SELECT 2")).toBeDefined();
expect(screen.getByText("@@ -1,2 +1,2 @@")).toBeDefined();
});
});
65 changes: 65 additions & 0 deletions src/components/VersionDiff/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import cn from "classnames";
import { useTranslation } from "react-i18next";

import type { DiffHunk, DiffLineType } from "@/utils/helpers/versionDiff";

import styles from "./index.module.less";

import type { FC } from "react";

interface VersionDiffProps {
hunks: DiffHunk[];
}

const signForType = (type: DiffLineType): string => {
if (type === "add") {
return "+";
}

if (type === "remove") {
return "-";
}

return " ";
};

const VersionDiff: FC<VersionDiffProps> = ({ hunks }) => {
const { t } = useTranslation(["models"]);

if (!hunks.length) {
return (
<div className={styles.empty}>{t("version_diff.no_line_changes")}</div>
);
}

return (
<div className={styles.wrapper}>
{hunks.map((hunk) => (
<div key={`hunk-${hunk.oldStart}-${hunk.newStart}`}>
<div className={styles.hunkHeader}>
{`@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@`}
</div>
{hunk.lines.map((line) => (
<div
key={`${line.type}-${line.oldNumber ?? "x"}-${
line.newNumber ?? "x"
}`}
className={cn(styles.line, {
[styles.add]: line.type === "add",
[styles.remove]: line.type === "remove",
[styles.equal]: line.type === "equal",
})}
>
<span className={styles.oldNo}>{line.oldNumber ?? ""}</span>
<span className={styles.newNo}>{line.newNumber ?? ""}</span>
<span className={styles.sign}>{signForType(line.type)}</span>
<span className={styles.text}>{line.text}</span>
</div>
))}
</div>
))}
</div>
);
};

export default VersionDiff;
96 changes: 95 additions & 1 deletion src/components/VersionsList/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,54 @@
border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important;
}

.checksumBlock {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}

.checksum {
font-weight: 700;
font-size: 14px;
font-family: monospace;
word-break: break-all;
}

.restoredChecksum {
color: #1677ff;
}

.restoreTag {
margin-inline-end: 0 !important;
}

.author {
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
}

.authorName {
color: rgba(0, 0, 0, 0.85);
font-weight: 700;
font-size: 13px;
}

.createdBlock {
display: flex;
flex-direction: column;
gap: 2px;
}

.author,
.createdAt {
color: rgba(0, 0, 0, 0.5);
font-weight: 600;
font-size: 12px;
white-space: nowrap;
}

.relativeTime {
color: rgba(0, 0, 0, 0.4);
}

.actions {
Expand Down Expand Up @@ -47,6 +86,61 @@
font-size: 14px;
}

.changeStats {
white-space: nowrap;
font-variant-numeric: tabular-nums;
font-weight: 700;
font-size: 13px;
}

.addedCount {
color: #22863a;
}

.removedCount {
margin-left: 8px;
color: #cb2431;
}

.filesHint {
display: block;
margin-top: 2px;
color: rgba(0, 0, 0, 0.45);
font-weight: 600;
font-size: 12px;
}

.initialVersion,
.noChanges {
color: rgba(0, 0, 0, 0.45);
font-weight: 600;
font-size: 13px;
}

.diffSection {
width: 100%;
}

.diffCaption {
color: rgba(0, 0, 0, 0.45);
font-weight: 600;
font-size: 12px;
}

.compareBar {
width: 100%;
}

.compareLabel {
color: rgba(0, 0, 0, 0.65);
font-weight: 700;
font-size: 13px;
}

.compareSelect {
min-width: 240px;
}

.save {
min-width: 141px;
}
Loading