전체적인 시스템 추가.

This commit is contained in:
girinb
2025-07-15 21:18:57 +09:00
parent a75ba845e4
commit d42fcb7102
45 changed files with 507 additions and 217 deletions

View File

@@ -0,0 +1,23 @@
class CaseStudyPlan {
final String planId;
final String planTitle;
final String planTeacher;
final String thumbnail;
CaseStudyPlan({
required this.planId,
required this.planTitle,
required this.planTeacher,
required this.thumbnail,
});
factory CaseStudyPlan.fromJson(Map<String, dynamic> json) {
return CaseStudyPlan(
planId: json['casestudy lesson id'] ?? '아이디 없음',
planTitle: json['course_name'] ?? '제목 없음',
planTeacher: json['planTeacher'] ?? '',
thumbnail: json['course_thumbnail'] ?? '',
);
}
}

View File

@@ -0,0 +1,32 @@
class Course {
final String affiliateNumber;
final String affiliateName;
final String affiliateType;
final String affiliateResult;
final String affiliateDescription;
final String affiliateOrg;
final String affiliateOrgIcon;
Course({
required this.affiliateNumber,
required this.affiliateName,
required this.affiliateType,
required this.affiliateResult,
required this.affiliateDescription,
required this.affiliateOrg,
required this.affiliateOrgIcon,
});
factory Course.fromJson(Map<String, dynamic> json) {
return Course(
affiliateNumber: json['affliate_number'] as String,
affiliateName: json['affliate_name'] as String,
affiliateType: json['affliate_type'] as String,
affiliateResult: json['affliate_result'] as String,
affiliateDescription: json['affliate_discription'] as String,
affiliateOrg: json['affliate_org'] as String,
affiliateOrgIcon: json['affliate_org_icon'] as String,
);
}
}

41
lib/common/data/job.dart Normal file
View File

@@ -0,0 +1,41 @@
class Job {
final String jobName;
final String jobJobtag;
final String jobDescription;
final double jobIncome;
final String jobIncomeType;
final String jobLocationCountry;
final String jobLocationCity;
final String jobRequirement;
final String jobTag;
final String jobEndpoint;
Job({
required this.jobName,
required this.jobJobtag,
required this.jobDescription,
required this.jobIncome,
required this.jobIncomeType,
required this.jobLocationCountry,
required this.jobLocationCity,
required this.jobRequirement,
required this.jobTag,
required this.jobEndpoint,
});
factory Job.fromJson(Map<String, dynamic> json) {
return Job(
jobName: json['Job_name'] as String,
jobJobtag: json['job_jobtag'] as String,
jobDescription: json['job_decriptopn'] as String,
jobIncome: double.tryParse(json['job_Income'].toString()) ?? 0.0,
jobIncomeType: json['job_income_type'] as String,
jobLocationCountry: json['job_location_country'] as String,
jobLocationCity: json['job_location_city'] as String,
jobRequirement: json['job_requirment'] as String,
jobTag: json['job_tag'] as String,
jobEndpoint: json['job_endpoint'] as String,
);
}
}

View File

@@ -0,0 +1,22 @@
class NewStudy {
final String planId;
final String planTitle;
final String planTeacher;
final String thumbnail;
NewStudy({
required this.planId,
required this.planTitle,
required this.planTeacher,
required this.thumbnail,
});
factory NewStudy.fromJson(Map<String, dynamic> json) {
return NewStudy(
planId: json['casestudy lesson id'] ?? '아이디 없음',
planTitle: json['course_name'] ?? '제목 없음',
planTeacher: json['course_description'] ?? '선생님',
thumbnail: json['course_thumbnail'] ?? '',
);
}
}

View File

@@ -0,0 +1,29 @@
class PlanDetailItem {
final String lessonId;
final String lessonTag;
final String lessonUrl;
final String thumbnail;
final String lessonName;
final String lessonDescription;
PlanDetailItem({
required this.lessonId,
required this.lessonTag,
required this.lessonUrl,
required this.thumbnail,
required this.lessonName,
required this.lessonDescription,
});
factory PlanDetailItem.fromJson(Map<String, dynamic> json) {
return PlanDetailItem(
lessonId: json['casestudy lesson id'] ?? 'ID 없음',
lessonTag: json['lesson tag'] ?? '태그 없음',
lessonUrl: json['lesson url'] ?? 'URL 없음',
thumbnail: json['thumbnail'] ?? '',
lessonName: json['lesson_name'] ?? '이름 없음',
lessonDescription: json['lesson_description'] ?? '설명 없음',
);
}
}

View File

@@ -0,0 +1,23 @@
class UpcomingStudy {
final String planId;
final String planTitle;
final String planTeacher;
final String thumbnail;
UpcomingStudy({
required this.planId,
required this.planTitle,
required this.planTeacher,
required this.thumbnail,
});
factory UpcomingStudy.fromJson(Map<String, dynamic> json) {
return UpcomingStudy(
planId: json['casestudy lesson id'] ?? '아이디 없음',
planTitle: json['course_name'] ?? '제목 없음',
planTeacher: json['course_description'] ?? '선생님',
thumbnail: json['course_thumbnail'] ?? '',
);
}
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:csp2/course.dart';
import 'package:csp2/common/data/course.dart';
class CourseCard extends StatelessWidget {
final Course course;

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:csp2/job.dart';
import 'package:csp2/common/data/job.dart';
class JobCard extends StatelessWidget {
final Job job;

View File

@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import '../data/new_study.dart'; // NewStudy 모델을 import 합니다.
class StudyClassCard extends StatelessWidget {
final NewStudy plan;
final VoidCallback onTap;
const StudyClassCard({
super.key,
required this.plan,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2, // 화면 너비의 절반으로 설정
child: InkWell(
onTap: onTap,
child: Card(
margin: const EdgeInsets.only(right: 12.0), // 항목 간의 간격 조정
elevation: 2.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (plan.thumbnail.isNotEmpty)
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.network(
plan.thumbnail,
width: double.infinity,
fit: BoxFit.contain,
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center(child: Icon(Icons.broken_image, color: Colors.grey, size: 50));
},
),
),
)
else
Expanded(
child: Container(
color: Colors.grey[200],
child: const Center(child: Text('No Image', style: TextStyle(color: Colors.grey)))
),
),
// const SizedBox(height: 5.0),
SizedBox(
height: 70, // 텍스트 영역의 고정 높이 설정 (두 줄 텍스트를 위한 충분한 공간)
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 48.0, // plan.planTitle이 항상 두 줄 공간을 차지하도록 고정
child: Text(
plan.planTitle,
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
const SizedBox(height: 4.0),
Text(
plan.planTeacher,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey[600]),
maxLines: 1,
),
],
),
),
],
),
),
),
),
);
}
}

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import '../../home_page.dart'; // CaseStudyPlan 모델을 import 합니다.
import '../data/upcoming_study.dart'; // UpcomingStudy 모델을 import 합니다.
class UpcomingClassCard extends StatelessWidget {
final CaseStudyPlan plan;
final UpcomingStudy plan;
final VoidCallback onTap;
const UpcomingClassCard({