Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
730fa15
fix: 결과 카드 버튼 UI 정합성 맞추기
maehwasoo Feb 6, 2026
60f16d5
fix: 큐레이션 상품 필드 기본값 보강
maehwasoo Feb 6, 2026
a8b0a73
fix: 결과 페이지 큐레이션 레이아웃 정리
maehwasoo Feb 6, 2026
cd360a9
fix: 결과 슬라이더 화살표 UI 정합성 맞추기
maehwasoo Feb 6, 2026
9efb342
fix: 결과 카드 사이트 버튼 UI 정합성 맞추기
maehwasoo Feb 6, 2026
8d756fc
fix: 결과 카드 하단 여백 간격 보정
maehwasoo Feb 6, 2026
380d37f
fix: 큐레이션 카드 그리드 반응형 폭 적용
maehwasoo Feb 6, 2026
ab56da7
fix: 결과 화살표 아이콘 크기 스펙 통일
maehwasoo Feb 6, 2026
da91cd7
fix: 결과 카드 사이트 버튼 배경색 고정
maehwasoo Feb 6, 2026
915b99f
fix: 결과 버튼 및 컬러칩 스펙 보정
maehwasoo Feb 6, 2026
2f52a00
fix: 버튼 스타일 빌드 오류 및 스펙 보정
maehwasoo Feb 6, 2026
14d6d0e
fix: 결과 화살표 버튼 36-24 구조 정렬
maehwasoo Feb 6, 2026
bc1c417
fix: 카드 사이트 버튼 배경·개행 동작 정렬
maehwasoo Feb 6, 2026
4e33dc2
fix: 컬러칩 레이어와 +N 간격 정렬
maehwasoo Feb 6, 2026
00be40d
fix: 비활성 하트 아이콘 내부 아웃라인 반영
maehwasoo Feb 6, 2026
9e054d9
fix: 하트 아웃라인 불투명도 스펙 반영
maehwasoo Feb 6, 2026
f45f323
fix: 하트 내부 30%·외곽선 흰색 반영
maehwasoo Feb 6, 2026
2a602ba
fix: 하트 내부 불투명도 20%로 조정
maehwasoo Feb 6, 2026
e619efa
refactor: 하트 색상 제어를 코드로 전환
maehwasoo Feb 6, 2026
7a31daf
refactor: 카드 스켈레톤 색상 토큰으로 치환
maehwasoo Feb 6, 2026
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
6 changes: 1 addition & 5 deletions src/pages/generate/pages/result/ResultPage.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ export const wrapper = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
height: `calc(100dvh - ${layoutVars.titleNavBarHeight})`, // TitleNavBar height
overflow: 'hidden',
minHeight: `calc(100dvh - ${layoutVars.titleNavBarHeight})`, // TitleNavBar height
});

export const resultSection = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
minHeight: 0,
overflow: 'hidden',
});

