Browse Source

add isFavorite to project list api (#26)

* feat: add isFavorite to project list api

* feat:update frontend resources
master
vran 3 years ago
committed by GitHub
parent
commit
e799940c2d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      api/src/main/java/com/databasir/api/ProjectController.java
  2. 1
      api/src/main/resources/static/css/chunk-03c4dbde.00ac37b1.css
  3. 1
      api/src/main/resources/static/css/chunk-152d8fda.24a020fa.css
  4. 1
      api/src/main/resources/static/css/chunk-2be10a43.3284ac4c.css
  5. 1
      api/src/main/resources/static/css/chunk-39c5c064.80a92238.css
  6. 0
      api/src/main/resources/static/css/chunk-af715350.06814884.css
  7. 1
      api/src/main/resources/static/img/MySQL.9d6aa60a.svg
  8. 1
      api/src/main/resources/static/img/PostgreSQL.308e2acc.svg
  9. 2
      api/src/main/resources/static/index.html
  10. 2
      api/src/main/resources/static/js/app.eed3d895.js
  11. 1
      api/src/main/resources/static/js/app.eed3d895.js.map
  12. 2
      api/src/main/resources/static/js/app.f350e7fd.js
  13. 1
      api/src/main/resources/static/js/app.f350e7fd.js.map
  14. 2
      api/src/main/resources/static/js/chunk-03c4dbde.fc870883.js
  15. 1
      api/src/main/resources/static/js/chunk-03c4dbde.fc870883.js.map
  16. 2
      api/src/main/resources/static/js/chunk-152d8fda.21133535.js
  17. 1
      api/src/main/resources/static/js/chunk-152d8fda.21133535.js.map
  18. 2
      api/src/main/resources/static/js/chunk-2be10a43.e440181e.js
  19. 1
      api/src/main/resources/static/js/chunk-2be10a43.e440181e.js.map
  20. 2
      api/src/main/resources/static/js/chunk-39c5c064.54a7dd3b.js
  21. 1
      api/src/main/resources/static/js/chunk-39c5c064.54a7dd3b.js.map
  22. 2
      api/src/main/resources/static/js/chunk-566612f0.8a0bf91d.js
  23. 1
      api/src/main/resources/static/js/chunk-566612f0.8a0bf91d.js.map
  24. 2
      api/src/main/resources/static/js/chunk-af715350.f0eb72e9.js
  25. 1
      api/src/main/resources/static/js/chunk-af715350.f0eb72e9.js.map
  26. 3
      core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java
  27. 2
      core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java
  28. 16
      core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java
  29. 11
      dao/src/main/java/com/databasir/dao/impl/UserFavoriteProjectDao.java

8
api/src/main/java/com/databasir/api/ProjectController.java

@ -1,5 +1,6 @@
package com.databasir.api;
import com.databasir.api.config.security.DatabasirUserDetails;
import com.databasir.api.validator.CronExpressionValidator;
import com.databasir.common.JsonData;
import com.databasir.core.domain.log.annotation.Operation;
@ -11,6 +12,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -71,7 +73,11 @@ public class ProjectController {
public JsonData<Page<ProjectSimpleResponse>> list(@PageableDefault(sort = "id", direction = Sort.Direction.DESC)
Pageable page,
ProjectListCondition condition) {
return JsonData.ok(projectService.list(page, condition));
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
Integer userId = user.getUserPojo().getId();
return JsonData.ok(projectService.list(userId, page, condition));
}
@PostMapping(Routes.GroupProject.TEST_CONNECTION)

1
api/src/main/resources/static/css/chunk-03c4dbde.00ac37b1.css

@ -1 +0,0 @@
.card-header{display:flex;justify-content:space-between;align-items:center}.el-row{margin-bottom:20px}.el-row:last-child{margin-bottom:0}

1
api/src/main/resources/static/css/chunk-152d8fda.24a020fa.css

@ -0,0 +1 @@
.el-row{margin-bottom:20px}.remark-card{margin-bottom:30px}.remark-header{display:flex;justify-content:space-between;align-items:center}.remark-header .remark-user{color:#6d6d6d}.remark-header .remark-time{color:#6d6d6d;font-size:.8rem;margin-left:10px}.remark-content{white-space:pre-wrap;text-align:left}.text{font-size:14px;line-height:1.3rem}.item{margin-top:10px;margin-right:40px}.toc-wrapper{right:0;z-index:0;bottom:auto;padding-left:12px;margin-left:10px}.toc{top:130px;margin-left:0;transform:scale(1);bottom:0;position:fixed;overflow-y:hidden;overflow-x:hidden}.toc:hover{overflow-y:auto;overflow-x:auto}.toc-wrapper .toc ul{list-style:none;line-height:1.7;inline-size:200px;overflow-wrap:break-word}.toc-wrapper .toc a{display:inherit}

1
api/src/main/resources/static/css/chunk-2be10a43.3284ac4c.css

@ -0,0 +1 @@
.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:22px}.el-row{margin-bottom:20px}.el-row:last-child{margin-bottom:0}

1
api/src/main/resources/static/css/chunk-39c5c064.80a92238.css

@ -1 +0,0 @@
.remark-card{margin-bottom:30px}.remark-header{display:flex;justify-content:space-between;align-items:center}.remark-header .remark-user{color:#6d6d6d}.remark-header .remark-time{color:#6d6d6d;font-size:.8rem;margin-left:10px}.remark-content{white-space:pre-wrap;text-align:left}.text{font-size:14px;line-height:1.3rem}.item{margin-top:10px;margin-right:40px}.toc-wrapper{right:0;top:60;z-index:0;bottom:auto;padding-left:33px;margin-left:10px}.toc{top:130px;margin-left:0;transform:scale(1);bottom:0;position:fixed;overflow-y:hidden;overflow-x:hidden}.toc:hover{overflow-y:auto}.toc-wrapper .toc ul{list-style:none;line-height:1.7;inline-size:200px;overflow-wrap:break-word}.toc-wrapper .toc a{display:inherit}.document-content-wrapper{display:flex;margin:0;min-width:1060px;max-width:1260px}.document-content{min-width:1060px}

0
api/src/main/resources/static/css/chunk-566612f0.06814884.css → api/src/main/resources/static/css/chunk-af715350.06814884.css

1
api/src/main/resources/static/img/MySQL.9d6aa60a.svg

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645770420428" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1363" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M1017.6 521.6c-3.2-3.2-6.4-6.4-6.4-9.6-6.4-12.8-16-22.4-28.8-32-9.6-6.4-28.8-16-32-25.6 6.4 0 12.8-3.2 19.2-3.2 9.6-3.2 16-3.2 25.6-3.2 3.2 0 9.6-3.2 12.8-3.2v-3.2c-3.2-6.4-9.6-12.8-12.8-19.2-12.8-12.8-28.8-25.6-44.8-38.4-9.6-6.4-19.2-9.6-28.8-16-3.2-3.2-9.6-3.2-9.6-6.4-3.2-6.4-6.4-16-9.6-22.4-6.4-16-16-35.2-22.4-54.4-3.2-12.8-6.4-25.6-12.8-35.2-28.8-51.2-57.6-83.2-102.4-115.2-9.6-6.4-22.4-9.6-35.2-12.8h-19.2c-3.2-3.2-9.6-6.4-12.8-9.6-16-9.6-54.4-35.2-67.2-3.2-6.4 19.2 9.6 38.4 16 48 3.2 6.4 9.6 16 12.8 22.4 3.2 6.4 3.2 9.6 3.2 16 6.4 12.8 9.6 28.8 16 41.6l9.6 19.2c3.2 3.2 6.4 6.4 6.4 9.6-3.2 6.4-3.2 16-6.4 22.4-9.6 35.2-6.4 76.8 6.4 102.4 3.2 6.4 16 25.6 28.8 19.2 12.8-6.4 9.6-22.4 12.8-38.4 0-3.2 0-6.4 3.2-9.6 3.2 9.6 6.4 16 9.6 25.6 9.6 16 22.4 32 35.2 41.6 6.4 6.4 12.8 16 19.2 19.2l-6.4-6.4c-6.4-6.4-9.6-12.8-12.8-19.2-12.8-19.2-22.4-35.2-28.8-54.4-3.2-9.6-9.6-19.2-12.8-28.8-3.2-3.2-3.2-9.6-3.2-9.6-3.2 6.4-9.6 12.8-12.8 19.2-6.4 12.8-6.4 28.8-6.4 44.8-9.6-3.2-12.8-12.8-16-22.4-6.4-22.4-9.6-57.6-3.2-83.2 3.2-6.4 9.6-25.6 6.4-32-3.2-6.4-6.4-9.6-9.6-12.8l-9.6-19.2c-6.4-16-9.6-35.2-16-54.4-3.2-6.4-9.6-16-12.8-22.4-6.4-9.6-9.6-12.8-16-22.4 0-3.2-3.2-9.6 0-12.8 0-3.2 3.2-3.2 3.2-3.2 3.2-3.2 12.8 0 16 3.2 9.6 3.2 19.2 9.6 25.6 16 3.2 3.2 6.4 9.6 12.8 9.6h6.4c9.6 3.2 19.2 0 25.6 3.2 12.8 6.4 25.6 12.8 38.4 22.4 35.2 25.6 64 60.8 83.2 105.6 3.2 6.4 3.2 12.8 6.4 22.4 6.4 16 12.8 28.8 19.2 44.8 6.4 12.8 9.6 28.8 19.2 41.6 3.2 6.4 19.2 9.6 28.8 12.8l19.2 9.6c9.6 6.4 19.2 12.8 25.6 19.2 3.2 3.2 19.2 9.6 19.2 16-22.4 0-38.4 3.2-51.2 9.6-6.4-3.2-12.8-3.2-12.8 3.2 3.2 3.2 3.2 6.4 3.2 9.6 3.2 6.4 9.6 12.8 12.8 19.2 6.4 6.4 12.8 9.6 16 12.8 9.6 6.4 22.4 12.8 32 19.2 6.4 3.2 12.8 9.6 19.2 12.8 3.2 3.2 3.2 6.4 9.6 6.4-3.2 0-3.2-3.2-3.2-6.4z" fill="#02608A" p-id="1364"></path><path d="M713.6 166.4c-3.2 0-6.4 0-9.6 3.2 3.2 0 12.8 25.6 16 25.6 3.2-3.2 6.4-9.6 6.4-12.8 0-6.4-3.2-9.6-9.6-12.8 0-3.2 0-3.2-3.2-3.2zM198.4 518.4l-57.6 201.6-57.6-201.6H32c-9.6 86.4-19.2 176-22.4 275.2h44.8c0-73.6 0-137.6 6.4-208l57.6 208h38.4l57.6-208c6.4 73.6 6.4 137.6 9.6 208h48c-3.2-83.2-12.8-172.8-22.4-275.2H198.4zM390.4 588.8c-6.4 60.8-9.6 112-28.8 163.2 0-12.8-3.2-19.2-9.6-44.8l-32-118.4H275.2l44.8 156.8c6.4 22.4 9.6 38.4 9.6 48 0 9.6 0 16-3.2 19.2-6.4 6.4-16 9.6-28.8 9.6h-16v35.2c6.4 3.2 16 6.4 22.4 6.4 25.6 0 48-16 67.2-54.4 25.6-44.8 48-118.4 64-220.8h-44.8z" fill="#02608A" p-id="1365"></path><path d="M953.6 748.8v-230.4h-48v272h115.2v-41.6zM832 771.2c3.2-3.2 6.4-6.4 9.6-12.8 19.2-25.6 28.8-60.8 28.8-112 0-89.6-32-134.4-92.8-134.4-32 0-54.4 12.8-70.4 35.2-19.2 25.6-28.8 60.8-28.8 112 0 48 9.6 83.2 25.6 105.6 16 19.2 38.4 28.8 67.2 28.8 12.8 0 22.4-3.2 32-3.2l57.6 38.4 16-32-44.8-25.6z m-54.4-25.6c-16 0-28.8-6.4-35.2-22.4-9.6-16-12.8-41.6-12.8-73.6 0-60.8 16-89.6 48-89.6 16 0 28.8 6.4 35.2 22.4 9.6 16 12.8 38.4 12.8 73.6 0 60.8-16 89.6-48 89.6zM633.6 656s-35.2-22.4-51.2-32c-19.2-12.8-32-25.6-32-41.6 0-9.6 6.4-16 12.8-25.6 6.4-6.4 16-9.6 28.8-9.6 16 0 32 6.4 44.8 16l9.6-35.2c-19.2-12.8-38.4-19.2-60.8-19.2s-38.4 6.4-54.4 19.2c-12.8 12.8-25.6 38.4-25.6 60.8 0 25.6 9.6 41.6 28.8 57.6 0 0 32 22.4 51.2 32 19.2 12.8 28.8 25.6 28.8 41.6 0 12.8-3.2 22.4-12.8 28.8-9.6 6.4-19.2 3.2-28.8 3.2-16 0-32-6.4-51.2-16l-9.6 32c22.4 16 44.8 25.6 67.2 25.6 25.6 0 44.8-6.4 60.8-22.4 16-16 22.4-35.2 22.4-57.6 0-25.6-9.6-41.6-28.8-57.6z" fill="#E58E01" p-id="1366"></path></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

1
api/src/main/resources/static/img/PostgreSQL.308e2acc.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

2
api/src/main/resources/static/index.html

@ -1 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>databasir-frontend</title><link href="/css/chunk-03c4dbde.00ac37b1.css" rel="prefetch"><link href="/css/chunk-0888fe78.e51aa148.css" rel="prefetch"><link href="/css/chunk-39c5c064.80a92238.css" rel="prefetch"><link href="/css/chunk-566612f0.06814884.css" rel="prefetch"><link href="/js/chunk-03c4dbde.fc870883.js" rel="prefetch"><link href="/js/chunk-0888fe78.b3edc7bf.js" rel="prefetch"><link href="/js/chunk-2d0cc811.c5d1ef9e.js" rel="prefetch"><link href="/js/chunk-39c5c064.54a7dd3b.js" rel="prefetch"><link href="/js/chunk-48cebeac.162363c9.js" rel="prefetch"><link href="/js/chunk-566612f0.8a0bf91d.js" rel="prefetch"><link href="/js/chunk-76185686.f269c8f3.js" rel="prefetch"><link href="/js/chunk-e421509c.b47034f5.js" rel="prefetch"><link href="/js/chunk-fffb1b64.1ffb9f27.js" rel="prefetch"><link href="/css/app.36ecf611.css" rel="preload" as="style"><link href="/css/chunk-vendors.d4aa889d.css" rel="preload" as="style"><link href="/js/app.eed3d895.js" rel="preload" as="script"><link href="/js/chunk-vendors.ec0aa4ac.js" rel="preload" as="script"><link href="/css/chunk-vendors.d4aa889d.css" rel="stylesheet"><link href="/css/app.36ecf611.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but databasir-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.ec0aa4ac.js"></script><script src="/js/app.eed3d895.js"></script></body></html>
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>databasir-frontend</title><link href="/css/chunk-0888fe78.e51aa148.css" rel="prefetch"><link href="/css/chunk-152d8fda.24a020fa.css" rel="prefetch"><link href="/css/chunk-2be10a43.3284ac4c.css" rel="prefetch"><link href="/css/chunk-af715350.06814884.css" rel="prefetch"><link href="/js/chunk-0888fe78.b3edc7bf.js" rel="prefetch"><link href="/js/chunk-152d8fda.21133535.js" rel="prefetch"><link href="/js/chunk-2be10a43.e440181e.js" rel="prefetch"><link href="/js/chunk-2d0cc811.c5d1ef9e.js" rel="prefetch"><link href="/js/chunk-48cebeac.162363c9.js" rel="prefetch"><link href="/js/chunk-76185686.f269c8f3.js" rel="prefetch"><link href="/js/chunk-af715350.f0eb72e9.js" rel="prefetch"><link href="/js/chunk-e421509c.b47034f5.js" rel="prefetch"><link href="/js/chunk-fffb1b64.1ffb9f27.js" rel="prefetch"><link href="/css/app.36ecf611.css" rel="preload" as="style"><link href="/css/chunk-vendors.d4aa889d.css" rel="preload" as="style"><link href="/js/app.f350e7fd.js" rel="preload" as="script"><link href="/js/chunk-vendors.ec0aa4ac.js" rel="preload" as="script"><link href="/css/chunk-vendors.d4aa889d.css" rel="stylesheet"><link href="/css/app.36ecf611.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but databasir-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.ec0aa4ac.js"></script><script src="/js/app.f350e7fd.js"></script></body></html>

2
api/src/main/resources/static/js/app.eed3d895.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/app.eed3d895.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/app.f350e7fd.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/app.f350e7fd.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-03c4dbde.fc870883.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-03c4dbde.fc870883.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-152d8fda.21133535.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-152d8fda.21133535.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-2be10a43.e440181e.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-2be10a43.e440181e.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-39c5c064.54a7dd3b.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-39c5c064.54a7dd3b.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-566612f0.8a0bf91d.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-566612f0.8a0bf91d.js.map

File diff suppressed because one or more lines are too long

2
api/src/main/resources/static/js/chunk-af715350.f0eb72e9.js

File diff suppressed because one or more lines are too long

1
api/src/main/resources/static/js/chunk-af715350.f0eb72e9.js.map

File diff suppressed because one or more lines are too long

3
core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java

@ -33,5 +33,6 @@ public interface ProjectResponseConverter {
@Mapping(target = "createAt", source = "project.createAt")
ProjectSimpleResponse toSimple(ProjectPojo project,
DataSourcePojo dataSource,
ProjectSyncRulePojo syncRule);
ProjectSyncRulePojo syncRule,
Boolean isFavorite);
}

2
core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java

@ -19,6 +19,8 @@ public class ProjectSimpleResponse {
private Boolean isAutoSync;
private Boolean isFavorite;
private String autoSyncCron;
private LocalDateTime createAt;

16
core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java

@ -17,10 +17,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -38,6 +35,8 @@ public class ProjectService {
private final DataSourcePropertyDao dataSourcePropertyDao;
private final UserFavoriteProjectDao userFavoriteProjectDao;
private final DataSourcePojoConverter dataSourcePojoConverter;
private final ProjectPojoConverter projectPojoConverter;
@ -128,7 +127,7 @@ public class ProjectService {
projectSyncRuleDao.disableAutoSyncByProjectId(projectId);
}
public Page<ProjectSimpleResponse> list(Pageable page, ProjectListCondition condition) {
public Page<ProjectSimpleResponse> list(Integer userId, Pageable page, ProjectListCondition condition) {
Page<ProjectPojo> pageData = projectDao.selectByCondition(page, condition.toCondition());
List<Integer> projectIds = pageData.getContent()
.stream()
@ -140,10 +139,15 @@ public class ProjectService {
Map<Integer, ProjectSyncRulePojo> syncRuleMapByProjectId = projectSyncRuleDao.selectInProjectIds(projectIds)
.stream()
.collect(Collectors.toMap(ProjectSyncRulePojo::getProjectId, Function.identity()));
Set<Integer> favoriteProjectIds = userFavoriteProjectDao.selectByUserIdAndProjectIds(userId, projectIds)
.stream()
.map(UserFavoriteProjectPojo::getProjectId)
.collect(Collectors.toSet());
return pageData.map(project -> {
DataSourcePojo dataSource = dataSourceMapByProjectId.get(project.getId());
ProjectSyncRulePojo syncRule = syncRuleMapByProjectId.get(project.getId());
return projectResponseConverter.toSimple(project, dataSource, syncRule);
Boolean isFavorite = favoriteProjectIds.contains(project.getId());
return projectResponseConverter.toSimple(project, dataSource, syncRule, isFavorite);
});
}

11
dao/src/main/java/com/databasir/dao/impl/UserFavoriteProjectDao.java

@ -10,6 +10,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import java.util.Collections;
import java.util.List;
import static com.databasir.dao.Tables.*;
@ -59,4 +60,14 @@ public class UserFavoriteProjectDao extends BaseDao<UserFavoriteProjectPojo> {
.fetchInto(UserFavoriteProjectPojo.class);
return new PageImpl<>(data, request, total);
}
public List<UserFavoriteProjectPojo> selectByUserIdAndProjectIds(Integer userId, List<Integer> projectIds) {
if (projectIds == null || projectIds.isEmpty()) {
return Collections.emptyList();
}
return this.getDslContext()
.select(USER_FAVORITE_PROJECT.fields()).from(USER_FAVORITE_PROJECT)
.where(USER_FAVORITE_PROJECT.USER_ID.eq(userId).and(USER_FAVORITE_PROJECT.PROJECT_ID.in(projectIds)))
.fetchInto(UserFavoriteProjectPojo.class);
}
}

Loading…
Cancel
Save