diff --git a/package.json b/package.json
index 1da17ea5..79855838 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "penguin-stats-frontend",
- "version": "1.1.1",
+ "version": "1.1.2",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -15,7 +15,6 @@
"aos": "^2.3.4",
"core-js": "^2.6.5",
"dayjs": "^1.8.15",
- "idb": "^4.0.4",
"js-cookie": "^2.2.1",
"plotly.js": "^1.49.2",
"vue": "^2.6.10",
diff --git a/public/index.html b/public/index.html
index 5746a9b0..43de3f8e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,21 +1,399 @@
-
-
-
-
-
-
-
-
- 企鹅物流数据统计
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 企鹅物流数据统计
+
+
+
+
+
+
+
+
+
+
+ 正在加载
+
+ 首次加载可能较慢,请耐心等待
+
+
+
+
+
+
+
+
+
diff --git a/src/App.vue b/src/App.vue
index 001612c1..97df1dad 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,20 +1,16 @@
-
-
+
+
+
+ mdi-database-refresh
+
+ {{ $t('menu.refreshData') }}
+
+
- mdi-database-refresh
-
-
- {{ $t('menu.refreshData') }}
-
-
-
-
-
mdi-invert-colors
{{ $t('menu.invertColors') }}
-
+
-
- {{ locale.name }}
-
+
+
+ mdi-translate
+ {{ $t('menu.languages') }}
+
+
+
+
+ {{ locale.name }}
+
+
+
+ mdi-beta
+
+
+
+ {{ locale.id }}
+
+
+
@@ -153,7 +167,6 @@
@@ -185,100 +198,109 @@
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
- {{ $t('meta.footer.copyright.title') }}
-
-
-
- {{ $t('meta.footer.copyright.content') }}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+ {{ $t('meta.footer.copyright.title') }}
+
+
+
+ {{ $t('meta.footer.copyright.content') }}
+
+
+
+
+
+
+
+
+ mdi-eye
+
+ {{ $t('meta.details') }}
+
+
+
+
-
-
-
-
- mdi-eye
-
- {{ $t('meta.details') }}
-
-
+
+ Penguin Statistics — {{ new Date().getFullYear() }}
+
-
-
-
-
+
+
+
@@ -289,6 +311,7 @@
import Console from "@/utils/Console";
import strings from "@/utils/strings";
import config from "@/config";
+ import {mapGetters} from "vuex";
export default {
name: 'App',
@@ -311,6 +334,10 @@ export default {
}, {
id: 'ja',
name: '日本語'
+ }, {
+ id: 'ko',
+ name: '한국어',
+ beta: true,
}
],
prefetchingResources: false,
@@ -319,14 +346,21 @@ export default {
}
},
computed: {
- dark: {
+ ...mapGetters('settings', ['language', 'dark']),
+ appDark: {
get () {
- return this.$store.state.settings.dark
+ return this.dark
},
set (value) {
- this.$store.commit('switchDark', value)
+ this.$store.commit('settings/switchDark', value)
this.$vuetify.theme.dark = value
}
+ },
+ localizationMapper: {
+ get () {
+ return this.localizations.indexOf(this.localizations.find(el => el.id === this.$i18n.locale))
+ },
+ set () {}
}
},
watch: {
@@ -337,15 +371,15 @@ export default {
'dark': ['onDarkChange']
},
beforeMount() {
- this.routes = this.$router.options.routes.filter(el => !(el.meta.hide));
- this.$store.dispatch("fetchData", false)
+ this.routes = this.$router.options.routes.filter(el => !el.meta.hide);
+ this.$store.dispatch("data/fetch", false)
},
- mounted () {
+ created () {
this.randomizeLogo();
- this.onDarkChange(this.$store.state.settings.dark);
+ this.onDarkChange(this.dark);
- if (this.$store.getters.language) {
- this.changeLocale(this.$store.getters.language, false)
+ if (this.language) {
+ this.changeLocale(this.language, false)
} else {
const language = strings.getFirstBrowserLanguage();
Console.debug("[i18n] detected language", language);
@@ -356,13 +390,15 @@ export default {
}
}
- if (this.$store.state.settings.dark) {
- this.$vuetify.theme.dark = this.$store.state.settings.dark
+ Console.debug("(before init) dark status", this.dark, this.$vuetify.theme.dark);
+ if (typeof this.dark === "boolean") {
+ this.$vuetify.theme.dark = this.dark
}
+ Console.debug("(after init) dark status", this.dark, this.$vuetify.theme.dark)
},
methods: {
async refreshData () {
- await this.$store.dispatch("fetchData", true);
+ await this.$store.dispatch("data/fetch", true);
},
onDarkChange (newValue) {
if (newValue) {
@@ -404,7 +440,7 @@ export default {
Console.debug("[i18n] locale changed to:", localeId, "| saving to vuex:", save);
this.$i18n.locale = localeId;
// this.$vuetify.lang.current = localeId;
- if (save) this.$store.commit("changeLocale", localeId);
+ if (save) this.$store.commit("settings/changeLocale", localeId);
document.title = `${this.$t(this.$route.meta.i18n) + ' | ' || ''}${this.$t('app.name')}`;
} else {
Console.debug("[i18n] Same locale");
@@ -413,9 +449,9 @@ export default {
logRouteEvent (newValue) {
if (newValue.name === "StatsByStage_Selected") {
// Console.log(this.$store.state.dataSource, newValue.params.stageId);
- this.$ga.event('result', 'fetch_' + this.$store.state.dataSource, newValue.params.stageId, 1)
+ this.$ga.event('result', 'fetch_' + this.$store.getters['dataSource/source'], newValue.params.stageId, 1)
} else if (newValue.name === "StatsByItem_Selected") {
- this.$ga.event('result', 'fetch_' + this.$store.state.dataSource, newValue.params.itemId, 1)
+ this.$ga.event('result', 'fetch_' + this.$store.getters['dataSource/source'], newValue.params.itemId, 1)
}
}
}
@@ -447,10 +483,12 @@ export default {
}
.drawer-logo:hover {
- height: 320px;
+ height: 304px;
padding: 32px;
}
-
+ .drawer-logo--two-line:hover {
+ height: 336px;
+ }
.drawer-logo > .description {
margin-top: 16px;
text-align: center;
@@ -513,4 +551,15 @@ export default {
word-break: break-word;
}
+ .monospace {
+ font-family: SF Mono, "Droid Sans Mono", Ubuntu Mono, Consolas, Courier New, Courier, monospace;
+ }
+
+ .footer--safe-area {
+ padding-top: 8px !important;
+ /*In case the old browsers doesn't support advanced CSS calculations*/
+ padding-bottom: 8px !important;
+ padding-bottom: calc(max(env(safe-area-inset-bottom), 8px)) !important;
+ }
+
diff --git a/src/assets/preloader_i18n.js b/src/assets/preloader_i18n.js
new file mode 100644
index 00000000..62a487e8
--- /dev/null
+++ b/src/assets/preloader_i18n.js
@@ -0,0 +1,91 @@
+"use strict";
+
+let _i18n = {
+ getFirstBrowserLanguageWithRegionCode () {
+ let nav = window.navigator,
+ browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
+ i,
+ language,
+ len,
+ shortLanguage = null;
+
+ // support for HTML 5.1 "navigator.languages"
+ if (Array.isArray(nav.languages)) {
+ for (i = 0; i < nav.languages.length; i++) {
+ language = nav.languages[i];
+ len = language.length;
+ if (!shortLanguage && len) {
+ shortLanguage = language;
+ }
+ if (language && len>2) {
+ return language;
+ }
+ }
+ }
+
+ // support for other well known properties in browsers
+ for (i = 0; i < browserLanguagePropertyKeys.length; i++) {
+ language = nav[browserLanguagePropertyKeys[i]];
+ //skip this loop iteration if property is null/undefined. IE11 fix.
+ if (language == null) { continue; }
+ len = language.length;
+ if (!shortLanguage && len) {
+ shortLanguage = language;
+ }
+ if (language && len > 2) {
+ return language;
+ }
+ }
+
+ return shortLanguage;
+ },
+ getFirstBrowserLanguage () {
+ const language = this.getFirstBrowserLanguageWithRegionCode().replace("_", "-");
+ if (!language) return "zh"; // use default
+ const languages = language.split("-");
+ if (languages.length === 1) {
+ return language
+ } else if (languages.length === 2) {
+ return languages[0]
+ } else {
+ // probably malformed...
+ return language
+ }
+ },
+ data: {
+ "en": {
+ "load_title--text": "Loading...",
+ "load_caption--text": "Initialization may take some time",
+ "load_copyright--text": "Penguin Statistics"
+ },
+ "ja": {
+ "load_title--text": "読み込み中...",
+ "load_caption--text": "読み込みが初めて遅くなる場合があります
しばらくお待ちください",
+ "load_copyright--text": "ペンギン急便データ統計処理部門"
+ },
+ "ko": {
+ "load_title--text": "로딩...",
+ "load_caption--text": "처음으로 로딩이 느려질 수 있으므로 잠시 기다려주십시오",
+ "load_copyright--text": "펭귄 물류 데이터 분석 부서"
+ },
+ },
+ fill (key, content) {
+ document.querySelector("#" + key).innerHTML = content;
+ },
+ render () {
+ document.querySelector("#load_copyright_year--text").textContent = new Date().getFullYear().toString();
+ let language = this.getFirstBrowserLanguage();
+ if (language in this.data && typeof language === "string" && language.length <= 2) {
+ let messages = this.data[language];
+ for (let [key, value] of Object.entries(messages)) {
+ this.fill(key, value)
+ }
+ }
+ }
+};
+
+try {
+ _i18n.render()
+} catch (e) {
+ console.error(e)
+}
\ No newline at end of file
diff --git a/src/components/stats/Charts.vue b/src/components/stats/Charts.vue
index 455b7ffd..1c4dceec 100644
--- a/src/components/stats/Charts.vue
+++ b/src/components/stats/Charts.vue
@@ -104,7 +104,7 @@ export default {
},
computed: {
gradient() {
- return this.$store.state.settings.dark ? ["white"] : ["black"];
+ return this.$store.getters['settings/dark'] ? ["white"] : ["black"];
},
sparkline() {
return {
diff --git a/src/components/stats/DataSourceToggle.vue b/src/components/stats/DataSourceToggle.vue
index 4633b78e..a6553732 100644
--- a/src/components/stats/DataSourceToggle.vue
+++ b/src/components/stats/DataSourceToggle.vue
@@ -23,6 +23,14 @@
"all": "全体",
"personal": "個人"
}
+ },
+ "ko": {
+ "dataSourceToggle": {
+ "title": "Login Required",
+ "loginNotice": "Please log in before viewing personal drop data.",
+ "all": "All",
+ "personal": "Personal"
+ }
}
}
@@ -89,6 +97,7 @@
\ No newline at end of file
diff --git a/src/config/index.js b/src/config/index.js
index 39842924..89fdccbf 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -1,6 +1,6 @@
const client = {
source: "frontend-v2",
- version: "v1.1.1"
+ version: "v1.1.2"
};
export default {
diff --git a/src/layouts/AboutLayout.vue b/src/layouts/AboutLayout.vue
index b1313d69..79bd3210 100644
--- a/src/layouts/AboutLayout.vue
+++ b/src/layouts/AboutLayout.vue
@@ -1,6 +1,6 @@
diff --git a/src/layouts/StatsLayout.vue b/src/layouts/StatsLayout.vue
index 32419521..0b5f7310 100644
--- a/src/layouts/StatsLayout.vue
+++ b/src/layouts/StatsLayout.vue
@@ -1,14 +1,18 @@
-
-
-
+
+
+
+
+
+
+
diff --git a/src/locales/en.json b/src/locales/en.json
index 9203071d..a5c8b18b 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -25,8 +25,9 @@
"planner": "ArkPlanner",
"changelog": "Change Log",
"v1": "Visit Old Version",
- "refreshData": "Refresh Data",
- "invertColors": "Change Theme Color"
+ "refreshData": "Reload Data",
+ "invertColors": "Change Color Theme",
+ "languages": "Language"
},
"meta": {
"details": "Details",
@@ -125,9 +126,10 @@
},
"type": {
"_name": "Items",
- "gte": "Should have at least {quantity} kinds of item",
- "lte": "Should have at most {quantity} kinds of item",
- "not": "Should not be {quantity} kinds of item"
+ "now": "Selected {quantity} kinds of items",
+ "gte": "Should have at least {quantity} kinds of items",
+ "lte": "Should have at most {quantity} kinds of items",
+ "not": "Should not be {quantity} kinds of items"
}
}
}
diff --git a/src/locales/ja.json b/src/locales/ja.json
index bed38e6f..5622c407 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -26,12 +26,13 @@
"changelog": "更新履歴",
"v1": "旧バージョン",
"refreshData": "データ更新",
- "invertColors": "テーマ変更"
+ "invertColors": "テーマ変更",
+ "languages": "言語切替"
},
"meta": {
"details": "詳細を見る",
"loading": "読み込み中...",
- "notfound": "これはどこ¿¿¿",
+ "notfound": "ここはどこ¿¿¿",
"footer": {
"copyright": {
"title": "利用許諾",
@@ -125,6 +126,7 @@
},
"type": {
"_name": "アイテム",
+ "now": "選択されたアイテムは {quantity} 種類です",
"gte": "最少 {quantity} 種類のアイテムが必要です",
"lte": "最大 {quantity} 種類のアイテムが必要です",
"not": "{quantity}種類のアイテムであってはなりません"
diff --git a/src/locales/ko.json b/src/locales/ko.json
new file mode 100644
index 00000000..a0aae7b2
--- /dev/null
+++ b/src/locales/ko.json
@@ -0,0 +1,136 @@
+{
+ "app": {
+ "name": "펭귄 물류 데이터 분석 부서",
+ "name_line1": "펭귄 물류 데이터 분석 부서",
+ "name_line2": ""
+ },
+ "menu": {
+ "home": "톱 페이지",
+ "report": "보고서 작성",
+ "stats": {
+ "_name": "통계",
+ "stage": "작전지역별 통계",
+ "item": "아이템별 통계"
+ },
+ "about": {
+ "_name": "펭귄 통계에 대해",
+ "members": "팀 구성원",
+ "contribute": "기여",
+ "changelog": "변경 로그",
+ "contact": "문의",
+ "donate": "기부",
+ "links": "링크",
+ "bulletin": "공지"
+ },
+ "planner": "명일방주 계획기",
+ "changelog": "변경 로그",
+ "v1": "구 버전 방문하기",
+ "refreshData": "데이터 새로고침",
+ "invertColors": "페이지 테마 변경",
+ "languages": "언어 전환"
+ },
+ "meta": {
+ "details": "세부 사항",
+ "loading": "데이터 새로고침 중...",
+ "notfound": "???",
+ "footer": {
+ "copyright": {
+ "title": "라이센스",
+ "content": "Data was collected by Penguin Delivery, licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. In case of redistribution and transformation towards the material, you must give appropriate credit, provide a link to the license, indicate if changes were made, and may not use the material for commercial purposes."
+ },
+ "credit": "펭귄 물류 데이터 분석 부서 | {date}"
+ },
+ "separator": ", "
+ },
+ "stats": {
+ "headers": {
+ "stage": "작전지역",
+ "apCost": "이성",
+ "item": "아이템",
+ "times": "보고서의 수",
+ "quantity": "확득한 수",
+ "percentage": "백분율",
+ "apPPR": "아이템당 필요 이성 기대치"
+ }
+ },
+ "builds": {
+ "development": {
+ "title": "Preview Build",
+ "description": "You are currently browsing the development build",
+ "ok": "OH YEAH!"
+ }
+ },
+ "boolean": {
+ "true": "네",
+ "false": "아니오"
+ },
+ "hasNorNot": {
+ "true": "드랍됨",
+ "false": "드랍되지 않음"
+ },
+ "dialog": {
+ "cancel": "취소",
+ "confirm": "확인",
+ "submit": "제출",
+ "close": "닫기"
+ },
+ "contribute": {
+ "repo": "프로젝트 저장소: ",
+ "frontend": "프론트엔드",
+ "backend": "백엔드",
+ "contribute_0": "다음 기술중 하나라도 경험이 있고, 펭귄 물류에 기여하고자 한다면. QQ 그룹 : 747099627로 연락하여 주십시오.",
+ "contribute_1": "펭귄 물류 데이터 분석 부서는 비영리 오픈소스 프로젝트 입니다",
+ "skills": {
+ "frontend": "프로트엔드 개발자 (Vue.js)",
+ "backend": "백엔드 개발자 (Java Spring Boot, MongoDB)",
+ "maintenance": "데브옵스",
+ "design": "UI/UX 디자인",
+ "analysis": "데이터 분석",
+ "others": "..."
+ }
+ },
+ "fetch": {
+ "failed": {
+ "title": "신경 네트워크와 연결할 수 없습니다",
+ "subtitle": "신경 네트워크와의 연결할 수 없습니다. 일부 혹은 모든 정보를 불러올 수 없습니다.",
+ "retry": "재시도"
+ }
+ },
+ "report": {
+ "alert": {
+ "title": {
+ "first": "거짓 보고 감지",
+ "repeat": "주의"
+ },
+ "continue": {
+ "first": "이대로 보고서를 제출하시겠습니까?",
+ "repeat": "보고서를 제출하시겠습니까?"
+ },
+ "contact": {
+ "before": "만약 이 오류가 잘못됐다면, ",
+ "activator": "저희에게 문의",
+ "after": " 해주시면 더없이 기쁠 것입니다. (검증을 위해 스테이지 이름과 드랍 목록을 볼 수 있는 스크린샷을 보내 주십시오)저희 팀이 잘못됐음을 확인하면, 최대치를 수정할 것입니다."
+ },
+ "causes": {
+ "noDrop": "드랍된 아이템이 선택되지 않았습니다.",
+ "limitation": "지금 제출되려는 보고서는 저희가 가진 데이터와 큰 차이가 있습니다, 이 보고서를 제출하면, 거짓 보고서로 판단되어 삭제될 수 있습니다."
+ }
+ },
+ "rules": {
+ "item": {
+ "_name": "아이템 수",
+ "now": "{item} ×{quantity}개가 선택되었습니다",
+ "gte": "\"{item}\" ≥ {quantity}개 여야 합니다",
+ "lte": "\"{item}\" ≤ {quantity}개 여야 합니다",
+ "not": "\"{item}\" ≠ {quantity}개 여야 합니다"
+ },
+ "type": {
+ "_name": "아이템 종류",
+ "now": "{quantity}가지의 아이템이 선택되었습니다.",
+ "gte": "최소 {quantity}가지의 아이템이 드랍되어야 합니다",
+ "lte": "최대 {quantity}가지의 아이템만이 드랍될 수 있습니다",
+ "not": "{quantity}가지의 아이템이 드랍될 수 없습니다"
+ }
+ }
+ }
+}
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 6eaaf1fb..a2aa9909 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -26,7 +26,8 @@
"changelog": "更新日志",
"v1": "访问旧版",
"refreshData": "刷新数据",
- "invertColors": "转换主题"
+ "invertColors": "转换主题",
+ "languages": "语言切换"
},
"meta": {
"details": "详细信息",
diff --git a/src/main.js b/src/main.js
index a8476b1e..b4d99447 100644
--- a/src/main.js
+++ b/src/main.js
@@ -31,11 +31,6 @@ if (production) {
});
}
-router.beforeEach((to, from, next) => {
- document.title = `${I18n.t(to.meta.i18n)} | ${I18n.t('app.name')}`;
- next();
-});
-
Vue.use(VueAnalytics, {
id: 'UA-142226262-1',
// customResourceURL: "https://www.google-analytics.com/analytics.js",
@@ -56,6 +51,11 @@ Vue.use(VueAnalytics, {
}
});
+router.beforeEach((to, from, next) => {
+ document.title = `${I18n.t(to.meta.i18n)} | ${I18n.t('app.name')}`;
+ next();
+});
+
Vue.config.productionTip = false;
new Vue({
diff --git a/src/models/_common.js b/src/models/_common.js
index 1b96bdd2..21d88efa 100644
--- a/src/models/_common.js
+++ b/src/models/_common.js
@@ -3,15 +3,15 @@ import store from '@/store'
export default {
defaultAjaxHooks: {
request: (id) => {
- store.dispatch("ajaxStarted", {id})
+ store.dispatch("ajax/started", {id})
},
response: (id, promise) => {
promise.then(
() => {
- store.dispatch("ajaxFinished", {id, error: null});
+ store.dispatch("ajax/finished", {id, error: null});
},
({errorMessage}) => {
- store.dispatch("ajaxFinished", {id, error: errorMessage});
+ store.dispatch("ajax/finished", {id, error: errorMessage});
}
)
}
diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js
index 33e87810..36e725a0 100644
--- a/src/plugins/vuetify.js
+++ b/src/plugins/vuetify.js
@@ -5,9 +5,6 @@ import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
- theme: {
- dark: true
- },
icons: {
iconfont: 'mdi',
},
diff --git a/src/store.js b/src/store.js
deleted file mode 100644
index be6669eb..00000000
--- a/src/store.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-import createPersistedState from 'vuex-persistedstate'
-
-import itemsManager from '@/models/items'
-import limitationsManager from '@/models/limitations'
-import stagesManager from '@/models/stages'
-import zonesManager from '@/models/zones'
-// import trendsManager from '@/models/trends'
-import globalMatrixManager from '@/models/matrices/globalMatrix'
-import personalMatrixManager from '@/models/matrices/personalMatrix'
-
-Vue.use(Vuex);
-
-export default new Vuex.Store({
- plugins: [
- createPersistedState({
- key: "penguin-stats-state",
- paths: [
- "data",
- "settings",
- "auth",
- "cacheUpdateAt"
- ]
- })
- ],
- state: {
- data: {},
- ajax: {
- states: []
- },
- settings: {
- dark: true,
- language: null
- },
- auth: {
- username: null
- },
- cacheUpdateAt: {},
- dataSource: 'global'
- },
- mutations: {
- store: (state, {key, value}) => {
- Vue.set(state.data, key, value)
- },
- storeCacheUpdateAt: (state, d) => {
- state.cacheUpdateAt = Object.assign(state.cacheUpdateAt, d);
- },
- switchDataSource: (state, value) => {
- state.dataSource = value;
- },
- switchDark(state, newState) {
- state.settings.dark = newState
- },
- changeLocale(state, newLocale) {
- state.settings.language = newLocale
- },
- authLogin(state, username) {
- state.auth.username = username
- },
- authLogout(state) {
- state.auth.username = null
- },
- ajaxNewState(state, payload) {
- state.ajax.states.push(payload);
- }
- },
- actions: {
- // eslint-disable-next-line
- async fetchData({}, refresh = false) {
- Promise.all([
- itemsManager.get(refresh),
- limitationsManager.get(refresh),
- stagesManager.get(refresh),
- zonesManager.get(refresh)
- ]).then(() => {
- globalMatrixManager.get(refresh);
- personalMatrixManager.get(refresh)
- })
- // await trendsManager.get(refresh);
-
- },
- async refreshPersonalMatrixData() {
- await personalMatrixManager.get(true)
- },
- _getOrCreateAjaxStateObject ({commit, state}, id) {
- let found = state.ajax.states.find(value => value.id === id);
- if (found) {
- return found
- } else {
- let pushing = Object.create(null);
- pushing.id = id;
- pushing.pending = false;
- pushing.error = null;
-
- commit('ajaxNewState', pushing);
- return pushing
- }
- } ,
- ajaxStarted({dispatch}, {id}) {
- dispatch('_getOrCreateAjaxStateObject', id)
- .then(res => {
- res.pending = true
- });
- },
- ajaxFinished({dispatch}, {id, error}) {
- dispatch('_getOrCreateAjaxStateObject', id)
- .then(res => {
- res.pending = false;
- res.error = error
- });
- }
- },
- getters: {
- authed: state => {
- return !!state.auth.username
- },
- authUsername: state => {
- return state.auth.username || ''
- },
- ajaxPending: state => {
- return state.ajax.states.some(value => value.pending)
- },
- ajaxFinishedAll: state => {
- return state.ajax.states.every(value => !value.pending)
- },
- ajaxErrors: state => {
- return state.ajax.states.filter(value => !!value.error)
- },
- dataByKey: (state) => (id) => {
- return state.data[id]
- },
- cacheUpdateAt: (state) => (name) => {
- return state.cacheUpdateAt[name]
- },
- language: state => state.settings.language
- }
-})
\ No newline at end of file
diff --git a/src/store/index.js b/src/store/index.js
new file mode 100644
index 00000000..39a075b3
--- /dev/null
+++ b/src/store/index.js
@@ -0,0 +1,35 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import createPersistedState from 'vuex-persistedstate'
+
+// store file
+import ajax from './modules/ajax';
+import auth from './modules/auth';
+import cacheUpdateAt from './modules/cacheUpdateAt';
+import data from './modules/data';
+import dataSource from './modules/dataSource';
+import settings from './modules/settings';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+ plugins: [
+ createPersistedState({
+ key: "penguin-stats-state",
+ paths: [
+ "data",
+ "settings",
+ "auth",
+ "cacheUpdateAt"
+ ]
+ })
+ ],
+ modules: {
+ ajax,
+ auth,
+ cacheUpdateAt,
+ data,
+ dataSource,
+ settings
+ }
+});
\ No newline at end of file
diff --git a/src/store/modules/ajax.js b/src/store/modules/ajax.js
new file mode 100644
index 00000000..f83494f8
--- /dev/null
+++ b/src/store/modules/ajax.js
@@ -0,0 +1,51 @@
+export default {
+ namespaced: true,
+ state: {
+ states: []
+ },
+ mutations: {
+ newState(state, payload) {
+ state.states.push(payload);
+ }
+ },
+ actions: {
+ _getOrCreateState ({commit, state}, id) {
+ let found = state.states.find(value => value.id === id);
+ if (found) {
+ return found
+ } else {
+ let pushing = Object.create(null);
+ pushing.id = id;
+ pushing.pending = false;
+ pushing.error = null;
+
+ commit('newState', pushing);
+ return pushing
+ }
+ } ,
+ started({dispatch}, {id}) {
+ dispatch('_getOrCreateState', id)
+ .then(res => {
+ res.pending = true
+ });
+ },
+ finished({dispatch}, {id, error}) {
+ dispatch('_getOrCreateState', id)
+ .then(res => {
+ res.pending = false;
+ res.error = error
+ });
+ }
+ },
+ getters: {
+ pending: state => {
+ return state.states.some(value => value.pending)
+ },
+ // finishedAll: state => {
+ // return state.states.every(value => !value.pending)
+ // },
+ errors: state => {
+ return state.states.filter(value => !!value.error)
+ },
+ }
+};
\ No newline at end of file
diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js
new file mode 100644
index 00000000..a1ddbe4b
--- /dev/null
+++ b/src/store/modules/auth.js
@@ -0,0 +1,22 @@
+export default {
+ namespaced: true,
+ state: {
+ username: null
+ },
+ mutations: {
+ login(state, username) {
+ state.username = username
+ },
+ logout(state) {
+ state.username = null
+ }
+ },
+ getters: {
+ loggedIn: state => {
+ return !!state.username
+ },
+ username: state => {
+ return state.username || ''
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/store/modules/cacheUpdateAt.js b/src/store/modules/cacheUpdateAt.js
new file mode 100644
index 00000000..75571186
--- /dev/null
+++ b/src/store/modules/cacheUpdateAt.js
@@ -0,0 +1,14 @@
+export default {
+ namespaced: true,
+ state: {},
+ mutations: {
+ store: (state, d) => {
+ state = Object.assign(state, d);
+ }
+ },
+ getters: {
+ byName: (state) => (name) => {
+ return state[name]
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/store/modules/data.js b/src/store/modules/data.js
new file mode 100644
index 00000000..eeebfae4
--- /dev/null
+++ b/src/store/modules/data.js
@@ -0,0 +1,41 @@
+import Vue from 'vue';
+import itemsManager from '@/models/items'
+import limitationsManager from '@/models/limitations'
+import stagesManager from '@/models/stages'
+import zonesManager from '@/models/zones'
+// import trendsManager from '@/models/trends'
+import globalMatrixManager from '@/models/matrices/globalMatrix'
+import personalMatrixManager from '@/models/matrices/personalMatrix'
+
+export default {
+ namespaced: true,
+ state: {},
+ mutations: {
+ store: (state, {key, value}) => {
+ Vue.set(state, key, value)
+ }
+ },
+ actions: {
+ // eslint-disable-next-line
+ async fetch({}, refresh = false) {
+ Promise.all([
+ itemsManager.get(refresh),
+ limitationsManager.get(refresh),
+ stagesManager.get(refresh),
+ zonesManager.get(refresh)
+ ]).then(() => {
+ globalMatrixManager.get(refresh);
+ personalMatrixManager.get(refresh)
+ })
+ // await trendsManager.get(refresh);
+ },
+ async refreshPersonalMatrix() {
+ await personalMatrixManager.get(true)
+ }
+ },
+ getters: {
+ byKey: (state) => (id) => {
+ return state[id]
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/store/modules/dataSource.js b/src/store/modules/dataSource.js
new file mode 100644
index 00000000..46e5d974
--- /dev/null
+++ b/src/store/modules/dataSource.js
@@ -0,0 +1,14 @@
+export default {
+ namespaced: true,
+ state: {
+ source: 'global'
+ },
+ mutations: {
+ switch: (state, value) => {
+ state.source = value;
+ }
+ },
+ getters: {
+ source: state => state.source
+ }
+};
\ No newline at end of file
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
new file mode 100644
index 00000000..6cd67a9a
--- /dev/null
+++ b/src/store/modules/settings.js
@@ -0,0 +1,19 @@
+export default {
+ namespaced: true,
+ state: {
+ dark: true,
+ language: null
+ },
+ mutations: {
+ switchDark(state, newState) {
+ state.dark = newState
+ },
+ changeLocale(state, newLocale) {
+ state.language = newLocale
+ }
+ },
+ getters: {
+ language: state => state.language,
+ dark: state => state.dark,
+ }
+};
\ No newline at end of file
diff --git a/src/utils/Console.js b/src/utils/Console.js
index 029e8140..c361fd5a 100644
--- a/src/utils/Console.js
+++ b/src/utils/Console.js
@@ -1,3 +1,5 @@
+import * as Sentry from "@sentry/browser";
+
class Console {
static debug (...content) {
this._render("debug", ...content)
@@ -6,10 +8,14 @@ class Console {
this._render("info", ...content)
}
static warn (...content) {
- this._render("warn", ...content)
+ this._render("warn", ...content);
+ const contents = [...content];
+ Sentry.captureMessage(contents.join(" | "), Sentry.Severity.Warning)
}
static error (...content) {
- this._render("error", ...content)
+ this._render("error", ...content);
+ const contents = [...content];
+ Sentry.captureMessage(contents.join(" | "), Sentry.Severity.Error)
}
static log (...content) {
this._render("log", ...content)
diff --git a/src/utils/getters.js b/src/utils/getters.js
index e27666d1..4d01fa9e 100644
--- a/src/utils/getters.js
+++ b/src/utils/getters.js
@@ -22,7 +22,7 @@ Getters.limitations = {
}
Getters.statistics = {
byItemId(itemId) {
- const stats = store.state.data[`${store.state.dataSource}Matrix`];
+ const stats = store.state.data[`${store.getters['dataSource/source']}Matrix`];
Console.debug(stats)
if (!stats) return [];
return stats.filter(el => {
@@ -30,7 +30,7 @@ Getters.statistics = {
})
},
byStageId(stageId) {
- const stats = store.state.data[`${store.state.dataSource}Matrix`];
+ const stats = store.state.data[`${store.getters['dataSource/source']}Matrix`];
Console.debug(stats)
if (!stats) return [];
return stats.filter(el => {
@@ -94,7 +94,7 @@ Getters.trends = {
return this.all() && this.all()[stageId];
},
all() {
- if (store.state.dataSource !== 'global') {
+ if (store.getters['dataSource/source'] !== 'global') {
return null;
}
return store.state.data && store.state.data.trends && store.state.data.trends.results
diff --git a/src/utils/objectManager.js b/src/utils/objectManager.js
index 9acd57a4..74a187b3 100644
--- a/src/utils/objectManager.js
+++ b/src/utils/objectManager.js
@@ -47,7 +47,7 @@ class ObjectManager {
* @returns {boolean} validity of the current cache
*/
get cacheValid() {
- let cacheUpdateAt = store.getters.cacheUpdateAt(this.name)
+ let cacheUpdateAt = store.getters['cacheUpdateAt/byName'](this.name)
// Console.debug("[debug]: ",
// this.name,
// "objectManager cache valid:",
@@ -70,7 +70,7 @@ class ObjectManager {
let context = this;
if (!refresh && context.cacheValid) {
// valid cache
- return Promise.resolve(store.getters.dataByKey(context.name));
+ return Promise.resolve(store.getters['data/byKey'](context.name));
} else {
// outdated cache, fetch api
context.ajaxHooks.request(context.name);
@@ -81,13 +81,13 @@ class ObjectManager {
Console.debug("[objectManager]", context.name , "after transform", data)
context.cache = data
- store.commit("store", {key: context.name, value: data});
+ store.commit("data/store", {key: context.name, value: data});
const now = Date.now();
let cacheUpdateAtTemp = {};
cacheUpdateAtTemp[context.name] = now;
- store.commit("storeCacheUpdateAt", cacheUpdateAtTemp);
+ store.commit("cacheUpdateAt/store", cacheUpdateAtTemp);
Console.debug(`fetched new ${context.name} data at ${now}`)
diff --git a/src/utils/strings.js b/src/utils/strings.js
index 9a8f38de..bffe0645 100644
--- a/src/utils/strings.js
+++ b/src/utils/strings.js
@@ -1,6 +1,10 @@
import I18n from "@/i18n"
import Console from "@/utils/Console";
+function getLocaleMessage(object, localeKey, key, language) {
+ return object[localeKey][language] || object[localeKey][I18n.fallbackLocale] || object[key] || "";
+}
+
function translate (object, key) {
let locale = I18n.locale;
let localeKey = `${key}_i18n`;
@@ -8,13 +12,13 @@ function translate (object, key) {
if (object) {
if (object[localeKey]) {
if (object[localeKey][locale]) {
- return object[localeKey][locale] || ""
+ return getLocaleMessage(object, localeKey, key, locale)
} else {
- let languages = locale.split("-")
- if (languages.length === 2 && languages[0] !== "") {
- return object[localeKey][languages[0]] || "";
+ let languages = locale.split("-");
+ if (languages.length <= 2 && languages[0] !== "") {
+ return getLocaleMessage(object, localeKey, key, languages[0])
} else {
- Console.debug(`translation error: ${key}: Specific country code detected but it's invalid`)
+ Console.debug(`translation error: ${key}: Specific country code detected but it's invalid`, locale, languages)
return ""
}
}
diff --git a/src/views/About/Bulletin.vue b/src/views/About/Bulletin.vue
index e042b7a8..524dc834 100644
--- a/src/views/About/Bulletin.vue
+++ b/src/views/About/Bulletin.vue
@@ -17,6 +17,12 @@
"bulletin_0": "1月16日に海外版が正式にリリースされます。データの混雑を避けるために、当サイトでは大陸版のドロップデータのみをアップロードして下さい。",
"bulletin_1": "海外版のドロップまとめサイトを開設するかは現時点では思案中のみとなっています。"
}
+ },
+ "ko": {
+ "bulletin": {
+ "bulletin_0": "명일방주 해외서버가 1/16일에 오픈하였습니다. 데이터의 혼란을 방지하기 위해, 중국 서버의 보고서만 작성하여 주십시오.",
+ "bulletin_1": "저희는 해외서버를 위한 웹 사이트 개설을 검토하고 있습니다."
+ }
}
}
diff --git a/src/views/About/Changelog.vue b/src/views/About/Changelog.vue
index ac501efb..b3297729 100644
--- a/src/views/About/Changelog.vue
+++ b/src/views/About/Changelog.vue
@@ -36,6 +36,10 @@ export default {
data() {
return {
logs: {
+ 'v1.1.2': [
+ '加入韩语翻译',
+ '增加载入界面'
+ ],
'v1.1.1': [
'“岁过华灯”等一系列开箱子的汇报支持批量上传'
],
diff --git a/src/views/About/Contact.vue b/src/views/About/Contact.vue
index 378ca6ef..acdfa2d5 100644
--- a/src/views/About/Contact.vue
+++ b/src/views/About/Contact.vue
@@ -28,6 +28,15 @@
"github": "Github Issues",
"nga": "NGAスレッド"
}
+ },
+ "ko": {
+ "contact": {
+ "contact_0": "문의",
+ "contact_2": "질문이나 건의 사안이 있다면, 언제든지 관리자에게 연락하여 주십시오.",
+ "contributor": "팀 구성원 페이지",
+ "github": "Github Issues",
+ "nga": "NGA (포럼) 스레드"
+ }
}
}
diff --git a/src/views/About/Contribute.vue b/src/views/About/Contribute.vue
index 6de9bcbc..679be0dc 100644
--- a/src/views/About/Contribute.vue
+++ b/src/views/About/Contribute.vue
@@ -23,6 +23,14 @@
"contribute_0": "以下のような開発の経験があるのであれば、Penguin Statisticsに自身の力を使ってみませんか?右記のQQグループを追加してください:747099627",
"contribute_1": "本プロジェクトは無償のオープンソースプロジェクトです"
}
+ },
+ "ko": {
+ "contribute": {
+ "frontend": "프론트엔드",
+ "backend": "백엔드",
+ "contribute_0": "다음 기술중 하나라도 경험이 있고, 펭귄 물류에 기여하고자 한다면. QQ 그룹 : 747099627로 연락하여 주십시오.",
+ "contribute_1": "펭귄 물류 데이터 분석 부서는 비영리 오픈소스 프로젝트 입니다"
+ }
}
}
diff --git a/src/views/About/Donate.vue b/src/views/About/Donate.vue
index dfce8dd3..e0614963 100644
--- a/src/views/About/Donate.vue
+++ b/src/views/About/Donate.vue
@@ -44,6 +44,21 @@
"paypal": "PayPalで寄付"
}
}
+ },
+ "ko": {
+ "donate": {
+ "donate_0": "사용자 수와 서버비가 늘어나면서 펭귄 통계는 폐쇄의 위기에 처하게 된다. 관리자는 사랑하는 펭귄 통계를 지키기 위해...",
+ "donate_1": "사용자 수가 늘어나면서 서버에 무리가 가고 있습니다. 만약 몇몇 박사들이",
+ "donate_2": "서버비",
+ "donate_3": "빼빼로나 애플파이도 환영합니다!",
+ "donate_4": "를 기부해 줄 수 있다면, 우리는 매우 감사할 것입니다!",
+ "redirectToApp": "{app}로 기부하기",
+ "methods": {
+ "alipay": "Alipay",
+ "wechatPay": "WeChat Pay",
+ "paypal": "PayPal로 기부하기"
+ }
+ }
}
}
diff --git a/src/views/About/Intro.vue b/src/views/About/Intro.vue
index b91b48d2..4f3f8b3c 100644
--- a/src/views/About/Intro.vue
+++ b/src/views/About/Intro.vue
@@ -20,6 +20,13 @@
"intro_1": "Penguin Statisticsはペンギン急便のデータ処理部門であり(嘘)、各作戦での素材ドロップの統計分析を行っています。",
"intro_2": "現在ほとんどのデータはユーザーの手によって手動でアップロードされており、誤った情報や誤った情報による影響を最小限に抑えるためにペンギンデータアナライズチームはドロップアイテムについて様々な確度から審査しています。"
}
+ },
+ "ko": {
+ "intro": {
+ "intro_0": "펭귄 물류 데이터 분석 부서에 오신것을 환영합니다!",
+ "intro_1": "저희는 펭귄 물류의 데이터 분석 부서입니다. 저희는 아이템 드랍에 대한 분석을 맡고 있습니다.",
+ "intro_2": "대부분의 드랍 데이터는 박사들에 의해 수동으로 제출되었으며, 저희 부서는 잘못된 보고서나 잘못된 결과를 방지하기 위해 전체적인 데이터 검사를 실시하고 있습니다."
+ }
}
}
diff --git a/src/views/About/Links.vue b/src/views/About/Links.vue
index e4eed8f2..32604a92 100644
--- a/src/views/About/Links.vue
+++ b/src/views/About/Links.vue
@@ -23,7 +23,7 @@
"en": {
"links": {
"tags": {
- "hr": "Public Recruiting Lookup",
+ "hr": "Recruiting Lookup",
"levelup": "Level Up Calculator",
"materials": "Material Requirement Calculator",
"planner": "Planner",
@@ -59,6 +59,26 @@
"jp_wiki": "日本版Wiki"
}
}
+ },
+ "ko": {
+ "links": {
+ "tags": {
+ "hr": "공개모집 계산기",
+ "levelup": "레벨링 계산기",
+ "materials": "육성 재료 계산기",
+ "planner": "계획기",
+ "storage": "창고 관리기",
+ "character": "오퍼레이터 목록",
+ "enemy": "적 목록",
+ "apRanking": "이성 효율 순위",
+ "dropRateRanking": "드랍률 순위",
+ "generalRanking": "종합 순위",
+ "walkthrough": "공략집",
+ "experience": "팁 모음",
+ "ja_translation": "메인스토리 일본어 번역",
+ "jp_wiki": "일본서버 위키"
+ }
+ }
}
}
@@ -69,6 +89,7 @@
v-for="(link, index) in links"
:key="index"
class="d-flex"
+ cols="12"
sm="6"
md="4"
>
diff --git a/src/views/About/Members.vue b/src/views/About/Members.vue
index dd7d712b..3ab9ab5e 100644
--- a/src/views/About/Members.vue
+++ b/src/views/About/Members.vue
@@ -4,7 +4,8 @@
"categories": {
"owner": "站长",
"developers": "开发者",
- "others": "其他"
+ "others": "其他",
+ "translators": "翻译"
},
"socials": {
"weibo": "微博",
@@ -23,14 +24,16 @@
"customersupport": "客服",
"logo": "Logo 画师",
"materials": "素材提供",
- "localization_ja": "日语化"
+ "localization_ja": "日语化",
+ "localization_ko": "韩语化"
}
},
"en": {
"categories": {
"owner": "Webmaster",
"developers": "Developers",
- "others": "Others"
+ "others": "Others",
+ "translators": "Translators"
},
"socials": {
"weibo": "Weibo",
@@ -46,17 +49,19 @@
"statistics": "Statistics and Analysis",
"arkplanner": "Author of ArkPlanner",
"bulkupload": "Bulk Upload",
- "customersupport": "Customer Support",
+ "customersupport": "User Support",
"logo": "Logo Designer",
"materials": "Materials Supplier",
- "localization_ja": "Japanese Localization Provider"
+ "localization_ja": "Japanese Localization Provider",
+ "localization_ko": "Korean Localization Provider"
}
},
"ja": {
"categories": {
"owner": "管理人",
"developers": "開発者",
- "others": "その他"
+ "others": "その他",
+ "translators": "翻訳者"
},
"socials": {
"weibo": "Weibo",
@@ -75,7 +80,36 @@
"customersupport": "顧客サービス",
"logo": "ロゴデザイナー",
"materials": "材料サプライヤー",
- "localization_ja": "日本語化"
+ "localization_ja": "日本語化",
+ "localization_ko": "韓国語化"
+ }
+ },
+ "ko": {
+ "categories": {
+ "owner": "관리자",
+ "developers": "개발자",
+ "others": "기타",
+ "translators": "번역가"
+ },
+ "socials": {
+ "weibo": "웨이보",
+ "github": "GitHub",
+ "twitter": "Twitter",
+ "qq": "QQ",
+ "email": "Email"
+ },
+ "responsibilities": {
+ "frontend": "프론트엔드",
+ "backend": "백엔드",
+ "maintenance": "데브옵스",
+ "statistics": "분석 및 통계",
+ "arkplanner": "명일방주 계획기 제작",
+ "bulkupload": "대량의 보고서 제출",
+ "customersupport": "고객 지원",
+ "logo": "로고 디자이너",
+ "materials": "재료 이미지 제공",
+ "localization_ja": "일본 현지화 제공",
+ "localization_ko": "한국 현지화 제공"
}
}
}
@@ -166,7 +200,8 @@
logo: "logo",
materials: "materials",
localization: {
- ja: "localization_ja"
+ ja: "localization_ja",
+ ko: "localization_ko",
}
}
export default {
@@ -312,6 +347,18 @@
socials: {
github: "https://github.com/Evealicemier"
}
+ }
+ ],
+ translators: [
+ {
+ name: "シャロ",
+ responsibility: [
+ r.localization.ko
+ ],
+ avatar: "syaro.jpeg",
+ socials: {
+ twitter: "https://twitter.com/StarNight_ko"
+ }
},
{
name: "方舟航海図",
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 5585ff36..c5794fd0 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -1,6 +1,8 @@
-
+
diff --git a/src/views/Report.vue b/src/views/Report.vue
index 7cae9e80..ec4744c4 100644
--- a/src/views/Report.vue
+++ b/src/views/Report.vue
@@ -83,6 +83,34 @@
},
"usage": "左クリックで個数増加、右クリックで個数減少",
"gacha": "Multiple results are allowed in the reporting of this stage."
+ },
+ "ko": {
+ "stage": {
+ "loots": {
+ "normal": "일반 드랍",
+ "extra": "추가 드랍",
+ "special": "특수 드랍"
+ }
+ },
+ "report": {
+ "name": "보고",
+ "furniture": "럭키 드랍: {state}",
+ "submit": "제출",
+ "success": "성공적으로 제출되었습니다",
+ "undo": "제출 취소",
+ "undoSuccess": "성공적으로 취소되었습니다",
+ "clear": "초기화",
+ "unable": "제출 실패: "
+ },
+ "rules": {
+ "rule_1": "한 번에 하나의 전투에 대한 보고서를 작성하여야 합니다, 제출 전에 입력 내용을 한번 더 확인해 주십시오.",
+ "rule_2": "드랍이 없는 경우에는, 아무것도 누르지 않은 채로 제출을 눌러 주십시오.",
+ "rule_3": "처음으로 클리어한 작전은 보고하지 마시고, 운이 좋았던 작전만 보고하지 마십시오 - 모든 드랍을 보고해 주십시오.",
+ "rule_4": "3성으로 클리어하여 주십시오.",
+ "rule_5": "중국 서버에서의 드랍만 보고하여 주십시오, 감사합니다."
+ },
+ "usage": "왼쪽 클릭시 증가하며, 오른쪽 클릭시 감소합니다",
+ "gacha": "이 작전지역은 여러 개의 결과를 한 보고서에 제출할 수 있습니다."
}
}
@@ -669,7 +697,7 @@ export default {
});
let reportedUserId = Cookies.get('userID');
if (userId !== reportedUserId) {
- this.$store.commit("authLogin", reportedUserId);
+ this.$store.commit("auth/login", reportedUserId);
}
this.lastSubmissionId = data;
this.submitting = false;
diff --git a/src/views/Stats/Item.vue b/src/views/Stats/Item.vue
index 4efcc621..4be812fb 100644
--- a/src/views/Stats/Item.vue
+++ b/src/views/Stats/Item.vue
@@ -44,6 +44,21 @@
"FURN": "家具",
"ACTIVITY_ITEM": "イベントアイテム"
}
+ },
+ "ko": {
+ "choose": {
+ "name": "아이템 선택"
+ },
+ "result": {
+ "name": "통계 결과",
+ "title": "{item}의 통계 결과"
+ },
+ "categories": {
+ "CARD_EXP": "작전 기록",
+ "MATERIAL": "재료",
+ "FURN": "가구",
+ "ACTIVITY_ITEM": "이벤트 아이템"
+ }
}
}
@@ -52,7 +67,7 @@
diff --git a/yarn.lock b/yarn.lock
index e20a12a6..aaa184ca 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6180,11 +6180,6 @@ icss-utils@^2.1.0:
dependencies:
postcss "^6.0.1"
-idb@^4.0.4:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/idb/-/idb-4.0.5.tgz#23b930fbb0abce391e939c35b7b31a669e74041f"
- integrity sha512-P+Fk9HT2h1DhXoE1YNK183SY+CRh2GHNh28de94sGwhe0bUA75JJeVJWt3SenE5p0BXK7maflIq29dl6UZHrFw==
-
ieee754@^1.1.12, ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"