export const imgArea = recipe({
Expand Down
96 changes: 76 additions & 20 deletions src/pages/generate/pages/result/components/GeneratedImg.css.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { style } from '@vanilla-extract/css';
import { globalStyle, style } from '@vanilla-extract/css';
import { recipe } from '@vanilla-extract/recipes';

// import { zIndex } from '@/shared/styles/tokens/zIndex';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

주석 처리된 import를 제거해 주세요.

// import { zIndex } from '@/shared/styles/tokens/zIndex'; — 사용하지 않는 주석 코드는 정리하는 게 좋습니다.

🧹 제거 제안
-// import { zIndex } from '@/shared/styles/tokens/zIndex';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// import { zIndex } from '@/shared/styles/tokens/zIndex';
🤖 Prompt for AI Agents
In `@src/pages/generate/pages/result/components/GeneratedImg.css.ts` at line 4,
Remove the unused commented import line in GeneratedImg.css.ts: delete the
commented statement "// import { zIndex } from '@/shared/styles/tokens/zIndex';"
so the file contains no dead/commented import; ensure no other unused/commented
imports remain in the same file (look for imports at the top of
GeneratedImg.css.ts and clean them) and run linting to confirm.

Expand Down Expand Up @@ -75,25 +75,55 @@ export const slideNumSkeleton = style({
animation: `${animationTokens.skeletonWave} 1.6s ease-in-out infinite`,
});

export const slideBtnCircle = style({
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '2.4rem',
height: '2.4rem',
borderRadius: '1.2rem',
backgroundColor: colorVars.color.gray999,
pointerEvents: 'none',
transition: 'opacity 0.2s ease-in-out',
});

export const slideBtnIcon = style({
position: 'relative',
zIndex: 1,
width: '1.2rem',
height: '1.2rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});

globalStyle(`${slideBtnIcon} > svg`, {
width: '1.2rem',
height: '1.2rem',
});

export const slidePrevBtn = style({
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
left: '1.2rem',
bottom: '50%',
left: '0.6rem',
top: 'calc(50% + 0.8rem)',
transform: 'translateY(-50%)',
width: '3.6rem',
height: '3.6rem',
backgroundColor: colorVars.color.gray999_30,
borderRadius: '99.9rem',
padding: 0,
border: 'none',
background: 'transparent',
appearance: 'none',
WebkitTapHighlightColor: 'transparent',
zIndex: 1,

':active': {
backgroundColor: colorVars.color.gray999_50,
},

':disabled': {
backgroundColor: colorVars.color.gray999_04,
selectors: {
'&:disabled': {
cursor: 'default',
},
},
});

Expand All @@ -102,21 +132,47 @@ export const slideNextBtn = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
right: '1.2rem',
bottom: '50%',
right: '0.6rem',
top: 'calc(50% + 0.8rem)',
transform: 'translateY(-50%)',
width: '3.6rem',
height: '3.6rem',
backgroundColor: colorVars.color.gray999_30,
borderRadius: '99.9rem',
padding: 0,
border: 'none',
background: 'transparent',
appearance: 'none',
WebkitTapHighlightColor: 'transparent',
zIndex: 1,

':active': {
backgroundColor: colorVars.color.gray999_50,
selectors: {
'&:disabled': {
cursor: 'default',
},
},
});

':disabled': {
backgroundColor: colorVars.color.gray999_04,
},
globalStyle(`${slidePrevBtn} ${slideBtnCircle}`, {
opacity: 0.3,
});

globalStyle(`${slidePrevBtn}:active:not(:disabled) ${slideBtnCircle}`, {
opacity: 0.5,
});

globalStyle(`${slidePrevBtn}:disabled ${slideBtnCircle}`, {
opacity: 0.04,
});

globalStyle(`${slideNextBtn} ${slideBtnCircle}`, {
opacity: 0.3,
});

globalStyle(`${slideNextBtn}:active:not(:disabled) ${slideBtnCircle}`, {
opacity: 0.5,
});

globalStyle(`${slideNextBtn}:disabled ${slideBtnCircle}`, {
opacity: 0.04,
});
Comment on lines +154 to 176
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Prev/Next 버튼의 globalStyle opacity 규칙이 완전히 중복돼요.

slidePrevBtnslideNextBtn의 세 가지 상태(default 0.3, active 0.5, disabled 0.04)가 동일합니다. 공통 셀렉터로 통합하면 유지보수가 편해져요.

♻️ 통합 제안
-globalStyle(`${slidePrevBtn} ${slideBtnCircle}`, {
-  opacity: 0.3,
-});
-
-globalStyle(`${slidePrevBtn}:active:not(:disabled) ${slideBtnCircle}`, {
-  opacity: 0.5,
-});
-
-globalStyle(`${slidePrevBtn}:disabled ${slideBtnCircle}`, {
-  opacity: 0.04,
-});
-
-globalStyle(`${slideNextBtn} ${slideBtnCircle}`, {
-  opacity: 0.3,
-});
-
-globalStyle(`${slideNextBtn}:active:not(:disabled) ${slideBtnCircle}`, {
-  opacity: 0.5,
-});
-
-globalStyle(`${slideNextBtn}:disabled ${slideBtnCircle}`, {
-  opacity: 0.04,
-});
+globalStyle(
+  `${slidePrevBtn} ${slideBtnCircle}, ${slideNextBtn} ${slideBtnCircle}`,
+  { opacity: 0.3 },
+);
+
+globalStyle(
+  `${slidePrevBtn}:active:not(:disabled) ${slideBtnCircle}, ${slideNextBtn}:active:not(:disabled) ${slideBtnCircle}`,
+  { opacity: 0.5 },
+);
+
+globalStyle(
+  `${slidePrevBtn}:disabled ${slideBtnCircle}, ${slideNextBtn}:disabled ${slideBtnCircle}`,
+  { opacity: 0.04 },
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
globalStyle(`${slidePrevBtn} ${slideBtnCircle}`, {
opacity: 0.3,
});
globalStyle(`${slidePrevBtn}:active:not(:disabled) ${slideBtnCircle}`, {
opacity: 0.5,
});
globalStyle(`${slidePrevBtn}:disabled ${slideBtnCircle}`, {
opacity: 0.04,
});
globalStyle(`${slideNextBtn} ${slideBtnCircle}`, {
opacity: 0.3,
});
globalStyle(`${slideNextBtn}:active:not(:disabled) ${slideBtnCircle}`, {
opacity: 0.5,
});
globalStyle(`${slideNextBtn}:disabled ${slideBtnCircle}`, {
opacity: 0.04,
});
globalStyle(
`${slidePrevBtn} ${slideBtnCircle}, ${slideNextBtn} ${slideBtnCircle}`,
{ opacity: 0.3 },
);
globalStyle(
`${slidePrevBtn}:active:not(:disabled) ${slideBtnCircle}, ${slideNextBtn}:active:not(:disabled) ${slideBtnCircle}`,
{ opacity: 0.5 },
);
globalStyle(
`${slidePrevBtn}:disabled ${slideBtnCircle}, ${slideNextBtn}:disabled ${slideBtnCircle}`,
{ opacity: 0.04 },
);
🤖 Prompt for AI Agents
In `@src/pages/generate/pages/result/components/GeneratedImg.css.ts` around lines
154 - 176, The opacity rules for slidePrevBtn and slideNextBtn are duplicated;
consolidate by creating a combined selector referencing slidePrevBtn and
slideNextBtn together with slideBtnCircle (e.g., `${slidePrevBtn},
${slideNextBtn}` then append ` ${slideBtnCircle}` and the state variants) and
replace the six globalStyle calls with three: default, :active:not(:disabled),
and :disabled, each using the combined selector so the default (0.3), active
(0.5), and disabled (0.04) opacities are applied to both buttons via a single
set of globalStyle calls.


export const imgAreaBlurred = recipe({
Expand Down
18 changes: 12 additions & 6 deletions src/pages/generate/pages/result/components/GeneratedImgA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ const GeneratedImgA = ({
className={styles.slidePrevBtn}
disabled={!swiper || currentSlideIndex === 0}
>
{currentSlideIndex === 0 ? <SlidePrevDisabled /> : <SlidePrev />}
<span className={styles.slideBtnCircle} aria-hidden />
<span className={styles.slideBtnIcon} aria-hidden>
{currentSlideIndex === 0 ? <SlidePrevDisabled /> : <SlidePrev />}
</span>
Comment on lines +204 to +207
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

aria-hidden에 명시적 값을 권장해요.

aria-hidden만 쓰면 JSX에서 true로 처리되지만, 접근성 속성은 aria-hidden="true"로 명시하는 게 가독성과 의도 전달에 좋습니다.

Also applies to: 258-265

🤖 Prompt for AI Agents
In `@src/pages/generate/pages/result/components/GeneratedImgA.tsx` around lines
204 - 207, Update the JSX in GeneratedImgA.tsx to use explicit aria-hidden
string values instead of bare attributes: replace occurrences on elements with
class names slideBtnCircle and slideBtnIcon (and the similar block at the other
occurrence) to use aria-hidden="true" or aria-hidden="false" as appropriate;
locate where currentSlideIndex is used to render SlidePrevDisabled/SlidePrev
(and the other slide-next block) and ensure the aria-hidden values reflect
visibility intent for screen readers.

</button>
{images.map((image, index) => {
const cachedDetection =
Expand Down Expand Up @@ -252,11 +255,14 @@ const GeneratedImgA = ({
className={styles.slideNextBtn}
disabled={!swiper || currentSlideIndex === totalSlideCount - 1}
>
{currentSlideIndex === totalSlideCount - 1 ? (
<SlideNextDisabled />
) : (
<SlideNext />
)}
<span className={styles.slideBtnCircle} aria-hidden />
<span className={styles.slideBtnIcon} aria-hidden>
{currentSlideIndex === totalSlideCount - 1 ? (
<SlideNextDisabled />
) : (
<SlideNext />
)}
</span>
</button>
</Swiper>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import { colorVars } from '@styles/tokens/color.css';

export const container = style({
width: '100%',
flex: '1 1 auto',
minHeight: 0,
display: 'flex',
flexDirection: 'column',
padding: '2rem 2rem 0',
backgroundColor: colorVars.color.gray000,
overflow: 'hidden',
});

export const title = style({
Expand Down Expand Up @@ -63,13 +60,11 @@ export const filterSkeletonChipWidth = styleVariants({
});

export const content = style({
flex: 1,
minHeight: 0,
width: '100%',
display: 'flex',
flexDirection: 'column',
overflowY: 'auto',
marginTop: '0.8rem',
overscrollBehavior: 'contain',
paddingBottom: '2.4rem',

selectors: {
'&::-webkit-scrollbar': {
Expand All @@ -84,16 +79,14 @@ export const gridbox = style({
width: '100%',
height: 'fit-content',
display: 'grid',
gridTemplateColumns: 'repeat(2, 16.4rem)',
gridTemplateColumns: 'repeat(2, minmax(16.4rem, 1fr))',
columnGap: '0.7rem',
rowGap: 0,
justifyContent: 'space-between',
justifyItems: 'start',
});

export const statusContainer = style({
width: '100%',
height: '100%',
minHeight: '22rem',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
Expand Down
78 changes: 56 additions & 22 deletions src/pages/generate/pages/result/curationSheet/CurationSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,40 @@ type ProductPrefetchQueryKey = [
},
];

const RESULT_CARD_UI_FALLBACK = {
productName: '상품명 준비중',
mallName: '브랜드 준비중',
originalPrice: 0,
discountPrice: 0,
discountRate: 0,
colorHexes: ['#E7EBF0', '#D7DFE8', '#C3CFDD', '#AEBED0'],
saveCount: 0,
} as const;

const normalizeText = (value: unknown, fallback: string) => {
if (typeof value !== 'string') return fallback;
const normalized = value.trim();
return normalized.length > 0 ? normalized : fallback;
};

const toFiniteNumber = (value: unknown) => {
const numeric = Number(value);
return Number.isFinite(numeric) ? numeric : null;
};
Comment on lines +57 to +60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

toFiniteNumber(null)0을 반환해요 — 의도된 동작인지 확인 필요.

Number(null) === 0이므로 toFiniteNumber(null)0(유효한 값)을 반환하고, Number(undefined)NaN이므로 null을 반환해요. API에서 null이 "값 없음"을 의미한다면, 0과 구별 없이 처리되는 셈이에요.

현재 fallback 값이 모두 0이라 실질적 영향은 없지만, 향후 fallback이 바뀌면 버그로 이어질 수 있어요.

🛡️ null/undefined 모두 fallback으로 빠지도록 수정 제안
 const toFiniteNumber = (value: unknown) => {
+  if (value === null || value === undefined) return null;
   const numeric = Number(value);
   return Number.isFinite(numeric) ? numeric : null;
 };
🤖 Prompt for AI Agents
In `@src/pages/generate/pages/result/curationSheet/CurationSheet.tsx` around lines
57 - 60, The helper toFiniteNumber currently treats null as 0 because
Number(null) === 0; update to treat null and undefined as "no value" by checking
for nullish first (e.g., value == null or value === null || value === undefined)
and returning null in that case, otherwise coerce to Number and return it only
if Number.isFinite; modify the toFiniteNumber function accordingly so
null/undefined both fall through to the fallback path instead of being converted
to 0.


const normalizeColorHexes = (value: unknown) => {
if (!Array.isArray(value)) return [...RESULT_CARD_UI_FALLBACK.colorHexes];

const normalized = value
.filter((hex): hex is string => typeof hex === 'string')
.map((hex) => hex.trim())
.filter((hex) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex));

return normalized.length > 0
? normalized
: [...RESULT_CARD_UI_FALLBACK.colorHexes];
};

interface CurationSheetProps {
groupId?: number | null;
}
Expand Down Expand Up @@ -96,37 +130,37 @@ export const CurationSheet = ({ groupId = null }: CurationSheetProps) => {
? byProductId
: index + 1;

const originalPrice = Number(product.furnitureProductOriginalPrice);
const discountPrice = Number(product.furnitureProductDiscountPrice);
const discountRate = Number(product.furnitureProductDiscountRate);
const saveCount = Number(product.furnitureProductSaveCount);
const originalPrice = toFiniteNumber(product.furnitureProductOriginalPrice);
const discountPrice = toFiniteNumber(product.furnitureProductDiscountPrice);
const discountRate = toFiniteNumber(product.furnitureProductDiscountRate);
const saveCount = toFiniteNumber(product.furnitureProductSaveCount);
Comment on lines +133 to +136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prettier 포맷팅 위반 — 80자 초과.

정적 분석에서 133~134번 줄이 80자를 넘는다고 보고했어요. Prettier 규칙에 맞게 줄바꿈을 적용해 주세요.

🔧 포맷 수정 제안
-      const originalPrice = toFiniteNumber(product.furnitureProductOriginalPrice);
-      const discountPrice = toFiniteNumber(product.furnitureProductDiscountPrice);
+      const originalPrice = toFiniteNumber(
+        product.furnitureProductOriginalPrice
+      );
+      const discountPrice = toFiniteNumber(
+        product.furnitureProductDiscountPrice
+      );

As per coding guidelines, "Maintain a maximum line width of 80 characters".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const originalPrice = toFiniteNumber(product.furnitureProductOriginalPrice);
const discountPrice = toFiniteNumber(product.furnitureProductDiscountPrice);
const discountRate = toFiniteNumber(product.furnitureProductDiscountRate);
const saveCount = toFiniteNumber(product.furnitureProductSaveCount);
const originalPrice = toFiniteNumber(
product.furnitureProductOriginalPrice
);
const discountPrice = toFiniteNumber(
product.furnitureProductDiscountPrice
);
const discountRate = toFiniteNumber(product.furnitureProductDiscountRate);
const saveCount = toFiniteNumber(product.furnitureProductSaveCount);
🧰 Tools
🪛 ESLint

[error] 133-133: Replace product.furnitureProductOriginalPrice with ⏎········product.furnitureProductOriginalPrice⏎······

(prettier/prettier)


[error] 134-134: Replace product.furnitureProductDiscountPrice with ⏎········product.furnitureProductDiscountPrice⏎······

(prettier/prettier)

🤖 Prompt for AI Agents
In `@src/pages/generate/pages/result/curationSheet/CurationSheet.tsx` around lines
133 - 136, The lines calling toFiniteNumber exceed 80 chars; destructure the
long property names from product first and then call toFiniteNumber on each
short variable to keep lines under 80. For example, in CurationSheet.tsx extract
const { furnitureProductOriginalPrice, furnitureProductDiscountPrice,
furnitureProductDiscountRate, furnitureProductSaveCount } = product and then
assign originalPrice, discountPrice, discountRate, saveCount by calling
toFiniteNumber on those shorter identifiers (refer to symbols originalPrice,
discountPrice, discountRate, saveCount and function toFiniteNumber).


return {
id: recommendId,
isRecommendId: Boolean(recommendId),
furnitureProductId: safeProductId,
furnitureProductName: product.furnitureProductName,
furnitureProductMallName: product.furnitureProductMallName,
furnitureProductName: normalizeText(
product.furnitureProductName,
RESULT_CARD_UI_FALLBACK.productName
),
furnitureProductMallName: normalizeText(
product.furnitureProductMallName,
RESULT_CARD_UI_FALLBACK.mallName
),
furnitureProductImageUrl:
product.furnitureProductImageUrl || product.baseFurnitureImageUrl,
furnitureProductSiteUrl: product.furnitureProductSiteUrl,
furnitureProductOriginalPrice: Number.isFinite(originalPrice)
? originalPrice
: undefined,
furnitureProductDiscountPrice: Number.isFinite(discountPrice)
? discountPrice
: undefined,
furnitureProductDiscountRate: Number.isFinite(discountRate)
? discountRate
: undefined,
furnitureProductColorHexes: Array.isArray(
furnitureProductOriginalPrice:
originalPrice ?? RESULT_CARD_UI_FALLBACK.originalPrice,
furnitureProductDiscountPrice:
discountPrice ?? RESULT_CARD_UI_FALLBACK.discountPrice,
furnitureProductDiscountRate:
discountRate ?? RESULT_CARD_UI_FALLBACK.discountRate,
furnitureProductColorHexes: normalizeColorHexes(
product.furnitureProductColorHexes
)
? product.furnitureProductColorHexes
: undefined,
furnitureProductSaveCount: Number.isFinite(saveCount)
? saveCount
: undefined,
),
furnitureProductSaveCount:
saveCount ?? RESULT_CARD_UI_FALLBACK.saveCount,
Comment on lines +153 to +163

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid showing 0 price/save count for missing data

When the API omits price/discount/saveCount fields, this mapping now replaces them with 0. In CardProduct the price block renders whenever a numeric value exists and the save-count row renders for any finite number, so missing data will now appear as “0원/0%/0” instead of being hidden. This is misleading for products with incomplete data and is a behavioral regression compared to the previous undefined handling. Consider preserving undefined (or a distinct sentinel) for missing values so the UI stays blank rather than implying a real zero value.

Useful? React with 👍 / 👎.

};
});
}, [productsData]);
Expand Down
4 changes: 2 additions & 2 deletions src/shared/assets/icons/nextAbled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading